Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
dd07a8db
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
dd07a8db
编写于
2月 07, 2008
作者:
L
Len Brown
浏览文件
操作
浏览文件
下载
差异文件
Merge branches 'release', 'asus', 'sony-laptop' and 'thinkpad' into release
上级
877c357e
31e0729a
fccd5d00
547266e4
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
2901 addition
and
2144 deletion
+2901
-2144
Documentation/thinkpad-acpi.txt
Documentation/thinkpad-acpi.txt
+108
-8
drivers/acpi/asus_acpi.c
drivers/acpi/asus_acpi.c
+45
-10
drivers/misc/Kconfig
drivers/misc/Kconfig
+19
-0
drivers/misc/asus-laptop.c
drivers/misc/asus-laptop.c
+16
-10
drivers/misc/sony-laptop.c
drivers/misc/sony-laptop.c
+275
-170
drivers/misc/thinkpad_acpi.c
drivers/misc/thinkpad_acpi.c
+2436
-1340
drivers/misc/thinkpad_acpi.h
drivers/misc/thinkpad_acpi.h
+0
-606
include/linux/sonypi.h
include/linux/sonypi.h
+2
-0
未找到文件。
Documentation/thinkpad-acpi.txt
浏览文件 @
dd07a8db
ThinkPad ACPI Extras Driver
Version 0.1
7
October 04th, 2007
Version 0.1
9
January 06th, 2008
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
...
...
@@ -215,6 +215,11 @@ The following commands can be written to the /proc/acpi/ibm/hotkey file:
... any other 8-hex-digit mask ...
echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
The procfs interface does not support NVRAM polling control. So as to
maintain maximum bug-to-bug compatibility, it does not report any masks,
nor does it allow one to manipulate the hot key mask when the firmware
does not support masks at all, even if NVRAM polling is in use.
sysfs notes:
hotkey_bios_enabled:
...
...
@@ -231,17 +236,26 @@ sysfs notes:
to this value.
hotkey_enable:
Enables/disables the hot keys feature, and reports
current status of the hot keys feature.
Enables/disables the hot keys feature in the ACPI
firmware, and reports current status of the hot keys
feature. Has no effect on the NVRAM hot key polling
functionality.
0: disables the hot keys feature / feature disabled
1: enables the hot keys feature / feature enabled
hotkey_mask:
bit mask to enable driver-handling and ACPI event
generation for each hot key (see above). Returns the
current status of the hot keys mask, and allows one to
modify it.
bit mask to enable driver-handling (and depending on
the firmware, ACPI event generation) for each hot key
(see above). Returns the current status of the hot keys
mask, and allows one to modify it.
Note: when NVRAM polling is active, the firmware mask
will be different from the value returned by
hotkey_mask. The driver will retain enabled bits for
hotkeys that are under NVRAM polling even if the
firmware refuses them, and will not set these bits on
the firmware hot key mask.
hotkey_all_mask:
bit mask that should enable event reporting for all
...
...
@@ -257,12 +271,48 @@ sysfs notes:
handled by the firmware anyway. Echo it to
hotkey_mask above, to use.
hotkey_source_mask:
bit mask that selects which hot keys will the driver
poll the NVRAM for. This is auto-detected by the driver
based on the capabilities reported by the ACPI firmware,
but it can be overridden at runtime.
Hot keys whose bits are set in both hotkey_source_mask
and also on hotkey_mask are polled for in NVRAM. Only a
few hot keys are available through CMOS NVRAM polling.
Warning: when in NVRAM mode, the volume up/down/mute
keys are synthesized according to changes in the mixer,
so you have to use volume up or volume down to unmute,
as per the ThinkPad volume mixer user interface. When
in ACPI event mode, volume up/down/mute are reported as
separate events, but this behaviour may be corrected in
future releases of this driver, in which case the
ThinkPad volume mixer user interface semanthics will be
enforced.
hotkey_poll_freq:
frequency in Hz for hot key polling. It must be between
0 and 25 Hz. Polling is only carried out when strictly
needed.
Setting hotkey_poll_freq to zero disables polling, and
will cause hot key presses that require NVRAM polling
to never be reported.
Setting hotkey_poll_freq too low will cause repeated
pressings of the same hot key to be misreported as a
single key press, or to not even be detected at all.
The recommended polling frequency is 10Hz.
hotkey_radio_sw:
if the ThinkPad has a hardware radio switch, this
attribute will read 0 if the switch is in the "radios
disabled" postition, and 1 if the switch is in the
"radios enabled" position.
This attribute has poll()/select() support.
hotkey_report_mode:
Returns the state of the procfs ACPI event report mode
filter for hot keys. If it is set to 1 (the default),
...
...
@@ -277,6 +327,25 @@ sysfs notes:
May return -EPERM (write access locked out by module
parameter) or -EACCES (read-only).
wakeup_reason:
Set to 1 if the system is waking up because the user
requested a bay ejection. Set to 2 if the system is
waking up because the user requested the system to
undock. Set to zero for normal wake-ups or wake-ups
due to unknown reasons.
This attribute has poll()/select() support.
wakeup_hotunplug_complete:
Set to 1 if the system was waken up because of an
undock or bay ejection request, and that request
was sucessfully completed. At this point, it might
be useful to send the system back to sleep, at the
user's choice. Refer to HKEY events 0x4003 and
0x3003, below.
This attribute has poll()/select() support.
input layer notes:
A Hot key is mapped to a single input layer EV_KEY event, possibly
...
...
@@ -427,6 +496,23 @@ Non hot-key ACPI HKEY event map:
The above events are not propagated by the driver, except for legacy
compatibility purposes when hotkey_report_mode is set to 1.
0x2304 System is waking up from suspend to undock
0x2305 System is waking up from suspend to eject bay
0x2404 System is waking up from hibernation to undock
0x2405 System is waking up from hibernation to eject bay
The above events are never propagated by the driver.
0x3003 Bay ejection (see 0x2x05) complete, can sleep again
0x4003 Undocked (see 0x2x04), can sleep again
0x5009 Tablet swivel: switched to tablet mode
0x500A Tablet swivel: switched to normal mode
0x500B Tablet pen insterted into its storage bay
0x500C Tablet pen removed from its storage bay
0x5010 Brightness level changed (newer Lenovo BIOSes)
The above events are propagated by the driver.
Compatibility notes:
ibm-acpi and thinkpad-acpi 0.15 (mainline kernels before 2.6.23) never
...
...
@@ -1263,3 +1349,17 @@ Sysfs interface changelog:
and the hwmon class for libsensors4 (lm-sensors 3)
compatibility. Moved all hwmon attributes to this
new platform device.
0x020100: Marker for thinkpad-acpi with hot key NVRAM polling
support. If you must, use it to know you should not
start an userspace NVRAM poller (allows to detect when
NVRAM is compiled out by the user because it is
unneeded/undesired in the first place).
0x020101: Marker for thinkpad-acpi with hot key NVRAM polling
and proper hotkey_mask semanthics (version 8 of the
NVRAM polling patch). Some development snapshots of
0.18 had an earlier version that did strange things
to hotkey_mask.
0x020200: Add poll()/select() support to the following attributes:
hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason
drivers/acpi/asus_acpi.c
浏览文件 @
dd07a8db
...
...
@@ -142,6 +142,7 @@ struct asus_hotk {
xxN
,
//M2400N, M3700N, M5200N, M6800N, S1300N, S5200N
A4S
,
//Z81sp
//(Centrino)
F3Sa
,
END_MODEL
}
model
;
//Models currently supported
u16
event_count
[
128
];
//count for each event TODO make this better
...
...
@@ -405,7 +406,20 @@ static struct model_data model_conf[END_MODEL] = {
.
brightness_get
=
"GPLV"
,
.
mt_bt_switch
=
"BLED"
,
.
mt_wled
=
"WLED"
}
},
{
.
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"
,
},
};
...
...
@@ -710,15 +724,8 @@ static int get_lcd_state(void)
{
int
lcd
=
0
;
if
(
hotk
->
model
!=
L3H
)
{
/* 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
;
}
else
{
/* L3H and the like have to be handled differently */
if
(
hotk
->
model
==
L3H
)
{
/* L3H and the like have to be handled differently */
acpi_status
status
=
0
;
struct
acpi_object_list
input
;
union
acpi_object
mt_params
[
2
];
...
...
@@ -745,6 +752,32 @@ static int get_lcd_state(void)
if
(
out_obj
.
type
==
ACPI_TYPE_INTEGER
)
/* That's what the AML code does */
lcd
=
out_obj
.
integer
.
value
>>
8
;
}
else
if
(
hotk
->
model
==
F3Sa
)
{
unsigned
long
tmp
;
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
;
}
return
(
lcd
&
1
);
...
...
@@ -1134,6 +1167,8 @@ static int asus_model_match(char *model)
return
W5A
;
else
if
(
strncmp
(
model
,
"A4S"
,
3
)
==
0
)
return
A4S
;
else
if
(
strncmp
(
model
,
"F3Sa"
,
4
)
==
0
)
return
F3Sa
;
else
return
END_MODEL
;
}
...
...
drivers/misc/Kconfig
浏览文件 @
dd07a8db
...
...
@@ -219,6 +219,25 @@ config THINKPAD_ACPI_BAY
If you are not sure, say Y here.
config THINKPAD_ACPI_HOTKEY_POLL
bool "Suport NVRAM polling for hot keys"
depends on THINKPAD_ACPI
default y
---help---
Some thinkpad models benefit from NVRAM polling to detect a few of
the hot key press events. If you know your ThinkPad model does not
need to do NVRAM polling to support any of the hot keys you use,
unselecting this option will save about 1kB of memory.
ThinkPads T40 and newer, R52 and newer, and X31 and newer are
unlikely to need NVRAM polling in their latest BIOS versions.
NVRAM polling can detect at most the following keys: ThinkPad/Access
IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so.
config ATMEL_SSC
tristate "Device driver for Atmel SSC peripheral"
...
...
drivers/misc/asus-laptop.c
浏览文件 @
dd07a8db
...
...
@@ -254,7 +254,7 @@ ASUS_LED(gled, "gaming");
* 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.
* returns
0 if write is successful, -1
else.
*/
static
int
write_acpi_int
(
acpi_handle
handle
,
const
char
*
method
,
int
val
,
struct
acpi_buffer
*
output
)
...
...
@@ -263,13 +263,19 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,
union
acpi_object
in_obj
;
//the only param we use
acpi_status
status
;
if
(
!
handle
)
return
0
;
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
);
if
(
status
==
AE_OK
)
return
0
;
else
return
-
1
;
}
static
int
read_wireless_status
(
int
mask
)
...
...
@@ -321,7 +327,7 @@ static void write_status(acpi_handle handle, int out, int mask)
switch
(
mask
)
{
case
MLED_ON
:
out
=
!
out
&
0x1
;
out
=
!
(
out
&
0x1
)
;
break
;
case
GLED_ON
:
out
=
(
out
&
0x1
)
+
1
;
...
...
@@ -335,7 +341,7 @@ static void write_status(acpi_handle handle, int out, int mask)
break
;
}
if
(
handle
&&
!
write_acpi_int
(
handle
,
NULL
,
out
,
NULL
))
if
(
write_acpi_int
(
handle
,
NULL
,
out
,
NULL
))
printk
(
ASUS_WARNING
" write failed %x
\n
"
,
mask
);
}
...
...
@@ -415,7 +421,7 @@ static int set_brightness(struct backlight_device *bd, int value)
value
=
(
0
<
value
)
?
((
15
<
value
)
?
15
:
value
)
:
0
;
/* 0 <= value <= 15 */
if
(
!
write_acpi_int
(
brightness_set_handle
,
NULL
,
value
,
NULL
))
{
if
(
write_acpi_int
(
brightness_set_handle
,
NULL
,
value
,
NULL
))
{
printk
(
ASUS_WARNING
"Error changing brightness
\n
"
);
ret
=
-
EIO
;
}
...
...
@@ -545,7 +551,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
rv
=
parse_arg
(
buf
,
count
,
&
value
);
if
(
rv
>
0
)
{
if
(
!
write_acpi_int
(
ledd_set_handle
,
NULL
,
value
,
NULL
))
if
(
write_acpi_int
(
ledd_set_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"LED display write failed
\n
"
);
else
hotk
->
ledd_status
=
(
u32
)
value
;
...
...
@@ -590,7 +596,7 @@ static ssize_t store_bluetooth(struct device *dev,
static
void
set_display
(
int
value
)
{
/* no sanity check needed for now */
if
(
!
write_acpi_int
(
display_set_handle
,
NULL
,
value
,
NULL
))
if
(
write_acpi_int
(
display_set_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting display
\n
"
);
return
;
}
...
...
@@ -647,7 +653,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
*/
static
void
set_light_sens_switch
(
int
value
)
{
if
(
!
write_acpi_int
(
ls_switch_handle
,
NULL
,
value
,
NULL
))
if
(
write_acpi_int
(
ls_switch_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting light sensor switch
\n
"
);
hotk
->
light_switch
=
value
;
}
...
...
@@ -672,7 +678,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
static
void
set_light_sens_level
(
int
value
)
{
if
(
!
write_acpi_int
(
ls_level_handle
,
NULL
,
value
,
NULL
))
if
(
write_acpi_int
(
ls_level_handle
,
NULL
,
value
,
NULL
))
printk
(
ASUS_WARNING
"Error setting light sensor level
\n
"
);
hotk
->
light_level
=
value
;
}
...
...
@@ -860,7 +866,7 @@ static int asus_hotk_get_info(void)
printk
(
ASUS_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
))
{
if
(
write_acpi_int
(
hotk
->
handle
,
"INIT"
,
0
,
&
buffer
))
{
printk
(
ASUS_ERR
"Hotkey initialization failed
\n
"
);
return
-
ENODEV
;
}
...
...
drivers/misc/sony-laptop.c
浏览文件 @
dd07a8db
...
...
@@ -73,7 +73,7 @@
if (debug) printk(KERN_WARNING DRV_PFX msg); \
} while (0)
#define SONY_LAPTOP_DRIVER_VERSION "0.
5
"
#define SONY_LAPTOP_DRIVER_VERSION "0.
6
"
#define SONY_NC_CLASS "sony-nc"
#define SONY_NC_HID "SNY5001"
...
...
@@ -146,68 +146,70 @@ struct sony_laptop_keypress {
* and input layer indexes in the keymap
*/
static
int
sony_laptop_input_index
[]
=
{
-
1
,
/* no event */
-
1
,
/* SONYPI_EVENT_JOGDIAL_DOWN */
-
1
,
/* SONYPI_EVENT_JOGDIAL_UP */
-
1
,
/* SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
-
1
,
/* SONYPI_EVENT_JOGDIAL_UP_PRESSED */
-
1
,
/* SONYPI_EVENT_JOGDIAL_PRESSED */
-
1
,
/* SONYPI_EVENT_JOGDIAL_RELEASED */
0
,
/* SONYPI_EVENT_CAPTURE_PRESSED */
1
,
/* SONYPI_EVENT_CAPTURE_RELEASED */
2
,
/* SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
3
,
/* SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
4
,
/* SONYPI_EVENT_FNKEY_ESC */
5
,
/* SONYPI_EVENT_FNKEY_F1 */
6
,
/* SONYPI_EVENT_FNKEY_F2 */
7
,
/* SONYPI_EVENT_FNKEY_F3 */
8
,
/* SONYPI_EVENT_FNKEY_F4 */
9
,
/* SONYPI_EVENT_FNKEY_F5 */
10
,
/* SONYPI_EVENT_FNKEY_F6 */
11
,
/* SONYPI_EVENT_FNKEY_F7 */
12
,
/* SONYPI_EVENT_FNKEY_F8 */
13
,
/* SONYPI_EVENT_FNKEY_F9 */
14
,
/* SONYPI_EVENT_FNKEY_F10 */
15
,
/* SONYPI_EVENT_FNKEY_F11 */
16
,
/* SONYPI_EVENT_FNKEY_F12 */
17
,
/* SONYPI_EVENT_FNKEY_1 */
18
,
/* SONYPI_EVENT_FNKEY_2 */
19
,
/* SONYPI_EVENT_FNKEY_D */
20
,
/* SONYPI_EVENT_FNKEY_E */
21
,
/* SONYPI_EVENT_FNKEY_F */
22
,
/* SONYPI_EVENT_FNKEY_S */
23
,
/* SONYPI_EVENT_FNKEY_B */
24
,
/* SONYPI_EVENT_BLUETOOTH_PRESSED */
25
,
/* SONYPI_EVENT_PKEY_P1 */
26
,
/* SONYPI_EVENT_PKEY_P2 */
27
,
/* SONYPI_EVENT_PKEY_P3 */
28
,
/* SONYPI_EVENT_BACK_PRESSED */
-
1
,
/* SONYPI_EVENT_LID_CLOSED */
-
1
,
/* SONYPI_EVENT_LID_OPENED */
29
,
/* SONYPI_EVENT_BLUETOOTH_ON */
30
,
/* SONYPI_EVENT_BLUETOOTH_OFF */
31
,
/* SONYPI_EVENT_HELP_PRESSED */
32
,
/* SONYPI_EVENT_FNKEY_ONLY */
33
,
/* SONYPI_EVENT_JOGDIAL_FAST_DOWN */
34
,
/* SONYPI_EVENT_JOGDIAL_FAST_UP */
35
,
/* SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
36
,
/* SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
37
,
/* SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
38
,
/* SONYPI_EVENT_JOGDIAL_VFAST_UP */
39
,
/* SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
40
,
/* SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
41
,
/* SONYPI_EVENT_ZOOM_PRESSED */
42
,
/* SONYPI_EVENT_THUMBPHRASE_PRESSED */
43
,
/* SONYPI_EVENT_MEYE_FACE */
44
,
/* SONYPI_EVENT_MEYE_OPPOSITE */
45
,
/* SONYPI_EVENT_MEMORYSTICK_INSERT */
46
,
/* SONYPI_EVENT_MEMORYSTICK_EJECT */
-
1
,
/* SONYPI_EVENT_ANYBUTTON_RELEASED */
-
1
,
/* SONYPI_EVENT_BATTERY_INSERT */
-
1
,
/* SONYPI_EVENT_BATTERY_REMOVE */
-
1
,
/* SONYPI_EVENT_FNKEY_RELEASED */
47
,
/* SONYPI_EVENT_WIRELESS_ON */
48
,
/* SONYPI_EVENT_WIRELESS_OFF */
-
1
,
/* 0 no event */
-
1
,
/* 1 SONYPI_EVENT_JOGDIAL_DOWN */
-
1
,
/* 2 SONYPI_EVENT_JOGDIAL_UP */
-
1
,
/* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
-
1
,
/* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
-
1
,
/* 5 SONYPI_EVENT_JOGDIAL_PRESSED */
-
1
,
/* 6 SONYPI_EVENT_JOGDIAL_RELEASED */
0
,
/* 7 SONYPI_EVENT_CAPTURE_PRESSED */
1
,
/* 8 SONYPI_EVENT_CAPTURE_RELEASED */
2
,
/* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
3
,
/* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
4
,
/* 11 SONYPI_EVENT_FNKEY_ESC */
5
,
/* 12 SONYPI_EVENT_FNKEY_F1 */
6
,
/* 13 SONYPI_EVENT_FNKEY_F2 */
7
,
/* 14 SONYPI_EVENT_FNKEY_F3 */
8
,
/* 15 SONYPI_EVENT_FNKEY_F4 */
9
,
/* 16 SONYPI_EVENT_FNKEY_F5 */
10
,
/* 17 SONYPI_EVENT_FNKEY_F6 */
11
,
/* 18 SONYPI_EVENT_FNKEY_F7 */
12
,
/* 19 SONYPI_EVENT_FNKEY_F8 */
13
,
/* 20 SONYPI_EVENT_FNKEY_F9 */
14
,
/* 21 SONYPI_EVENT_FNKEY_F10 */
15
,
/* 22 SONYPI_EVENT_FNKEY_F11 */
16
,
/* 23 SONYPI_EVENT_FNKEY_F12 */
17
,
/* 24 SONYPI_EVENT_FNKEY_1 */
18
,
/* 25 SONYPI_EVENT_FNKEY_2 */
19
,
/* 26 SONYPI_EVENT_FNKEY_D */
20
,
/* 27 SONYPI_EVENT_FNKEY_E */
21
,
/* 28 SONYPI_EVENT_FNKEY_F */
22
,
/* 29 SONYPI_EVENT_FNKEY_S */
23
,
/* 30 SONYPI_EVENT_FNKEY_B */
24
,
/* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
25
,
/* 32 SONYPI_EVENT_PKEY_P1 */
26
,
/* 33 SONYPI_EVENT_PKEY_P2 */
27
,
/* 34 SONYPI_EVENT_PKEY_P3 */
28
,
/* 35 SONYPI_EVENT_BACK_PRESSED */
-
1
,
/* 36 SONYPI_EVENT_LID_CLOSED */
-
1
,
/* 37 SONYPI_EVENT_LID_OPENED */
29
,
/* 38 SONYPI_EVENT_BLUETOOTH_ON */
30
,
/* 39 SONYPI_EVENT_BLUETOOTH_OFF */
31
,
/* 40 SONYPI_EVENT_HELP_PRESSED */
32
,
/* 41 SONYPI_EVENT_FNKEY_ONLY */
33
,
/* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
34
,
/* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
35
,
/* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
36
,
/* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
37
,
/* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
38
,
/* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
39
,
/* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
40
,
/* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
41
,
/* 50 SONYPI_EVENT_ZOOM_PRESSED */
42
,
/* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
43
,
/* 52 SONYPI_EVENT_MEYE_FACE */
44
,
/* 53 SONYPI_EVENT_MEYE_OPPOSITE */
45
,
/* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
46
,
/* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
-
1
,
/* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
-
1
,
/* 57 SONYPI_EVENT_BATTERY_INSERT */
-
1
,
/* 58 SONYPI_EVENT_BATTERY_REMOVE */
-
1
,
/* 59 SONYPI_EVENT_FNKEY_RELEASED */
47
,
/* 60 SONYPI_EVENT_WIRELESS_ON */
48
,
/* 61 SONYPI_EVENT_WIRELESS_OFF */
49
,
/* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
50
,
/* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
};
static
int
sony_laptop_input_keycode_map
[]
=
{
...
...
@@ -260,6 +262,8 @@ static int sony_laptop_input_keycode_map[] = {
KEY_RESERVED
,
/* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
KEY_WLAN
,
/* 47 SONYPI_EVENT_WIRELESS_ON */
KEY_WLAN
,
/* 48 SONYPI_EVENT_WIRELESS_OFF */
KEY_ZOOMIN
,
/* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
KEY_ZOOMOUT
/* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
};
/* release buttons after a short delay if pressed */
...
...
@@ -311,7 +315,7 @@ static void sony_laptop_report_input_event(u8 event)
break
;
default:
if
(
event
>
ARRAY_SIZE
(
sony_laptop_input_keycode_map
))
{
if
(
event
>
ARRAY_SIZE
(
sony_laptop_input_index
))
{
dprintk
(
"sony_laptop_report_input_event, event not known: %d
\n
"
,
event
);
break
;
}
...
...
@@ -875,6 +879,15 @@ static const struct dmi_system_id sony_nc_ids[] = {
DMI_MATCH
(
DMI_PRODUCT_NAME
,
"VGN-C"
),
},
},
{
.
ident
=
"Sony Vaio N Series"
,
.
callback
=
sony_nc_C_enable
,
.
driver_data
=
sony_C_events
,
.
matches
=
{
DMI_MATCH
(
DMI_SYS_VENDOR
,
"Sony Corporation"
),
DMI_MATCH
(
DMI_PRODUCT_NAME
,
"VGN-N"
),
},
},
{
}
};
...
...
@@ -1169,10 +1182,12 @@ static struct acpi_driver sony_nc_driver = {
#define SONYPI_DEVICE_TYPE1 0x00000001
#define SONYPI_DEVICE_TYPE2 0x00000002
#define SONYPI_DEVICE_TYPE3 0x00000004
#define SONYPI_DEVICE_TYPE4 0x00000008
#define SONYPI_TYPE1_OFFSET 0x04
#define SONYPI_TYPE2_OFFSET 0x12
#define SONYPI_TYPE3_OFFSET 0x12
#define SONYPI_TYPE4_OFFSET 0x12
struct
sony_pic_ioport
{
struct
acpi_resource_io
io1
;
...
...
@@ -1185,18 +1200,33 @@ struct sony_pic_irq {
struct
list_head
list
;
};
struct
sonypi_eventtypes
{
u8
data
;
unsigned
long
mask
;
struct
sonypi_event
*
events
;
};
struct
device_ctrl
{
int
model
;
int
(
*
handle_irq
)(
const
u8
,
const
u8
);
u16
evport_offset
;
u8
has_camera
;
u8
has_bluetooth
;
u8
has_wwan
;
struct
sonypi_eventtypes
*
event_types
;
};
struct
sony_pic_dev
{
int
model
;
u16
evport_offset
;
u8
camera_power
;
u8
bluetooth_power
;
u8
wwan_power
;
struct
device_ctrl
*
control
;
struct
acpi_device
*
acpi_dev
;
struct
sony_pic_irq
*
cur_irq
;
struct
sony_pic_ioport
*
cur_ioport
;
struct
list_head
interrupts
;
struct
list_head
ioports
;
struct
mutex
lock
;
u8
camera_power
;
u8
bluetooth_power
;
u8
wwan_power
;
};
static
struct
sony_pic_dev
spic_dev
=
{
...
...
@@ -1253,6 +1283,7 @@ static struct sonypi_event sonypi_joggerev[] = {
static
struct
sonypi_event
sonypi_captureev
[]
=
{
{
0x05
,
SONYPI_EVENT_CAPTURE_PARTIALPRESSED
},
{
0x07
,
SONYPI_EVENT_CAPTURE_PRESSED
},
{
0x40
,
SONYPI_EVENT_CAPTURE_PRESSED
},
{
0x01
,
SONYPI_EVENT_CAPTURE_PARTIALRELEASED
},
{
0
,
0
}
};
...
...
@@ -1289,7 +1320,6 @@ static struct sonypi_event sonypi_pkeyev[] = {
{
0x01
,
SONYPI_EVENT_PKEY_P1
},
{
0x02
,
SONYPI_EVENT_PKEY_P2
},
{
0x04
,
SONYPI_EVENT_PKEY_P3
},
{
0x5c
,
SONYPI_EVENT_PKEY_P1
},
{
0
,
0
}
};
...
...
@@ -1331,6 +1361,8 @@ static struct sonypi_event sonypi_lidev[] = {
/* The set of possible zoom events */
static
struct
sonypi_event
sonypi_zoomev
[]
=
{
{
0x39
,
SONYPI_EVENT_ZOOM_PRESSED
},
{
0x10
,
SONYPI_EVENT_ZOOM_IN_PRESSED
},
{
0x20
,
SONYPI_EVENT_ZOOM_OUT_PRESSED
},
{
0
,
0
}
};
...
...
@@ -1361,76 +1393,58 @@ static struct sonypi_event sonypi_batteryev[] = {
{
0
,
0
}
};
static
struct
sonypi_eventtypes
{
int
model
;
u8
data
;
unsigned
long
mask
;
struct
sonypi_event
*
events
;
}
sony_pic_eventtypes
[]
=
{
{
SONYPI_DEVICE_TYPE1
,
0
,
0xffffffff
,
sonypi_releaseev
},
{
SONYPI_DEVICE_TYPE1
,
0x70
,
SONYPI_MEYE_MASK
,
sonypi_meyeev
},
{
SONYPI_DEVICE_TYPE1
,
0x30
,
SONYPI_LID_MASK
,
sonypi_lidev
},
{
SONYPI_DEVICE_TYPE1
,
0x60
,
SONYPI_CAPTURE_MASK
,
sonypi_captureev
},
{
SONYPI_DEVICE_TYPE1
,
0x10
,
SONYPI_JOGGER_MASK
,
sonypi_joggerev
},
{
SONYPI_DEVICE_TYPE1
,
0x20
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
SONYPI_DEVICE_TYPE1
,
0x30
,
SONYPI_BLUETOOTH_MASK
,
sonypi_blueev
},
{
SONYPI_DEVICE_TYPE1
,
0x40
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
SONYPI_DEVICE_TYPE1
,
0x30
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
SONYPI_DEVICE_TYPE1
,
0x40
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
SONYPI_DEVICE_TYPE2
,
0
,
0xffffffff
,
sonypi_releaseev
},
{
SONYPI_DEVICE_TYPE2
,
0x38
,
SONYPI_LID_MASK
,
sonypi_lidev
},
{
SONYPI_DEVICE_TYPE2
,
0x11
,
SONYPI_JOGGER_MASK
,
sonypi_joggerev
},
{
SONYPI_DEVICE_TYPE2
,
0x61
,
SONYPI_CAPTURE_MASK
,
sonypi_captureev
},
{
SONYPI_DEVICE_TYPE2
,
0x21
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
SONYPI_DEVICE_TYPE2
,
0x31
,
SONYPI_BLUETOOTH_MASK
,
sonypi_blueev
},
{
SONYPI_DEVICE_TYPE2
,
0x08
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
SONYPI_DEVICE_TYPE2
,
0x11
,
SONYPI_BACK_MASK
,
sonypi_backev
},
{
SONYPI_DEVICE_TYPE2
,
0x21
,
SONYPI_HELP_MASK
,
sonypi_helpev
},
{
SONYPI_DEVICE_TYPE2
,
0x21
,
SONYPI_ZOOM_MASK
,
sonypi_zoomev
},
{
SONYPI_DEVICE_TYPE2
,
0x20
,
SONYPI_THUMBPHRASE_MASK
,
sonypi_thumbphraseev
},
{
SONYPI_DEVICE_TYPE2
,
0x31
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
SONYPI_DEVICE_TYPE2
,
0x41
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
SONYPI_DEVICE_TYPE2
,
0x31
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
SONYPI_DEVICE_TYPE3
,
0
,
0xffffffff
,
sonypi_releaseev
},
{
SONYPI_DEVICE_TYPE3
,
0x21
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
SONYPI_DEVICE_TYPE3
,
0x31
,
SONYPI_WIRELESS_MASK
,
sonypi_wlessev
},
{
SONYPI_DEVICE_TYPE3
,
0x31
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
SONYPI_DEVICE_TYPE3
,
0x41
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
SONYPI_DEVICE_TYPE3
,
0x31
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0
}
static
struct
sonypi_eventtypes
type1_events
[]
=
{
{
0
,
0xffffffff
,
sonypi_releaseev
},
{
0x70
,
SONYPI_MEYE_MASK
,
sonypi_meyeev
},
{
0x30
,
SONYPI_LID_MASK
,
sonypi_lidev
},
{
0x60
,
SONYPI_CAPTURE_MASK
,
sonypi_captureev
},
{
0x10
,
SONYPI_JOGGER_MASK
,
sonypi_joggerev
},
{
0x20
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
0x30
,
SONYPI_BLUETOOTH_MASK
,
sonypi_blueev
},
{
0x40
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0x30
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
0x40
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
0
},
};
static
struct
sonypi_eventtypes
type2_events
[]
=
{
{
0
,
0xffffffff
,
sonypi_releaseev
},
{
0x38
,
SONYPI_LID_MASK
,
sonypi_lidev
},
{
0x11
,
SONYPI_JOGGER_MASK
,
sonypi_joggerev
},
{
0x61
,
SONYPI_CAPTURE_MASK
,
sonypi_captureev
},
{
0x21
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
0x31
,
SONYPI_BLUETOOTH_MASK
,
sonypi_blueev
},
{
0x08
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0x11
,
SONYPI_BACK_MASK
,
sonypi_backev
},
{
0x21
,
SONYPI_HELP_MASK
,
sonypi_helpev
},
{
0x21
,
SONYPI_ZOOM_MASK
,
sonypi_zoomev
},
{
0x20
,
SONYPI_THUMBPHRASE_MASK
,
sonypi_thumbphraseev
},
{
0x31
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
0x41
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
0x31
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0
},
};
static
struct
sonypi_eventtypes
type3_events
[]
=
{
{
0
,
0xffffffff
,
sonypi_releaseev
},
{
0x21
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
0x31
,
SONYPI_WIRELESS_MASK
,
sonypi_wlessev
},
{
0x31
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
0x41
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
0x31
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0
},
};
static
struct
sonypi_eventtypes
type4_events
[]
=
{
{
0
,
0xffffffff
,
sonypi_releaseev
},
{
0x21
,
SONYPI_FNKEY_MASK
,
sonypi_fnkeyev
},
{
0x31
,
SONYPI_WIRELESS_MASK
,
sonypi_wlessev
},
{
0x31
,
SONYPI_MEMORYSTICK_MASK
,
sonypi_memorystickev
},
{
0x41
,
SONYPI_BATTERY_MASK
,
sonypi_batteryev
},
{
0x05
,
SONYPI_PKEY_MASK
,
sonypi_pkeyev
},
{
0x05
,
SONYPI_ZOOM_MASK
,
sonypi_zoomev
},
{
0x05
,
SONYPI_CAPTURE_MASK
,
sonypi_captureev
},
{
0
},
};
static
int
sony_pic_detect_device_type
(
void
)
{
struct
pci_dev
*
pcidev
;
int
model
=
0
;
if
((
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_82371AB_3
,
NULL
)))
model
=
SONYPI_DEVICE_TYPE1
;
else
if
((
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_ICH6_1
,
NULL
)))
model
=
SONYPI_DEVICE_TYPE3
;
else
if
((
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_ICH7_1
,
NULL
)))
model
=
SONYPI_DEVICE_TYPE3
;
else
model
=
SONYPI_DEVICE_TYPE2
;
if
(
pcidev
)
pci_dev_put
(
pcidev
);
printk
(
KERN_INFO
DRV_PFX
"detected Type%d model
\n
"
,
model
==
SONYPI_DEVICE_TYPE1
?
1
:
model
==
SONYPI_DEVICE_TYPE2
?
2
:
3
);
return
model
;
}
/* low level spic calls */
#define ITERATIONS_LONG 10000
#define ITERATIONS_SHORT 10
#define wait_on_command(command, iterations) { \
...
...
@@ -1451,7 +1465,7 @@ static u8 sony_pic_call1(u8 dev)
outb
(
dev
,
spic_dev
.
cur_ioport
->
io1
.
minimum
+
4
);
v1
=
inb_p
(
spic_dev
.
cur_ioport
->
io1
.
minimum
+
4
);
v2
=
inb_p
(
spic_dev
.
cur_ioport
->
io1
.
minimum
);
dprintk
(
"sony_pic_call1
: 0x%.4x
\n
"
,
(
v2
<<
8
)
|
v1
);
dprintk
(
"sony_pic_call1
(0x%.2x): 0x%.4x
\n
"
,
dev
,
(
v2
<<
8
)
|
v1
);
return
v2
;
}
...
...
@@ -1466,7 +1480,7 @@ static u8 sony_pic_call2(u8 dev, u8 fn)
ITERATIONS_LONG
);
outb
(
fn
,
spic_dev
.
cur_ioport
->
io1
.
minimum
);
v1
=
inb_p
(
spic_dev
.
cur_ioport
->
io1
.
minimum
);
dprintk
(
"sony_pic_call2
: 0x%.4x
\n
"
,
v1
);
dprintk
(
"sony_pic_call2
(0x%.2x - 0x%.2x): 0x%.4x
\n
"
,
dev
,
fn
,
v1
);
return
v1
;
}
...
...
@@ -1481,10 +1495,105 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
wait_on_command
(
inb_p
(
spic_dev
.
cur_ioport
->
io1
.
minimum
+
4
)
&
2
,
ITERATIONS_LONG
);
outb
(
v
,
spic_dev
.
cur_ioport
->
io1
.
minimum
);
v1
=
inb_p
(
spic_dev
.
cur_ioport
->
io1
.
minimum
);
dprintk
(
"sony_pic_call3: 0x%.4x
\n
"
,
v1
);
dprintk
(
"sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x
\n
"
,
dev
,
fn
,
v
,
v1
);
return
v1
;
}
/*
* minidrivers for SPIC models
*/
static
int
type4_handle_irq
(
const
u8
data_mask
,
const
u8
ev
)
{
/*
* 0x31 could mean we have to take some extra action and wait for
* the next irq for some Type4 models, it will generate a new
* irq and we can read new data from the device:
* - 0x5c and 0x5f requires 0xA0
* - 0x61 requires 0xB3
*/
if
(
data_mask
==
0x31
)
{
if
(
ev
==
0x5c
||
ev
==
0x5f
)
sony_pic_call1
(
0xA0
);
else
if
(
ev
==
0x61
)
sony_pic_call1
(
0xB3
);
return
0
;
}
return
1
;
}
static
struct
device_ctrl
spic_types
[]
=
{
{
.
model
=
SONYPI_DEVICE_TYPE1
,
.
handle_irq
=
NULL
,
.
evport_offset
=
SONYPI_TYPE1_OFFSET
,
.
event_types
=
type1_events
,
},
{
.
model
=
SONYPI_DEVICE_TYPE2
,
.
handle_irq
=
NULL
,
.
evport_offset
=
SONYPI_TYPE2_OFFSET
,
.
event_types
=
type2_events
,
},
{
.
model
=
SONYPI_DEVICE_TYPE3
,
.
handle_irq
=
NULL
,
.
evport_offset
=
SONYPI_TYPE3_OFFSET
,
.
event_types
=
type3_events
,
},
{
.
model
=
SONYPI_DEVICE_TYPE4
,
.
handle_irq
=
type4_handle_irq
,
.
evport_offset
=
SONYPI_TYPE4_OFFSET
,
.
event_types
=
type4_events
,
},
};
static
void
sony_pic_detect_device_type
(
struct
sony_pic_dev
*
dev
)
{
struct
pci_dev
*
pcidev
;
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_82371AB_3
,
NULL
);
if
(
pcidev
)
{
dev
->
control
=
&
spic_types
[
0
];
goto
out
;
}
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_ICH6_1
,
NULL
);
if
(
pcidev
)
{
dev
->
control
=
&
spic_types
[
2
];
goto
out
;
}
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_ICH7_1
,
NULL
);
if
(
pcidev
)
{
dev
->
control
=
&
spic_types
[
3
];
goto
out
;
}
pcidev
=
pci_get_device
(
PCI_VENDOR_ID_INTEL
,
PCI_DEVICE_ID_INTEL_ICH8_4
,
NULL
);
if
(
pcidev
)
{
dev
->
control
=
&
spic_types
[
3
];
goto
out
;
}
/* default */
dev
->
control
=
&
spic_types
[
1
];
out:
if
(
pcidev
)
pci_dev_put
(
pcidev
);
printk
(
KERN_INFO
DRV_PFX
"detected Type%d model
\n
"
,
dev
->
control
->
model
==
SONYPI_DEVICE_TYPE1
?
1
:
dev
->
control
->
model
==
SONYPI_DEVICE_TYPE2
?
2
:
dev
->
control
->
model
==
SONYPI_DEVICE_TYPE3
?
3
:
4
);
}
/* camera tests and poweron/poweroff */
#define SONYPI_CAMERA_PICTURE 5
#define SONYPI_CAMERA_CONTROL 0x10
...
...
@@ -2253,7 +2362,7 @@ static int sony_pic_enable(struct acpi_device *device,
buffer
.
pointer
=
resource
;
/* setup Type 1 resources */
if
(
spic_dev
.
model
==
SONYPI_DEVICE_TYPE1
)
{
if
(
spic_dev
.
control
->
model
==
SONYPI_DEVICE_TYPE1
)
{
/* setup io resources */
resource
->
res1
.
type
=
ACPI_RESOURCE_TYPE_IO
;
...
...
@@ -2335,39 +2444,49 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
if
(
dev
->
cur_ioport
->
io2
.
minimum
)
data_mask
=
inb_p
(
dev
->
cur_ioport
->
io2
.
minimum
);
else
data_mask
=
inb_p
(
dev
->
cur_ioport
->
io1
.
minimum
+
dev
->
evport_offset
);
data_mask
=
inb_p
(
dev
->
cur_ioport
->
io1
.
minimum
+
dev
->
control
->
evport_offset
);
dprintk
(
"event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)
\n
"
,
ev
,
data_mask
,
dev
->
cur_ioport
->
io1
.
minimum
,
dev
->
evport_offset
);
ev
,
data_mask
,
dev
->
cur_ioport
->
io1
.
minimum
,
dev
->
control
->
evport_offset
);
if
(
ev
==
0x00
||
ev
==
0xff
)
return
IRQ_HANDLED
;
for
(
i
=
0
;
sony_pic_eventtypes
[
i
].
model
;
i
++
)
{
if
(
spic_dev
.
model
!=
sony_pic_eventtypes
[
i
].
model
)
continue
;
for
(
i
=
0
;
dev
->
control
->
event_types
[
i
].
mask
;
i
++
)
{
if
((
data_mask
&
sony_pic_event
types
[
i
].
data
)
!=
sony_pic_event
types
[
i
].
data
)
if
((
data_mask
&
dev
->
control
->
event_
types
[
i
].
data
)
!=
dev
->
control
->
event_
types
[
i
].
data
)
continue
;
if
(
!
(
mask
&
sony_pic_event
types
[
i
].
mask
))
if
(
!
(
mask
&
dev
->
control
->
event_
types
[
i
].
mask
))
continue
;
for
(
j
=
0
;
sony_pic_event
types
[
i
].
events
[
j
].
event
;
j
++
)
{
if
(
ev
==
sony_pic_event
types
[
i
].
events
[
j
].
data
)
{
for
(
j
=
0
;
dev
->
control
->
event_
types
[
i
].
events
[
j
].
event
;
j
++
)
{
if
(
ev
==
dev
->
control
->
event_
types
[
i
].
events
[
j
].
data
)
{
device_event
=
sony_pic_eventtypes
[
i
].
events
[
j
].
event
;
dev
->
control
->
event_types
[
i
].
events
[
j
].
event
;
goto
found
;
}
}
}
/* Still not able to decode the event try to pass
* it over to the minidriver
*/
if
(
dev
->
control
->
handle_irq
&&
dev
->
control
->
handle_irq
(
data_mask
,
ev
)
==
0
)
return
IRQ_HANDLED
;
dprintk
(
"unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)
\n
"
,
ev
,
data_mask
,
dev
->
cur_ioport
->
io1
.
minimum
,
dev
->
control
->
evport_offset
);
return
IRQ_HANDLED
;
found:
sony_laptop_report_input_event
(
device_event
);
acpi_bus_generate_proc_event
(
spic_dev
.
acpi_dev
,
1
,
device_event
);
acpi_bus_generate_proc_event
(
dev
->
acpi_dev
,
1
,
device_event
);
sonypi_compat_report_event
(
device_event
);
return
IRQ_HANDLED
;
...
...
@@ -2429,23 +2548,9 @@ static int sony_pic_add(struct acpi_device *device)
spic_dev
.
acpi_dev
=
device
;
strcpy
(
acpi_device_class
(
device
),
"sony/hotkey"
);
s
pic_dev
.
model
=
sony_pic_detect_device_type
(
);
s
ony_pic_detect_device_type
(
&
spic_dev
);
mutex_init
(
&
spic_dev
.
lock
);
/* model specific characteristics */
switch
(
spic_dev
.
model
)
{
case
SONYPI_DEVICE_TYPE1
:
spic_dev
.
evport_offset
=
SONYPI_TYPE1_OFFSET
;
break
;
case
SONYPI_DEVICE_TYPE3
:
spic_dev
.
evport_offset
=
SONYPI_TYPE3_OFFSET
;
break
;
case
SONYPI_DEVICE_TYPE2
:
default:
spic_dev
.
evport_offset
=
SONYPI_TYPE2_OFFSET
;
break
;
}
/* read _PRS resources */
result
=
sony_pic_possible_resources
(
device
);
if
(
result
)
{
...
...
drivers/misc/thinkpad_acpi.c
浏览文件 @
dd07a8db
仅显示部分。点此显示全部。
...
...
@@ -3,7 +3,7 @@
*
*
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
* Copyright (C) 2006-200
7
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
* Copyright (C) 2006-200
8
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
*
* 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
...
...
@@ -21,11 +21,13 @@
* 02110-1301, USA.
*/
#define
IBM_VERSION "0.17
"
#define TPACPI_SYSFS_VERSION 0x020
0
00
#define
TPACPI_VERSION "0.19
"
#define TPACPI_SYSFS_VERSION 0x020
2
00
/*
* Changelog:
* 2007-10-20 changelog trimmed down
*
* 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
* drivers/misc.
*
...
...
@@ -33,89 +35,219 @@
* changelog now lives in git commit history, and will
* not be updated further in-file.
*
* 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
* 2005-03-17 0.11 support for 600e, 770x
* thanks to Jamie Lentin <lentinj@dial.pipex.com>
* support for 770e, G41
* G40 and G41 don't have a thinklight
* temperatures no longer experimental
* experimental brightness control
* experimental volume control
* experimental fan enable/disable
* 2005-01-16 0.10 fix module loading on R30, R31
* 2005-01-16 0.9 support for 570, R30, R31
* ultrabay support on A22p, A3x
* limit arg for cmos, led, beep, drop experimental status
* more capable led control on A21e, A22p, T20-22, X20
* experimental temperatures and fan speed
* experimental embedded controller register dump
* mark more functions as __init, drop incorrect __exit
* use MODULE_VERSION
*
* 2005-01-16 0.9 use MODULE_VERSION
* thanks to Henrik Brix Andersen <brix@gentoo.org>
* fix parameter passing on module loading
* thanks to Rusty Russell <rusty@rustcorp.com.au>
* thanks to Jim Radford <radford@blackbean.org>
* 2004-11-08 0.8 fix init error case, don't return from a macro
* thanks to Chris Wright <chrisw@osdl.org>
* 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
* fix led control on A21e
* 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
* 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
* proc file format changed
* video_switch command
* experimental cmos control
* experimental led control
* experimental acpi sounds
* 2004-09-16 0.4 support for module parameters
* hotkey mask can be prefixed by 0x
* video output switching
* video expansion control
* ultrabay eject support
* removed lcd brightness/on/off control, didn't work
* 2004-08-17 0.3 support for R40
* lcd off, brightness control
* thinklight on/off
* 2004-08-14 0.2 support for T series, X20
* bluetooth enable/disable
* hotkey events disabled by default
* removed fan control, currently useless
* 2004-08-09 0.1 initial release, support for X series
*/
#include "thinkpad_acpi.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/nvram.h>
#include <linux/proc_fs.h>
#include <linux/sysfs.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
#include <linux/pci_ids.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
#define TP_CMOS_VOLUME_UP 1
#define TP_CMOS_VOLUME_MUTE 2
#define TP_CMOS_BRIGHTNESS_UP 4
#define TP_CMOS_BRIGHTNESS_DOWN 5
/* NVRAM Addresses */
enum
tp_nvram_addr
{
TP_NVRAM_ADDR_HK2
=
0x57
,
TP_NVRAM_ADDR_THINKLIGHT
=
0x58
,
TP_NVRAM_ADDR_VIDEO
=
0x59
,
TP_NVRAM_ADDR_BRIGHTNESS
=
0x5e
,
TP_NVRAM_ADDR_MIXER
=
0x60
,
};
MODULE_AUTHOR
(
"Borislav Deianov, Henrique de Moraes Holschuh"
);
MODULE_DESCRIPTION
(
IBM_DESC
);
MODULE_VERSION
(
IBM_VERSION
);
MODULE_LICENSE
(
"GPL"
);
/* NVRAM bit masks */
enum
{
TP_NVRAM_MASK_HKT_THINKPAD
=
0x08
,
TP_NVRAM_MASK_HKT_ZOOM
=
0x20
,
TP_NVRAM_MASK_HKT_DISPLAY
=
0x40
,
TP_NVRAM_MASK_HKT_HIBERNATE
=
0x80
,
TP_NVRAM_MASK_THINKLIGHT
=
0x10
,
TP_NVRAM_MASK_HKT_DISPEXPND
=
0x30
,
TP_NVRAM_MASK_HKT_BRIGHTNESS
=
0x20
,
TP_NVRAM_MASK_LEVEL_BRIGHTNESS
=
0x0f
,
TP_NVRAM_POS_LEVEL_BRIGHTNESS
=
0
,
TP_NVRAM_MASK_MUTE
=
0x40
,
TP_NVRAM_MASK_HKT_VOLUME
=
0x80
,
TP_NVRAM_MASK_LEVEL_VOLUME
=
0x0f
,
TP_NVRAM_POS_LEVEL_VOLUME
=
0
,
};
/*
Please remove this in year 2009
*/
MODULE_ALIAS
(
"ibm_acpi"
);
/*
ACPI HIDs
*/
#define TPACPI_ACPI_HKEY_HID "IBM0068"
/*
* DMI matching for module autoloading
*
* See http://thinkwiki.org/wiki/List_of_DMI_IDs
* See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
*
* Only models listed in thinkwiki will be supported, so add yours
* if it is not there yet.
/* Input IDs */
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054
/* "TP" */
#define TPACPI_HKEY_INPUT_VERSION 0x4101
/****************************************************************************
* Main driver
*/
#define IBM_BIOS_MODULE_ALIAS(__type) \
MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
/* Non-ancient thinkpads */
MODULE_ALIAS
(
"dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*"
);
MODULE_ALIAS
(
"dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*"
);
#define TPACPI_NAME "thinkpad"
#define TPACPI_DESC "ThinkPad ACPI Extras"
#define TPACPI_FILE TPACPI_NAME "_acpi"
#define TPACPI_URL "http://ibm-acpi.sf.net/"
#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net"
#define TPACPI_PROC_DIR "ibm"
#define TPACPI_ACPI_EVENT_PREFIX "ibm"
#define TPACPI_DRVR_NAME TPACPI_FILE
#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
#define TPACPI_MAX_ACPI_ARGS 3
/* Debugging */
#define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_ERR KERN_ERR TPACPI_LOG
#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
#define TPACPI_INFO KERN_INFO TPACPI_LOG
#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_INIT 0x0001
#define TPACPI_DBG_EXIT 0x0002
#define dbg_printk(a_dbg_level, format, arg...) \
do { if (dbg_level & a_dbg_level) \
printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
} while (0)
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
dbg_printk(a_dbg_level, format, ## arg)
static
const
char
*
str_supported
(
int
is_supported
);
#else
#define vdbg_printk(a_dbg_level, format, arg...)
#endif
/* Ancient thinkpad BIOSes have to be identified by
* BIOS type or model number, and there are far less
* BIOS types than model numbers... */
IBM_BIOS_MODULE_ALIAS
(
"I[B,D,H,I,M,N,O,T,W,V,Y,Z]"
);
IBM_BIOS_MODULE_ALIAS
(
"1[0,3,6,8,A-G,I,K,M-P,S,T]"
);
IBM_BIOS_MODULE_ALIAS
(
"K[U,X-Z]"
);
#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
#define __unused __attribute__ ((unused))
/****************************************************************************
* Driver-wide structs and misc. variables
*/
struct
ibm_struct
;
struct
tp_acpi_drv_struct
{
const
struct
acpi_device_id
*
hid
;
struct
acpi_driver
*
driver
;
void
(
*
notify
)
(
struct
ibm_struct
*
,
u32
);
acpi_handle
*
handle
;
u32
type
;
struct
acpi_device
*
device
;
};
struct
ibm_struct
{
char
*
name
;
int
(
*
read
)
(
char
*
);
int
(
*
write
)
(
char
*
);
void
(
*
exit
)
(
void
);
void
(
*
resume
)
(
void
);
void
(
*
suspend
)
(
pm_message_t
state
);
struct
list_head
all_drivers
;
struct
tp_acpi_drv_struct
*
acpi
;
struct
{
u8
acpi_driver_registered
:
1
;
u8
acpi_notify_installed
:
1
;
u8
proc_created
:
1
;
u8
init_called
:
1
;
u8
experimental
:
1
;
}
flags
;
};
struct
ibm_init_struct
{
char
param
[
32
];
int
(
*
init
)
(
struct
ibm_init_struct
*
);
struct
ibm_struct
*
data
;
};
static
struct
{
#ifdef CONFIG_THINKPAD_ACPI_BAY
u32
bay_status
:
1
;
u32
bay_eject
:
1
;
u32
bay_status2
:
1
;
u32
bay_eject2
:
1
;
#endif
u32
bluetooth
:
1
;
u32
hotkey
:
1
;
u32
hotkey_mask
:
1
;
u32
hotkey_wlsw
:
1
;
u32
light
:
1
;
u32
light_status
:
1
;
u32
bright_16levels
:
1
;
u32
wan
:
1
;
u32
fan_ctrl_status_undef
:
1
;
u32
input_device_registered
:
1
;
u32
platform_drv_registered
:
1
;
u32
platform_drv_attrs_registered
:
1
;
u32
sensors_pdrv_registered
:
1
;
u32
sensors_pdrv_attrs_registered
:
1
;
u32
sensors_pdev_attrs_registered
:
1
;
u32
hotkey_poll_active
:
1
;
}
tp_features
;
struct
thinkpad_id_data
{
unsigned
int
vendor
;
/* ThinkPad vendor:
* PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
char
*
bios_version_str
;
/* Something like 1ZET51WW (1.03z) */
char
*
ec_version_str
;
/* Something like 1ZHT51WW-1.04a */
u16
bios_model
;
/* Big Endian, TP-1Y = 0x5931, 0 = unknown */
u16
ec_model
;
char
*
model_str
;
};
static
struct
thinkpad_id_data
thinkpad_id
;
static
enum
{
TPACPI_LIFE_INIT
=
0
,
...
...
@@ -123,6 +255,9 @@ static enum {
TPACPI_LIFE_EXITING
,
}
tpacpi_lifecycle
;
static
int
experimental
;
static
u32
dbg_level
;
/****************************************************************************
****************************************************************************
*
...
...
@@ -137,13 +272,13 @@ static enum {
static
acpi_handle
root_handle
;
#define
IBM
_HANDLE(object, parent, paths...) \
#define
TPACPI
_HANDLE(object, parent, paths...) \
static acpi_handle object##_handle; \
static acpi_handle *object##_parent = &parent##_handle; \
static char *object##_path; \
static char *object##_paths[] = { paths }
IBM
_HANDLE
(
ec
,
root
,
"
\\
_SB.PCI0.ISA.EC0"
,
/* 240, 240x */
TPACPI
_HANDLE
(
ec
,
root
,
"
\\
_SB.PCI0.ISA.EC0"
,
/* 240, 240x */
"
\\
_SB.PCI.ISA.EC"
,
/* 570 */
"
\\
_SB.PCI0.ISA0.EC0"
,
/* 600e/x, 770e, 770x */
"
\\
_SB.PCI0.ISA.EC"
,
/* A21e, A2xm/p, T20-22, X20-21 */
...
...
@@ -152,20 +287,16 @@ IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
"
\\
_SB.PCI0.LPC.EC"
,
/* all others */
);
IBM_HANDLE
(
ecrd
,
ec
,
"ECRD"
);
/* 570 */
IBM_HANDLE
(
ecwr
,
ec
,
"ECWR"
);
/* 570 */
TPACPI_HANDLE
(
ecrd
,
ec
,
"ECRD"
);
/* 570 */
TPACPI_HANDLE
(
ecwr
,
ec
,
"ECWR"
);
/* 570 */
/*************************************************************************
* Misc ACPI handles
*/
IBM_HANDLE
(
cmos
,
root
,
"
\\
UCMS"
,
/* R50, R50e, R50p, R51, T4x, X31, X40 */
TPACPI_HANDLE
(
cmos
,
root
,
"
\\
UCMS"
,
/* R50, R50e, R50p, R51, */
/* T4x, X31, X40 */
"
\\
CMOS"
,
/* A3x, G4x, R32, T23, T30, X22-24, X30 */
"
\\
CMS"
,
/* R40, R40e */
);
/* all others */
IBM
_HANDLE
(
hkey
,
ec
,
"
\\
_SB.HKEY"
,
/* 600e/x, 770e, 770x */
TPACPI
_HANDLE
(
hkey
,
ec
,
"
\\
_SB.HKEY"
,
/* 600e/x, 770e, 770x */
"^HKEY"
,
/* R30, R31 */
"HKEY"
,
/* all others */
);
/* 570 */
...
...
@@ -180,7 +311,7 @@ static int acpi_evalf(acpi_handle handle,
{
char
*
fmt0
=
fmt
;
struct
acpi_object_list
params
;
union
acpi_object
in_objs
[
IBM
_MAX_ACPI_ARGS
];
union
acpi_object
in_objs
[
TPACPI
_MAX_ACPI_ARGS
];
struct
acpi_buffer
result
,
*
resultp
;
union
acpi_object
out_obj
;
acpi_status
status
;
...
...
@@ -190,7 +321,7 @@ static int acpi_evalf(acpi_handle handle,
int
quiet
;
if
(
!*
fmt
)
{
printk
(
IBM
_ERR
"acpi_evalf() called with empty format
\n
"
);
printk
(
TPACPI
_ERR
"acpi_evalf() called with empty format
\n
"
);
return
0
;
}
...
...
@@ -215,7 +346,7 @@ static int acpi_evalf(acpi_handle handle,
break
;
/* add more types as needed */
default:
printk
(
IBM
_ERR
"acpi_evalf() called "
printk
(
TPACPI
_ERR
"acpi_evalf() called "
"with invalid format character '%c'
\n
"
,
c
);
return
0
;
}
...
...
@@ -242,29 +373,19 @@ static int acpi_evalf(acpi_handle handle,
break
;
/* add more types as needed */
default:
printk
(
IBM
_ERR
"acpi_evalf() called "
printk
(
TPACPI
_ERR
"acpi_evalf() called "
"with invalid format character '%c'
\n
"
,
res_type
);
return
0
;
}
if
(
!
success
&&
!
quiet
)
printk
(
IBM
_ERR
"acpi_evalf(%s, %s, ...) failed: %d
\n
"
,
printk
(
TPACPI
_ERR
"acpi_evalf(%s, %s, ...) failed: %d
\n
"
,
method
,
fmt0
,
status
);
return
success
;
}
static
void
__unused
acpi_print_int
(
acpi_handle
handle
,
char
*
method
)
{
int
i
;
if
(
acpi_evalf
(
handle
,
&
i
,
method
,
"d"
))
printk
(
IBM_INFO
"%s = 0x%x
\n
"
,
method
,
i
);
else
printk
(
IBM_ERR
"error calling %s
\n
"
,
method
);
}
static
int
acpi_ec_read
(
int
i
,
u8
*
p
)
static
int
acpi_ec_read
(
int
i
,
u8
*
p
)
{
int
v
;
...
...
@@ -293,6 +414,7 @@ static int acpi_ec_write(int i, u8 v)
return
1
;
}
#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
static
int
_sta
(
acpi_handle
handle
)
{
int
status
;
...
...
@@ -302,6 +424,7 @@ static int _sta(acpi_handle handle)
return
status
;
}
#endif
static
int
issue_thinkpad_cmos_command
(
int
cmos_cmd
)
{
...
...
@@ -318,6 +441,10 @@ static int issue_thinkpad_cmos_command(int cmos_cmd)
* ACPI device model
*/
#define TPACPI_ACPIHANDLE_INIT(object) \
drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
static
void
drv_acpi_handle_init
(
char
*
name
,
acpi_handle
*
handle
,
acpi_handle
parent
,
char
**
paths
,
int
num_paths
,
char
**
path
)
...
...
@@ -372,25 +499,27 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
rc
=
acpi_bus_get_device
(
*
ibm
->
acpi
->
handle
,
&
ibm
->
acpi
->
device
);
if
(
rc
<
0
)
{
printk
(
IBM
_ERR
"acpi_bus_get_device(%s) failed: %d
\n
"
,
printk
(
TPACPI
_ERR
"acpi_bus_get_device(%s) failed: %d
\n
"
,
ibm
->
name
,
rc
);
return
-
ENODEV
;
}
acpi_driver_data
(
ibm
->
acpi
->
device
)
=
ibm
;
sprintf
(
acpi_device_class
(
ibm
->
acpi
->
device
),
"%s/%s"
,
IBM
_ACPI_EVENT_PREFIX
,
TPACPI
_ACPI_EVENT_PREFIX
,
ibm
->
name
);
status
=
acpi_install_notify_handler
(
*
ibm
->
acpi
->
handle
,
ibm
->
acpi
->
type
,
dispatch_acpi_notify
,
ibm
);
if
(
ACPI_FAILURE
(
status
))
{
if
(
status
==
AE_ALREADY_EXISTS
)
{
printk
(
IBM_NOTICE
"another device driver is already handling %s events
\n
"
,
ibm
->
name
);
printk
(
TPACPI_NOTICE
"another device driver is already "
"handling %s events
\n
"
,
ibm
->
name
);
}
else
{
printk
(
IBM_ERR
"acpi_install_notify_handler(%s) failed: %d
\n
"
,
ibm
->
name
,
status
);
printk
(
TPACPI_ERR
"acpi_install_notify_handler(%s) failed: %d
\n
"
,
ibm
->
name
,
status
);
}
return
-
ENODEV
;
}
...
...
@@ -414,18 +543,18 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
ibm
->
acpi
->
driver
=
kzalloc
(
sizeof
(
struct
acpi_driver
),
GFP_KERNEL
);
if
(
!
ibm
->
acpi
->
driver
)
{
printk
(
IBM
_ERR
"kzalloc(ibm->driver) failed
\n
"
);
printk
(
TPACPI
_ERR
"kzalloc(ibm->driver) failed
\n
"
);
return
-
ENOMEM
;
}
sprintf
(
ibm
->
acpi
->
driver
->
name
,
"%s_%s"
,
IBM
_NAME
,
ibm
->
name
);
sprintf
(
ibm
->
acpi
->
driver
->
name
,
"%s_%s"
,
TPACPI
_NAME
,
ibm
->
name
);
ibm
->
acpi
->
driver
->
ids
=
ibm
->
acpi
->
hid
;
ibm
->
acpi
->
driver
->
ops
.
add
=
&
tpacpi_device_add
;
rc
=
acpi_bus_register_driver
(
ibm
->
acpi
->
driver
);
if
(
rc
<
0
)
{
printk
(
IBM
_ERR
"acpi_bus_register_driver(%s) failed: %d
\n
"
,
printk
(
TPACPI
_ERR
"acpi_bus_register_driver(%s) failed: %d
\n
"
,
ibm
->
name
,
rc
);
kfree
(
ibm
->
acpi
->
driver
);
ibm
->
acpi
->
driver
=
NULL
;
...
...
@@ -470,7 +599,7 @@ static int dispatch_procfs_read(char *page, char **start, off_t off,
}
static
int
dispatch_procfs_write
(
struct
file
*
file
,
const
char
__user
*
userbuf
,
const
char
__user
*
userbuf
,
unsigned
long
count
,
void
*
data
)
{
struct
ibm_struct
*
ibm
=
data
;
...
...
@@ -530,7 +659,22 @@ static struct platform_device *tpacpi_sensors_pdev;
static
struct
device
*
tpacpi_hwmon
;
static
struct
input_dev
*
tpacpi_inputdev
;
static
struct
mutex
tpacpi_inputdev_send_mutex
;
static
LIST_HEAD
(
tpacpi_all_drivers
);
static
int
tpacpi_suspend_handler
(
struct
platform_device
*
pdev
,
pm_message_t
state
)
{
struct
ibm_struct
*
ibm
,
*
itmp
;
list_for_each_entry_safe
(
ibm
,
itmp
,
&
tpacpi_all_drivers
,
all_drivers
)
{
if
(
ibm
->
suspend
)
(
ibm
->
suspend
)(
state
);
}
return
0
;
}
static
int
tpacpi_resume_handler
(
struct
platform_device
*
pdev
)
{
...
...
@@ -548,107 +692,36 @@ static int tpacpi_resume_handler(struct platform_device *pdev)
static
struct
platform_driver
tpacpi_pdriver
=
{
.
driver
=
{
.
name
=
IBM
_DRVR_NAME
,
.
name
=
TPACPI
_DRVR_NAME
,
.
owner
=
THIS_MODULE
,
},
.
suspend
=
tpacpi_suspend_handler
,
.
resume
=
tpacpi_resume_handler
,
};
static
struct
platform_driver
tpacpi_hwmon_pdriver
=
{
.
driver
=
{
.
name
=
IBM
_HWMON_DRVR_NAME
,
.
name
=
TPACPI
_HWMON_DRVR_NAME
,
.
owner
=
THIS_MODULE
,
},
};
/*************************************************************************
*
thinkpad-acpi driver attribute
s
*
sysfs support helper
s
*/
/* interface_version --------------------------------------------------- */
static
ssize_t
tpacpi_driver_interface_version_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
TPACPI_SYSFS_VERSION
);
}
static
DRIVER_ATTR
(
interface_version
,
S_IRUGO
,
tpacpi_driver_interface_version_show
,
NULL
);
/* debug_level --------------------------------------------------------- */
static
ssize_t
tpacpi_driver_debug_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
dbg_level
);
}
static
ssize_t
tpacpi_driver_debug_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
0xffff
,
&
t
))
return
-
EINVAL
;
dbg_level
=
t
;
return
count
;
}
static
DRIVER_ATTR
(
debug_level
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_debug_show
,
tpacpi_driver_debug_store
);
/* version ------------------------------------------------------------- */
static
ssize_t
tpacpi_driver_version_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s v%s
\n
"
,
IBM_DESC
,
IBM_VERSION
);
}
static
DRIVER_ATTR
(
version
,
S_IRUGO
,
tpacpi_driver_version_show
,
NULL
);
/* --------------------------------------------------------------------- */
static
struct
driver_attribute
*
tpacpi_driver_attributes
[]
=
{
&
driver_attr_debug_level
,
&
driver_attr_version
,
&
driver_attr_interface_version
,
struct
attribute_set
{
unsigned
int
members
,
max_members
;
struct
attribute_group
group
;
};
static
int
__init
tpacpi_create_driver_attributes
(
struct
device_driver
*
drv
)
{
int
i
,
res
;
i
=
0
;
res
=
0
;
while
(
!
res
&&
i
<
ARRAY_SIZE
(
tpacpi_driver_attributes
))
{
res
=
driver_create_file
(
drv
,
tpacpi_driver_attributes
[
i
]);
i
++
;
}
return
res
;
}
static
void
tpacpi_remove_driver_attributes
(
struct
device_driver
*
drv
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tpacpi_driver_attributes
);
i
++
)
driver_remove_file
(
drv
,
tpacpi_driver_attributes
[
i
]);
}
/*************************************************************************
* sysfs support helpers
*/
struct
attribute_set_obj
{
struct
attribute_set
s
;
struct
attribute
*
a
;
}
__attribute__
((
packed
));
static
struct
attribute_set
*
create_attr_set
(
unsigned
int
max_members
,
const
char
*
name
)
const
char
*
name
)
{
struct
attribute_set_obj
*
sobj
;
...
...
@@ -668,8 +741,11 @@ static struct attribute_set *create_attr_set(unsigned int max_members,
return
&
sobj
->
s
;
}
#define destroy_attr_set(_set) \
kfree(_set);
/* not multi-threaded safe, use it in a single thread per set */
static
int
add_to_attr_set
(
struct
attribute_set
*
s
,
struct
attribute
*
attr
)
static
int
add_to_attr_set
(
struct
attribute_set
*
s
,
struct
attribute
*
attr
)
{
if
(
!
s
||
!
attr
)
return
-
EINVAL
;
...
...
@@ -683,7 +759,7 @@ static int add_to_attr_set(struct attribute_set* s, struct attribute *attr)
return
0
;
}
static
int
add_many_to_attr_set
(
struct
attribute_set
*
s
,
static
int
add_many_to_attr_set
(
struct
attribute_set
*
s
,
struct
attribute
**
attr
,
unsigned
int
count
)
{
...
...
@@ -698,12 +774,15 @@ static int add_many_to_attr_set(struct attribute_set* s,
return
0
;
}
static
void
delete_attr_set
(
struct
attribute_set
*
s
,
struct
kobject
*
kobj
)
static
void
delete_attr_set
(
struct
attribute_set
*
s
,
struct
kobject
*
kobj
)
{
sysfs_remove_group
(
kobj
,
&
s
->
group
);
destroy_attr_set
(
s
);
}
#define register_attr_set_with_sysfs(_attr_set, _kobj) \
sysfs_create_group(_kobj, &_attr_set->group)
static
int
parse_strtoul
(
const
char
*
buf
,
unsigned
long
max
,
unsigned
long
*
value
)
{
...
...
@@ -720,6 +799,84 @@ static int parse_strtoul(const char *buf,
return
0
;
}
/*************************************************************************
* thinkpad-acpi driver attributes
*/
/* interface_version --------------------------------------------------- */
static
ssize_t
tpacpi_driver_interface_version_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
TPACPI_SYSFS_VERSION
);
}
static
DRIVER_ATTR
(
interface_version
,
S_IRUGO
,
tpacpi_driver_interface_version_show
,
NULL
);
/* debug_level --------------------------------------------------------- */
static
ssize_t
tpacpi_driver_debug_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
dbg_level
);
}
static
ssize_t
tpacpi_driver_debug_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
0xffff
,
&
t
))
return
-
EINVAL
;
dbg_level
=
t
;
return
count
;
}
static
DRIVER_ATTR
(
debug_level
,
S_IWUSR
|
S_IRUGO
,
tpacpi_driver_debug_show
,
tpacpi_driver_debug_store
);
/* version ------------------------------------------------------------- */
static
ssize_t
tpacpi_driver_version_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s v%s
\n
"
,
TPACPI_DESC
,
TPACPI_VERSION
);
}
static
DRIVER_ATTR
(
version
,
S_IRUGO
,
tpacpi_driver_version_show
,
NULL
);
/* --------------------------------------------------------------------- */
static
struct
driver_attribute
*
tpacpi_driver_attributes
[]
=
{
&
driver_attr_debug_level
,
&
driver_attr_version
,
&
driver_attr_interface_version
,
};
static
int
__init
tpacpi_create_driver_attributes
(
struct
device_driver
*
drv
)
{
int
i
,
res
;
i
=
0
;
res
=
0
;
while
(
!
res
&&
i
<
ARRAY_SIZE
(
tpacpi_driver_attributes
))
{
res
=
driver_create_file
(
drv
,
tpacpi_driver_attributes
[
i
]);
i
++
;
}
return
res
;
}
static
void
tpacpi_remove_driver_attributes
(
struct
device_driver
*
drv
)
{
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tpacpi_driver_attributes
);
i
++
)
driver_remove_file
(
drv
,
tpacpi_driver_attributes
[
i
]);
}
/****************************************************************************
****************************************************************************
*
...
...
@@ -734,17 +891,17 @@ static int parse_strtoul(const char *buf,
static
int
__init
thinkpad_acpi_driver_init
(
struct
ibm_init_struct
*
iibm
)
{
printk
(
IBM_INFO
"%s v%s
\n
"
,
IBM_DESC
,
IBM
_VERSION
);
printk
(
IBM_INFO
"%s
\n
"
,
IBM
_URL
);
printk
(
TPACPI_INFO
"%s v%s
\n
"
,
TPACPI_DESC
,
TPACPI
_VERSION
);
printk
(
TPACPI_INFO
"%s
\n
"
,
TPACPI
_URL
);
printk
(
IBM
_INFO
"ThinkPad BIOS %s, EC %s
\n
"
,
printk
(
TPACPI
_INFO
"ThinkPad BIOS %s, EC %s
\n
"
,
(
thinkpad_id
.
bios_version_str
)
?
thinkpad_id
.
bios_version_str
:
"unknown"
,
(
thinkpad_id
.
ec_version_str
)
?
thinkpad_id
.
ec_version_str
:
"unknown"
);
if
(
thinkpad_id
.
vendor
&&
thinkpad_id
.
model_str
)
printk
(
IBM
_INFO
"%s %s
\n
"
,
printk
(
TPACPI
_INFO
"%s %s
\n
"
,
(
thinkpad_id
.
vendor
==
PCI_VENDOR_ID_IBM
)
?
"IBM"
:
((
thinkpad_id
.
vendor
==
PCI_VENDOR_ID_LENOVO
)
?
...
...
@@ -758,8 +915,8 @@ static int thinkpad_acpi_driver_read(char *p)
{
int
len
=
0
;
len
+=
sprintf
(
p
+
len
,
"driver:
\t\t
%s
\n
"
,
IBM
_DESC
);
len
+=
sprintf
(
p
+
len
,
"version:
\t
%s
\n
"
,
IBM
_VERSION
);
len
+=
sprintf
(
p
+
len
,
"driver:
\t\t
%s
\n
"
,
TPACPI
_DESC
);
len
+=
sprintf
(
p
+
len
,
"version:
\t
%s
\n
"
,
TPACPI
_VERSION
);
return
len
;
}
...
...
@@ -773,15 +930,129 @@ static struct ibm_struct thinkpad_acpi_driver_data = {
* Hotkey subdriver
*/
enum
{
/* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_FNF1
=
0
,
TP_ACPI_HOTKEYSCAN_FNF2
,
TP_ACPI_HOTKEYSCAN_FNF3
,
TP_ACPI_HOTKEYSCAN_FNF4
,
TP_ACPI_HOTKEYSCAN_FNF5
,
TP_ACPI_HOTKEYSCAN_FNF6
,
TP_ACPI_HOTKEYSCAN_FNF7
,
TP_ACPI_HOTKEYSCAN_FNF8
,
TP_ACPI_HOTKEYSCAN_FNF9
,
TP_ACPI_HOTKEYSCAN_FNF10
,
TP_ACPI_HOTKEYSCAN_FNF11
,
TP_ACPI_HOTKEYSCAN_FNF12
,
TP_ACPI_HOTKEYSCAN_FNBACKSPACE
,
TP_ACPI_HOTKEYSCAN_FNINSERT
,
TP_ACPI_HOTKEYSCAN_FNDELETE
,
TP_ACPI_HOTKEYSCAN_FNHOME
,
TP_ACPI_HOTKEYSCAN_FNEND
,
TP_ACPI_HOTKEYSCAN_FNPAGEUP
,
TP_ACPI_HOTKEYSCAN_FNPAGEDOWN
,
TP_ACPI_HOTKEYSCAN_FNSPACE
,
TP_ACPI_HOTKEYSCAN_VOLUMEUP
,
TP_ACPI_HOTKEYSCAN_VOLUMEDOWN
,
TP_ACPI_HOTKEYSCAN_MUTE
,
TP_ACPI_HOTKEYSCAN_THINKPAD
,
};
enum
{
/* Keys available through NVRAM polling */
TPACPI_HKEY_NVRAM_KNOWN_MASK
=
0x00fb88c0U
,
TPACPI_HKEY_NVRAM_GOOD_MASK
=
0x00fb8000U
,
};
enum
{
/* Positions of some of the keys in hotkey masks */
TP_ACPI_HKEY_DISPSWTCH_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNF7
,
TP_ACPI_HKEY_DISPXPAND_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNF8
,
TP_ACPI_HKEY_HIBERNATE_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNF12
,
TP_ACPI_HKEY_BRGHTUP_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNHOME
,
TP_ACPI_HKEY_BRGHTDWN_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNEND
,
TP_ACPI_HKEY_THNKLGHT_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNPAGEUP
,
TP_ACPI_HKEY_ZOOM_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_FNSPACE
,
TP_ACPI_HKEY_VOLUP_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_VOLUMEUP
,
TP_ACPI_HKEY_VOLDWN_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_VOLUMEDOWN
,
TP_ACPI_HKEY_MUTE_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_MUTE
,
TP_ACPI_HKEY_THINKPAD_MASK
=
1
<<
TP_ACPI_HOTKEYSCAN_THINKPAD
,
};
enum
{
/* NVRAM to ACPI HKEY group map */
TP_NVRAM_HKEY_GROUP_HK2
=
TP_ACPI_HKEY_THINKPAD_MASK
|
TP_ACPI_HKEY_ZOOM_MASK
|
TP_ACPI_HKEY_DISPSWTCH_MASK
|
TP_ACPI_HKEY_HIBERNATE_MASK
,
TP_NVRAM_HKEY_GROUP_BRIGHTNESS
=
TP_ACPI_HKEY_BRGHTUP_MASK
|
TP_ACPI_HKEY_BRGHTDWN_MASK
,
TP_NVRAM_HKEY_GROUP_VOLUME
=
TP_ACPI_HKEY_VOLUP_MASK
|
TP_ACPI_HKEY_VOLDWN_MASK
|
TP_ACPI_HKEY_MUTE_MASK
,
};
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
struct
tp_nvram_state
{
u16
thinkpad_toggle
:
1
;
u16
zoom_toggle
:
1
;
u16
display_toggle
:
1
;
u16
thinklight_toggle
:
1
;
u16
hibernate_toggle
:
1
;
u16
displayexp_toggle
:
1
;
u16
display_state
:
1
;
u16
brightness_toggle
:
1
;
u16
volume_toggle
:
1
;
u16
mute
:
1
;
u8
brightness_level
;
u8
volume_level
;
};
static
struct
task_struct
*
tpacpi_hotkey_task
;
static
u32
hotkey_source_mask
;
/* bit mask 0=ACPI,1=NVRAM */
static
int
hotkey_poll_freq
=
10
;
/* Hz */
static
struct
mutex
hotkey_thread_mutex
;
static
struct
mutex
hotkey_thread_data_mutex
;
static
unsigned
int
hotkey_config_change
;
#else
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
#define hotkey_source_mask 0U
#endif
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
static
struct
mutex
hotkey_mutex
;
static
enum
{
/* Reasons for waking up */
TP_ACPI_WAKEUP_NONE
=
0
,
/* None or unknown */
TP_ACPI_WAKEUP_BAYEJ
,
/* Bay ejection request */
TP_ACPI_WAKEUP_UNDOCK
,
/* Undock request */
}
hotkey_wakeup_reason
;
static
int
hotkey_autosleep_ack
;
static
int
hotkey_orig_status
;
static
u32
hotkey_orig_mask
;
static
u32
hotkey_all_mask
;
static
u32
hotkey_reserved_mask
;
static
u32
hotkey_mask
;
static
unsigned
int
hotkey_report_mode
;
static
u16
*
hotkey_keycode_map
;
static
struct
attribute_set
*
hotkey_dev_attributes
;
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
#define HOTKEY_CONFIG_CRITICAL_START \
do { \
mutex_lock(&hotkey_thread_data_mutex); \
hotkey_config_change++; \
} while (0);
#define HOTKEY_CONFIG_CRITICAL_END \
mutex_unlock(&hotkey_thread_data_mutex);
#else
#define HOTKEY_CONFIG_CRITICAL_START
#define HOTKEY_CONFIG_CRITICAL_END
#endif
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
static
int
hotkey_get_wlsw
(
int
*
status
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
status
,
"WLSW"
,
"d"
))
...
...
@@ -789,15 +1060,400 @@ static int hotkey_get_wlsw(int *status)
return
0
;
}
/*
sysfs hotkey enable ------------------------------------------------- */
static
ssize_t
hotkey_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
/*
* Call with hotkey_mutex held
*/
static
int
hotkey_mask_get
(
void
)
{
int
res
,
status
;
u32
m
=
0
;
if
(
tp_features
.
hotkey_mask
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
&
m
,
"DHKN"
,
"d"
))
return
-
EIO
;
}
hotkey_mask
=
m
|
(
hotkey_source_mask
&
hotkey_mask
);
return
0
;
}
/*
* Call with hotkey_mutex held
*/
static
int
hotkey_mask_set
(
u32
mask
)
{
int
i
;
int
rc
=
0
;
if
(
tp_features
.
hotkey_mask
)
{
HOTKEY_CONFIG_CRITICAL_START
for
(
i
=
0
;
i
<
32
;
i
++
)
{
u32
m
=
1
<<
i
;
/* enable in firmware mask only keys not in NVRAM
* mode, but enable the key in the cached hotkey_mask
* regardless of mode, or the key will end up
* disabled by hotkey_mask_get() */
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"MHKM"
,
"vdd"
,
i
+
1
,
!!
((
mask
&
~
hotkey_source_mask
)
&
m
)))
{
rc
=
-
EIO
;
break
;
}
else
{
hotkey_mask
=
(
hotkey_mask
&
~
m
)
|
(
mask
&
m
);
}
}
HOTKEY_CONFIG_CRITICAL_END
/* hotkey_mask_get must be called unconditionally below */
if
(
!
hotkey_mask_get
()
&&
!
rc
&&
(
hotkey_mask
&
~
hotkey_source_mask
)
!=
(
mask
&
~
hotkey_source_mask
))
{
printk
(
TPACPI_NOTICE
"requested hot key mask 0x%08x, but "
"firmware forced it to 0x%08x
\n
"
,
mask
,
hotkey_mask
);
}
}
else
{
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
HOTKEY_CONFIG_CRITICAL_START
hotkey_mask
=
mask
&
hotkey_source_mask
;
HOTKEY_CONFIG_CRITICAL_END
hotkey_mask_get
();
if
(
hotkey_mask
!=
mask
)
{
printk
(
TPACPI_NOTICE
"requested hot key mask 0x%08x, "
"forced to 0x%08x (NVRAM poll mask is "
"0x%08x): no firmware mask support
\n
"
,
mask
,
hotkey_mask
,
hotkey_source_mask
);
}
#else
hotkey_mask_get
();
rc
=
-
ENXIO
;
#endif
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
}
return
rc
;
}
static
int
hotkey_status_get
(
int
*
status
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
status
,
"DHKC"
,
"d"
))
return
-
EIO
;
return
0
;
}
static
int
hotkey_status_set
(
int
status
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"MHKC"
,
"vd"
,
status
))
return
-
EIO
;
return
0
;
}
static
void
tpacpi_input_send_radiosw
(
void
)
{
int
wlsw
;
mutex_lock
(
&
tpacpi_inputdev_send_mutex
);
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
wlsw
))
{
input_report_switch
(
tpacpi_inputdev
,
SW_RADIO
,
!!
wlsw
);
input_sync
(
tpacpi_inputdev
);
}
mutex_unlock
(
&
tpacpi_inputdev_send_mutex
);
}
static
void
tpacpi_input_send_key
(
unsigned
int
scancode
)
{
unsigned
int
keycode
;
keycode
=
hotkey_keycode_map
[
scancode
];
if
(
keycode
!=
KEY_RESERVED
)
{
mutex_lock
(
&
tpacpi_inputdev_send_mutex
);
input_report_key
(
tpacpi_inputdev
,
keycode
,
1
);
if
(
keycode
==
KEY_UNKNOWN
)
input_event
(
tpacpi_inputdev
,
EV_MSC
,
MSC_SCAN
,
scancode
);
input_sync
(
tpacpi_inputdev
);
input_report_key
(
tpacpi_inputdev
,
keycode
,
0
);
if
(
keycode
==
KEY_UNKNOWN
)
input_event
(
tpacpi_inputdev
,
EV_MSC
,
MSC_SCAN
,
scancode
);
input_sync
(
tpacpi_inputdev
);
mutex_unlock
(
&
tpacpi_inputdev_send_mutex
);
}
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
static
struct
tp_acpi_drv_struct
ibm_hotkey_acpidriver
;
static
void
tpacpi_hotkey_send_key
(
unsigned
int
scancode
)
{
tpacpi_input_send_key
(
scancode
);
if
(
hotkey_report_mode
<
2
)
{
acpi_bus_generate_proc_event
(
ibm_hotkey_acpidriver
.
device
,
0x80
,
0x1001
+
scancode
);
}
}
static
void
hotkey_read_nvram
(
struct
tp_nvram_state
*
n
,
u32
m
)
{
u8
d
;
if
(
m
&
TP_NVRAM_HKEY_GROUP_HK2
)
{
d
=
nvram_read_byte
(
TP_NVRAM_ADDR_HK2
);
n
->
thinkpad_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_THINKPAD
);
n
->
zoom_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_ZOOM
);
n
->
display_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_DISPLAY
);
n
->
hibernate_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_HIBERNATE
);
}
if
(
m
&
TP_ACPI_HKEY_THNKLGHT_MASK
)
{
d
=
nvram_read_byte
(
TP_NVRAM_ADDR_THINKLIGHT
);
n
->
thinklight_toggle
=
!!
(
d
&
TP_NVRAM_MASK_THINKLIGHT
);
}
if
(
m
&
TP_ACPI_HKEY_DISPXPAND_MASK
)
{
d
=
nvram_read_byte
(
TP_NVRAM_ADDR_VIDEO
);
n
->
displayexp_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_DISPEXPND
);
}
if
(
m
&
TP_NVRAM_HKEY_GROUP_BRIGHTNESS
)
{
d
=
nvram_read_byte
(
TP_NVRAM_ADDR_BRIGHTNESS
);
n
->
brightness_level
=
(
d
&
TP_NVRAM_MASK_LEVEL_BRIGHTNESS
)
>>
TP_NVRAM_POS_LEVEL_BRIGHTNESS
;
n
->
brightness_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_BRIGHTNESS
);
}
if
(
m
&
TP_NVRAM_HKEY_GROUP_VOLUME
)
{
d
=
nvram_read_byte
(
TP_NVRAM_ADDR_MIXER
);
n
->
volume_level
=
(
d
&
TP_NVRAM_MASK_LEVEL_VOLUME
)
>>
TP_NVRAM_POS_LEVEL_VOLUME
;
n
->
mute
=
!!
(
d
&
TP_NVRAM_MASK_MUTE
);
n
->
volume_toggle
=
!!
(
d
&
TP_NVRAM_MASK_HKT_VOLUME
);
}
}
#define TPACPI_COMPARE_KEY(__scancode, __member) \
do { \
if ((mask & (1 << __scancode)) && \
oldn->__member != newn->__member) \
tpacpi_hotkey_send_key(__scancode); \
} while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \
do { if (mask & (1 << __scancode)) \
tpacpi_hotkey_send_key(__scancode); } while (0)
static
void
hotkey_compare_and_issue_event
(
struct
tp_nvram_state
*
oldn
,
struct
tp_nvram_state
*
newn
,
u32
mask
)
{
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_THINKPAD
,
thinkpad_toggle
);
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_FNSPACE
,
zoom_toggle
);
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_FNF7
,
display_toggle
);
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_FNF12
,
hibernate_toggle
);
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_FNPAGEUP
,
thinklight_toggle
);
TPACPI_COMPARE_KEY
(
TP_ACPI_HOTKEYSCAN_FNF8
,
displayexp_toggle
);
/* handle volume */
if
(
oldn
->
volume_toggle
!=
newn
->
volume_toggle
)
{
if
(
oldn
->
mute
!=
newn
->
mute
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_MUTE
);
}
if
(
oldn
->
volume_level
>
newn
->
volume_level
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_VOLUMEDOWN
);
}
else
if
(
oldn
->
volume_level
<
newn
->
volume_level
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_VOLUMEUP
);
}
else
if
(
oldn
->
mute
==
newn
->
mute
)
{
/* repeated key presses that didn't change state */
if
(
newn
->
mute
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_MUTE
);
}
else
if
(
newn
->
volume_level
!=
0
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_VOLUMEUP
);
}
else
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_VOLUMEDOWN
);
}
}
}
/* handle brightness */
if
(
oldn
->
brightness_toggle
!=
newn
->
brightness_toggle
)
{
if
(
oldn
->
brightness_level
<
newn
->
brightness_level
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_FNHOME
);
}
else
if
(
oldn
->
brightness_level
>
newn
->
brightness_level
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_FNEND
);
}
else
{
/* repeated key presses that didn't change state */
if
(
newn
->
brightness_level
!=
0
)
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_FNHOME
);
}
else
{
TPACPI_MAY_SEND_KEY
(
TP_ACPI_HOTKEYSCAN_FNEND
);
}
}
}
}
#undef TPACPI_COMPARE_KEY
#undef TPACPI_MAY_SEND_KEY
static
int
hotkey_kthread
(
void
*
data
)
{
struct
tp_nvram_state
s
[
2
];
u32
mask
;
unsigned
int
si
,
so
;
unsigned
long
t
;
unsigned
int
change_detector
,
must_reset
;
mutex_lock
(
&
hotkey_thread_mutex
);
if
(
tpacpi_lifecycle
==
TPACPI_LIFE_EXITING
)
goto
exit
;
set_freezable
();
so
=
0
;
si
=
1
;
t
=
0
;
/* Initial state for compares */
mutex_lock
(
&
hotkey_thread_data_mutex
);
change_detector
=
hotkey_config_change
;
mask
=
hotkey_source_mask
&
hotkey_mask
;
mutex_unlock
(
&
hotkey_thread_data_mutex
);
hotkey_read_nvram
(
&
s
[
so
],
mask
);
while
(
!
kthread_should_stop
()
&&
hotkey_poll_freq
)
{
if
(
t
==
0
)
t
=
1000
/
hotkey_poll_freq
;
t
=
msleep_interruptible
(
t
);
if
(
unlikely
(
kthread_should_stop
()))
break
;
must_reset
=
try_to_freeze
();
if
(
t
>
0
&&
!
must_reset
)
continue
;
mutex_lock
(
&
hotkey_thread_data_mutex
);
if
(
must_reset
||
hotkey_config_change
!=
change_detector
)
{
/* forget old state on thaw or config change */
si
=
so
;
t
=
0
;
change_detector
=
hotkey_config_change
;
}
mask
=
hotkey_source_mask
&
hotkey_mask
;
mutex_unlock
(
&
hotkey_thread_data_mutex
);
if
(
likely
(
mask
))
{
hotkey_read_nvram
(
&
s
[
si
],
mask
);
if
(
likely
(
si
!=
so
))
{
hotkey_compare_and_issue_event
(
&
s
[
so
],
&
s
[
si
],
mask
);
}
}
so
=
si
;
si
^=
1
;
}
exit:
mutex_unlock
(
&
hotkey_thread_mutex
);
return
0
;
}
static
void
hotkey_poll_stop_sync
(
void
)
{
if
(
tpacpi_hotkey_task
)
{
if
(
frozen
(
tpacpi_hotkey_task
)
||
freezing
(
tpacpi_hotkey_task
))
thaw_process
(
tpacpi_hotkey_task
);
kthread_stop
(
tpacpi_hotkey_task
);
tpacpi_hotkey_task
=
NULL
;
mutex_lock
(
&
hotkey_thread_mutex
);
/* at this point, the thread did exit */
mutex_unlock
(
&
hotkey_thread_mutex
);
}
}
/* call with hotkey_mutex held */
static
void
hotkey_poll_setup
(
int
may_warn
)
{
if
((
hotkey_source_mask
&
hotkey_mask
)
!=
0
&&
hotkey_poll_freq
>
0
&&
(
tpacpi_inputdev
->
users
>
0
||
hotkey_report_mode
<
2
))
{
if
(
!
tpacpi_hotkey_task
)
{
tpacpi_hotkey_task
=
kthread_run
(
hotkey_kthread
,
NULL
,
TPACPI_FILE
"d"
);
if
(
IS_ERR
(
tpacpi_hotkey_task
))
{
tpacpi_hotkey_task
=
NULL
;
printk
(
TPACPI_ERR
"could not create kernel thread "
"for hotkey polling
\n
"
);
}
}
}
else
{
hotkey_poll_stop_sync
();
if
(
may_warn
&&
hotkey_source_mask
!=
0
&&
hotkey_poll_freq
==
0
)
{
printk
(
TPACPI_NOTICE
"hot keys 0x%08x require polling, "
"which is currently disabled
\n
"
,
hotkey_source_mask
);
}
}
}
static
void
hotkey_poll_setup_safe
(
int
may_warn
)
{
mutex_lock
(
&
hotkey_mutex
);
hotkey_poll_setup
(
may_warn
);
mutex_unlock
(
&
hotkey_mutex
);
}
static
int
hotkey_inputdev_open
(
struct
input_dev
*
dev
)
{
switch
(
tpacpi_lifecycle
)
{
case
TPACPI_LIFE_INIT
:
/*
* hotkey_init will call hotkey_poll_setup_safe
* at the appropriate moment
*/
return
0
;
case
TPACPI_LIFE_EXITING
:
return
-
EBUSY
;
case
TPACPI_LIFE_RUNNING
:
hotkey_poll_setup_safe
(
0
);
return
0
;
}
/* Should only happen if tpacpi_lifecycle is corrupt */
BUG
();
return
-
EBUSY
;
}
static
void
hotkey_inputdev_close
(
struct
input_dev
*
dev
)
{
/* disable hotkey polling when possible */
if
(
tpacpi_lifecycle
==
TPACPI_LIFE_RUNNING
)
hotkey_poll_setup_safe
(
0
);
}
#endif
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
/* sysfs hotkey enable ------------------------------------------------- */
static
ssize_t
hotkey_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
res
,
status
;
res
=
hotkey_
get
(
&
status
,
&
mask
);
res
=
hotkey_
status_get
(
&
status
);
if
(
res
)
return
res
;
...
...
@@ -809,15 +1465,12 @@ static ssize_t hotkey_enable_store(struct device *dev,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
int
res
,
status
;
u32
mask
;
int
res
;
if
(
parse_strtoul
(
buf
,
1
,
&
t
))
return
-
EINVAL
;
res
=
hotkey_get
(
&
status
,
&
mask
);
if
(
!
res
)
res
=
hotkey_set
(
t
,
mask
);
res
=
hotkey_status_set
(
t
);
return
(
res
)
?
res
:
count
;
}
...
...
@@ -831,14 +1484,15 @@ static ssize_t hotkey_mask_show(struct device *dev,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
res
,
status
;
u32
mask
;
int
res
;
res
=
hotkey_get
(
&
status
,
&
mask
);
if
(
res
)
return
res
;
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_mask_get
();
mutex_unlock
(
&
hotkey_mutex
);
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
mask
);
return
(
res
)
?
res
:
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
hotkey_mask
);
}
static
ssize_t
hotkey_mask_store
(
struct
device
*
dev
,
...
...
@@ -846,15 +1500,21 @@ static ssize_t hotkey_mask_store(struct device *dev,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
int
res
,
status
;
u32
mask
;
int
res
;
if
(
parse_strtoul
(
buf
,
0xffffffffUL
,
&
t
))
return
-
EINVAL
;
res
=
hotkey_get
(
&
status
,
&
mask
);
if
(
!
res
)
hotkey_set
(
status
,
t
);
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_mask_set
(
t
);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_setup
(
1
);
#endif
mutex_unlock
(
&
hotkey_mutex
);
return
(
res
)
?
res
:
count
;
}
...
...
@@ -890,7 +1550,8 @@ static ssize_t hotkey_all_mask_show(struct device *dev,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
hotkey_all_mask
);
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
hotkey_all_mask
|
hotkey_source_mask
);
}
static
struct
device_attribute
dev_attr_hotkey_all_mask
=
...
...
@@ -902,14 +1563,87 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
hotkey_all_mask
&
~
hotkey_reserved_mask
);
(
hotkey_all_mask
|
hotkey_source_mask
)
&
~
hotkey_reserved_mask
);
}
static
struct
device_attribute
dev_attr_hotkey_recommended_mask
=
__ATTR
(
hotkey_recommended_mask
,
S_IRUGO
,
hotkey_recommended_mask_show
,
NULL
);
/* sysfs hotkey radio_sw ----------------------------------------------- */
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
/* sysfs hotkey hotkey_source_mask ------------------------------------- */
static
ssize_t
hotkey_source_mask_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%08x
\n
"
,
hotkey_source_mask
);
}
static
ssize_t
hotkey_source_mask_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
0xffffffffUL
,
&
t
)
||
((
t
&
~
TPACPI_HKEY_NVRAM_KNOWN_MASK
)
!=
0
))
return
-
EINVAL
;
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
HOTKEY_CONFIG_CRITICAL_START
hotkey_source_mask
=
t
;
HOTKEY_CONFIG_CRITICAL_END
hotkey_poll_setup
(
1
);
mutex_unlock
(
&
hotkey_mutex
);
return
count
;
}
static
struct
device_attribute
dev_attr_hotkey_source_mask
=
__ATTR
(
hotkey_source_mask
,
S_IWUSR
|
S_IRUGO
,
hotkey_source_mask_show
,
hotkey_source_mask_store
);
/* sysfs hotkey hotkey_poll_freq --------------------------------------- */
static
ssize_t
hotkey_poll_freq_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
hotkey_poll_freq
);
}
static
ssize_t
hotkey_poll_freq_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
25
,
&
t
))
return
-
EINVAL
;
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
hotkey_poll_freq
=
t
;
hotkey_poll_setup
(
1
);
mutex_unlock
(
&
hotkey_mutex
);
return
count
;
}
static
struct
device_attribute
dev_attr_hotkey_poll_freq
=
__ATTR
(
hotkey_poll_freq
,
S_IWUSR
|
S_IRUGO
,
hotkey_poll_freq_show
,
hotkey_poll_freq_store
);
#endif
/* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
static
ssize_t
hotkey_radio_sw_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
...
...
@@ -925,6 +1659,13 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
static
struct
device_attribute
dev_attr_hotkey_radio_sw
=
__ATTR
(
hotkey_radio_sw
,
S_IRUGO
,
hotkey_radio_sw_show
,
NULL
);
static
void
hotkey_radio_sw_notify_change
(
void
)
{
if
(
tp_features
.
hotkey_wlsw
)
sysfs_notify
(
&
tpacpi_pdev
->
dev
.
kobj
,
NULL
,
"hotkey_radio_sw"
);
}
/* sysfs hotkey report_mode -------------------------------------------- */
static
ssize_t
hotkey_report_mode_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
...
...
@@ -937,43 +1678,132 @@ static ssize_t hotkey_report_mode_show(struct device *dev,
static
struct
device_attribute
dev_attr_hotkey_report_mode
=
__ATTR
(
hotkey_report_mode
,
S_IRUGO
,
hotkey_report_mode_show
,
NULL
);
/* --------------------------------------------------------------------- */
/* sysfs wakeup reason (pollable) -------------------------------------- */
static
ssize_t
hotkey_wakeup_reason_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
hotkey_wakeup_reason
);
}
static
struct
attribute
*
hotkey_attributes
[]
__initdata
=
{
&
dev_attr_hotkey_enable
.
attr
,
&
dev_attr_hotkey_report_mode
.
attr
,
};
static
struct
device_attribute
dev_attr_hotkey_wakeup_reason
=
__ATTR
(
wakeup_reason
,
S_IRUGO
,
hotkey_wakeup_reason_show
,
NULL
);
static
struct
attribute
*
hotkey_mask_attributes
[]
__initdata
=
{
&
dev_attr_hotkey_mask
.
attr
,
void
hotkey_wakeup_reason_notify_change
(
void
)
{
if
(
tp_features
.
hotkey_mask
)
sysfs_notify
(
&
tpacpi_pdev
->
dev
.
kobj
,
NULL
,
"wakeup_reason"
);
}
/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
static
ssize_t
hotkey_wakeup_hotunplug_complete_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
hotkey_autosleep_ack
);
}
static
struct
device_attribute
dev_attr_hotkey_wakeup_hotunplug_complete
=
__ATTR
(
wakeup_hotunplug_complete
,
S_IRUGO
,
hotkey_wakeup_hotunplug_complete_show
,
NULL
);
void
hotkey_wakeup_hotunplug_complete_notify_change
(
void
)
{
if
(
tp_features
.
hotkey_mask
)
sysfs_notify
(
&
tpacpi_pdev
->
dev
.
kobj
,
NULL
,
"wakeup_hotunplug_complete"
);
}
/* --------------------------------------------------------------------- */
static
struct
attribute
*
hotkey_attributes
[]
__initdata
=
{
&
dev_attr_hotkey_enable
.
attr
,
&
dev_attr_hotkey_bios_enabled
.
attr
,
&
dev_attr_hotkey_report_mode
.
attr
,
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
&
dev_attr_hotkey_mask
.
attr
,
&
dev_attr_hotkey_all_mask
.
attr
,
&
dev_attr_hotkey_recommended_mask
.
attr
,
&
dev_attr_hotkey_source_mask
.
attr
,
&
dev_attr_hotkey_poll_freq
.
attr
,
#endif
};
static
struct
attribute
*
hotkey_mask_attributes
[]
__initdata
=
{
&
dev_attr_hotkey_bios_mask
.
attr
,
#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
&
dev_attr_hotkey_mask
.
attr
,
&
dev_attr_hotkey_all_mask
.
attr
,
&
dev_attr_hotkey_recommended_mask
.
attr
,
#endif
&
dev_attr_hotkey_wakeup_reason
.
attr
,
&
dev_attr_hotkey_wakeup_hotunplug_complete
.
attr
,
};
static
int
__init
hotkey_init
(
struct
ibm_init_struct
*
iibm
)
{
/* Requirements for changing the default keymaps:
*
* 1. Many of the keys are mapped to KEY_RESERVED for very
* good reasons. Do not change them unless you have deep
* knowledge on the IBM and Lenovo ThinkPad firmware for
* the various ThinkPad models. The driver behaves
* differently for KEY_RESERVED: such keys have their
* hot key mask *unset* in mask_recommended, and also
* in the initial hot key mask programmed into the
* firmware at driver load time, which means the firm-
* ware may react very differently if you change them to
* something else;
*
* 2. You must be subscribed to the linux-thinkpad and
* ibm-acpi-devel mailing lists, and you should read the
* list archives since 2007 if you want to change the
* keymaps. This requirement exists so that you will
* know the past history of problems with the thinkpad-
* acpi driver keymaps, and also that you will be
* listening to any bug reports;
*
* 3. Do not send thinkpad-acpi specific patches directly to
* for merging, *ever*. Send them to the linux-acpi
* mailinglist for comments. Merging is to be done only
* through acpi-test and the ACPI maintainer.
*
* If the above is too much to ask, don't change the keymap.
* Ask the thinkpad-acpi maintainer to do it, instead.
*/
static
u16
ibm_keycode_map
[]
__initdata
=
{
/* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
KEY_FN_F1
,
KEY_FN_F2
,
KEY_COFFEE
,
KEY_SLEEP
,
KEY_WLAN
,
KEY_FN_F6
,
KEY_SWITCHVIDEOMODE
,
KEY_FN_F8
,
KEY_FN_F9
,
KEY_FN_F10
,
KEY_FN_F11
,
KEY_SUSPEND
,
/* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
/* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
KEY_UNKNOWN
,
/* 0x0C: FN+BACKSPACE */
KEY_UNKNOWN
,
/* 0x0D: FN+INSERT */
KEY_UNKNOWN
,
/* 0x0E: FN+DELETE */
/* brightness: firmware always reacts to them, unless
* X.org did some tricks in the radeon BIOS scratch
* registers of *some* models */
KEY_RESERVED
,
/* 0x0F: FN+HOME (brightness up) */
/* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
KEY_RESERVED
,
/* 0x10: FN+END (brightness down) */
/* Thinklight: firmware always react to it */
KEY_RESERVED
,
/* 0x11: FN+PGUP (thinklight toggle) */
KEY_UNKNOWN
,
/* 0x12: FN+PGDOWN */
KEY_ZOOM
,
/* 0x13: FN+SPACE (zoom) */
/* Volume: firmware always react to it and reprograms
* the built-in *extra* mixer. Never map it to control
* another mixer by default. */
KEY_RESERVED
,
/* 0x14: VOLUME UP */
KEY_RESERVED
,
/* 0x15: VOLUME DOWN */
KEY_RESERVED
,
/* 0x16: MUTE */
KEY_VENDOR
,
/* 0x17: Thinkpad/AccessIBM/Lenovo */
/* (assignments unknown, please report if found) */
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
...
...
@@ -983,20 +1813,37 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_FN_F1
,
KEY_COFFEE
,
KEY_BATTERY
,
KEY_SLEEP
,
KEY_WLAN
,
KEY_FN_F6
,
KEY_SWITCHVIDEOMODE
,
KEY_FN_F8
,
KEY_FN_F9
,
KEY_FN_F10
,
KEY_FN_F11
,
KEY_SUSPEND
,
/* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
/* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
KEY_UNKNOWN
,
/* 0x0C: FN+BACKSPACE */
KEY_UNKNOWN
,
/* 0x0D: FN+INSERT */
KEY_UNKNOWN
,
/* 0x0E: FN+DELETE */
KEY_RESERVED
,
/* 0x0F: FN+HOME (brightness up) */
/* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
KEY_RESERVED
,
/* 0x10: FN+END (brightness down) */
KEY_RESERVED
,
/* 0x11: FN+PGUP (thinklight toggle) */
KEY_UNKNOWN
,
/* 0x12: FN+PGDOWN */
KEY_ZOOM
,
/* 0x13: FN+SPACE (zoom) */
/* Volume: z60/z61, T60 (BIOS version?): firmware always
* react to it and reprograms the built-in *extra* mixer.
* Never map it to control another mixer by default.
*
* T60?, T61, R60?, R61: firmware and EC tries to send
* these over the regular keyboard, so these are no-ops,
* but there are still weird bugs re. MUTE, so do not
* change unless you get test reports from all Lenovo
* models. May cause the BIOS to interfere with the
* HDA mixer.
*/
KEY_RESERVED
,
/* 0x14: VOLUME UP */
KEY_RESERVED
,
/* 0x15: VOLUME DOWN */
KEY_RESERVED
,
/* 0x16: MUTE */
KEY_VENDOR
,
/* 0x17: Thinkpad/AccessIBM/Lenovo */
/* (assignments unknown, please report if found) */
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
...
...
@@ -1013,10 +1860,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing hotkey subdriver
\n
"
);
BUG_ON
(
!
tpacpi_inputdev
);
BUG_ON
(
tpacpi_inputdev
->
open
!=
NULL
||
tpacpi_inputdev
->
close
!=
NULL
);
IBM
_ACPIHANDLE_INIT
(
hkey
);
TPACPI
_ACPIHANDLE_INIT
(
hkey
);
mutex_init
(
&
hotkey_mutex
);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
mutex_init
(
&
hotkey_thread_mutex
);
mutex_init
(
&
hotkey_thread_data_mutex
);
#endif
/* hotkey not supported on 570 */
tp_features
.
hotkey
=
hkey_handle
!=
NULL
;
...
...
@@ -1024,7 +1878,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
str_supported
(
tp_features
.
hotkey
));
if
(
tp_features
.
hotkey
)
{
hotkey_dev_attributes
=
create_attr_set
(
8
,
NULL
);
hotkey_dev_attributes
=
create_attr_set
(
12
,
NULL
);
if
(
!
hotkey_dev_attributes
)
return
-
ENOMEM
;
res
=
add_many_to_attr_set
(
hotkey_dev_attributes
,
...
...
@@ -1038,15 +1892,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
for HKEY interface version 0x100 */
if
(
acpi_evalf
(
hkey_handle
,
&
hkeyv
,
"MHKV"
,
"qd"
))
{
if
((
hkeyv
>>
8
)
!=
1
)
{
printk
(
IBM
_ERR
"unknown version of the "
printk
(
TPACPI
_ERR
"unknown version of the "
"HKEY interface: 0x%x
\n
"
,
hkeyv
);
printk
(
IBM
_ERR
"please report this to %s
\n
"
,
IBM
_MAIL
);
printk
(
TPACPI
_ERR
"please report this to %s
\n
"
,
TPACPI
_MAIL
);
}
else
{
/*
* MHKV 0x100 in A31, R40, R40e,
* T4x, X31, and later
*
*
/
*/
tp_features
.
hotkey_mask
=
1
;
}
}
...
...
@@ -1057,25 +1911,46 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if
(
tp_features
.
hotkey_mask
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
&
hotkey_all_mask
,
"MHKA"
,
"qd"
))
{
printk
(
IBM
_ERR
printk
(
TPACPI
_ERR
"missing MHKA handler, "
"please report this to %s
\n
"
,
IBM_MAIL
);
hotkey_all_mask
=
0x080cU
;
/* FN+F12, FN+F4, FN+F3 */
TPACPI_MAIL
);
/* FN+F12, FN+F4, FN+F3 */
hotkey_all_mask
=
0x080cU
;
}
}
res
=
hotkey_get
(
&
hotkey_orig_status
,
&
hotkey_orig_mask
);
/* hotkey_source_mask *must* be zero for
* the first hotkey_mask_get */
res
=
hotkey_status_get
(
&
hotkey_orig_status
);
if
(
!
res
&&
tp_features
.
hotkey_mask
)
{
res
=
add_many_to_attr_set
(
hotkey_dev_attributes
,
hotkey_mask_attributes
,
ARRAY_SIZE
(
hotkey_mask_attributes
));
res
=
hotkey_mask_get
();
hotkey_orig_mask
=
hotkey_mask
;
if
(
!
res
)
{
res
=
add_many_to_attr_set
(
hotkey_dev_attributes
,
hotkey_mask_attributes
,
ARRAY_SIZE
(
hotkey_mask_attributes
));
}
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
if
(
tp_features
.
hotkey_mask
)
{
hotkey_source_mask
=
TPACPI_HKEY_NVRAM_GOOD_MASK
&
~
hotkey_all_mask
;
}
else
{
hotkey_source_mask
=
TPACPI_HKEY_NVRAM_GOOD_MASK
;
}
vdbg_printk
(
TPACPI_DBG_INIT
,
"hotkey source mask 0x%08x, polling freq %d
\n
"
,
hotkey_source_mask
,
hotkey_poll_freq
);
#endif
/* Not all thinkpads have a hardware radio switch */
if
(
!
res
&&
acpi_evalf
(
hkey_handle
,
&
status
,
"WLSW"
,
"qd"
))
{
tp_features
.
hotkey_wlsw
=
1
;
printk
(
IBM
_INFO
printk
(
TPACPI
_INFO
"radio switch found; radios are %s
\n
"
,
enabled
(
status
,
0
));
res
=
add_to_attr_set
(
hotkey_dev_attributes
,
...
...
@@ -1094,7 +1969,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
hotkey_keycode_map
=
kmalloc
(
TPACPI_HOTKEY_MAP_SIZE
,
GFP_KERNEL
);
if
(
!
hotkey_keycode_map
)
{
printk
(
IBM_ERR
"failed to allocate memory for key map
\n
"
);
printk
(
TPACPI_ERR
"failed to allocate memory for key map
\n
"
);
return
-
ENOMEM
;
}
...
...
@@ -1133,15 +2009,26 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
dbg_printk
(
TPACPI_DBG_INIT
,
"enabling hot key handling
\n
"
);
res
=
hotkey_set
(
1
,
(
hotkey_all_mask
&
~
hotkey_reserved_mask
)
|
hotkey_orig_mask
);
res
=
hotkey_status_set
(
1
);
if
(
res
)
return
res
;
res
=
hotkey_mask_set
(((
hotkey_all_mask
|
hotkey_source_mask
)
&
~
hotkey_reserved_mask
)
|
hotkey_orig_mask
);
if
(
res
<
0
&&
res
!=
-
ENXIO
)
return
res
;
dbg_printk
(
TPACPI_DBG_INIT
,
"legacy hot key reporting over procfs %s
\n
"
,
(
hotkey_report_mode
<
2
)
?
"enabled"
:
"disabled"
);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
tpacpi_inputdev
->
open
=
&
hotkey_inputdev_open
;
tpacpi_inputdev
->
close
=
&
hotkey_inputdev_close
;
hotkey_poll_setup_safe
(
1
);
#endif
}
return
(
tp_features
.
hotkey
)
?
0
:
1
;
...
...
@@ -1149,13 +2036,19 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
static
void
hotkey_exit
(
void
)
{
int
res
;
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_stop_sync
();
#endif
if
(
tp_features
.
hotkey
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"restoring original hotkey mask
\n
"
);
res
=
hotkey_set
(
hotkey_orig_status
,
hotkey_orig_mask
);
if
(
res
)
printk
(
IBM_ERR
"failed to restore hotkey to BIOS defaults
\n
"
);
dbg_printk
(
TPACPI_DBG_EXIT
,
"restoring original hot key mask
\n
"
);
/* no short-circuit boolean operator below! */
if
((
hotkey_mask_set
(
hotkey_orig_mask
)
|
hotkey_status_set
(
hotkey_orig_status
))
!=
0
)
printk
(
TPACPI_ERR
"failed to restore hot key mask "
"to BIOS defaults
\n
"
);
}
if
(
hotkey_dev_attributes
)
{
...
...
@@ -1164,62 +2057,28 @@ static void hotkey_exit(void)
}
}
static
void
tpacpi_input_send_key
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
if
(
keycode
!=
KEY_RESERVED
)
{
mutex_lock
(
&
tpacpi_inputdev_send_mutex
);
input_report_key
(
tpacpi_inputdev
,
keycode
,
1
);
if
(
keycode
==
KEY_UNKNOWN
)
input_event
(
tpacpi_inputdev
,
EV_MSC
,
MSC_SCAN
,
scancode
);
input_sync
(
tpacpi_inputdev
);
input_report_key
(
tpacpi_inputdev
,
keycode
,
0
);
if
(
keycode
==
KEY_UNKNOWN
)
input_event
(
tpacpi_inputdev
,
EV_MSC
,
MSC_SCAN
,
scancode
);
input_sync
(
tpacpi_inputdev
);
mutex_unlock
(
&
tpacpi_inputdev_send_mutex
);
}
}
static
void
tpacpi_input_send_radiosw
(
void
)
{
int
wlsw
;
mutex_lock
(
&
tpacpi_inputdev_send_mutex
);
if
(
tp_features
.
hotkey_wlsw
&&
!
hotkey_get_wlsw
(
&
wlsw
))
{
input_report_switch
(
tpacpi_inputdev
,
SW_RADIO
,
!!
wlsw
);
input_sync
(
tpacpi_inputdev
);
}
mutex_unlock
(
&
tpacpi_inputdev_send_mutex
);
}
static
void
hotkey_notify
(
struct
ibm_struct
*
ibm
,
u32
event
)
{
u32
hkey
;
unsigned
int
keycode
,
scancode
;
unsigned
int
scancode
;
int
send_acpi_ev
;
int
ignore_acpi_ev
;
int
unk_ev
;
if
(
event
!=
0x80
)
{
printk
(
IBM_ERR
"unknown HKEY notification event %d
\n
"
,
event
);
printk
(
TPACPI_ERR
"unknown HKEY notification event %d
\n
"
,
event
);
/* forward it to userspace, maybe it knows how to handle it */
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
event
,
0
);
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
event
,
0
);
return
;
}
while
(
1
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
&
hkey
,
"MHKP"
,
"d"
))
{
printk
(
IBM
_ERR
"failed to retrieve HKEY event
\n
"
);
printk
(
TPACPI
_ERR
"failed to retrieve HKEY event
\n
"
);
return
;
}
...
...
@@ -1228,8 +2087,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
return
;
}
send_acpi_ev
=
0
;
send_acpi_ev
=
1
;
ignore_acpi_ev
=
0
;
unk_ev
=
0
;
switch
(
hkey
>>
12
)
{
case
1
:
...
...
@@ -1237,104 +2097,139 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
scancode
=
hkey
&
0xfff
;
if
(
scancode
>
0
&&
scancode
<
0x21
)
{
scancode
--
;
keycode
=
hotkey_keycode_map
[
scancode
];
tpacpi_input_send_key
(
scancode
,
keycode
);
if
(
!
(
hotkey_source_mask
&
(
1
<<
scancode
)))
{
tpacpi_input_send_key
(
scancode
);
send_acpi_ev
=
0
;
}
else
{
ignore_acpi_ev
=
1
;
}
}
else
{
printk
(
IBM_ERR
"hotkey 0x%04x out of range for keyboard map
\n
"
,
hkey
);
send_acpi_ev
=
1
;
unk_ev
=
1
;
}
break
;
case
5
:
/* 0x5000-0x5FFF: LID */
/* we don't handle it through this path, just
* eat up known LID events */
if
(
hkey
!=
0x5001
&&
hkey
!=
0x5002
)
{
printk
(
IBM_ERR
"unknown LID-related HKEY event: 0x%04x
\n
"
,
hkey
);
send_acpi_ev
=
1
;
case
2
:
/* Wakeup reason */
switch
(
hkey
)
{
case
0x2304
:
/* suspend, undock */
case
0x2404
:
/* hibernation, undock */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_UNDOCK
;
ignore_acpi_ev
=
1
;
break
;
case
0x2305
:
/* suspend, bay eject */
case
0x2405
:
/* hibernation, bay eject */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_BAYEJ
;
ignore_acpi_ev
=
1
;
break
;
default:
unk_ev
=
1
;
}
if
(
hotkey_wakeup_reason
!=
TP_ACPI_WAKEUP_NONE
)
{
printk
(
TPACPI_INFO
"woke up due to a hot-unplug "
"request...
\n
"
);
hotkey_wakeup_reason_notify_change
();
}
break
;
case
3
:
/* bay-related wakeups */
if
(
hkey
==
0x3003
)
{
hotkey_autosleep_ack
=
1
;
printk
(
TPACPI_INFO
"bay ejected
\n
"
);
hotkey_wakeup_hotunplug_complete_notify_change
();
}
else
{
unk_ev
=
1
;
}
break
;
case
4
:
/* dock-related wakeups */
if
(
hkey
==
0x4003
)
{
hotkey_autosleep_ack
=
1
;
printk
(
TPACPI_INFO
"undocked
\n
"
);
hotkey_wakeup_hotunplug_complete_notify_change
();
}
else
{
unk_ev
=
1
;
}
break
;
case
5
:
/* 0x5000-0x5FFF: human interface helpers */
switch
(
hkey
)
{
case
0x5010
:
/* Lenovo new BIOS: brightness changed */
case
0x5009
:
/* X61t: swivel up (tablet mode) */
case
0x500a
:
/* X61t: swivel down (normal mode) */
case
0x500b
:
/* X61t: tablet pen inserted into bay */
case
0x500c
:
/* X61t: tablet pen removed from bay */
break
;
case
0x5001
:
case
0x5002
:
/* LID switch events. Do not propagate */
ignore_acpi_ev
=
1
;
break
;
default:
unk_ev
=
1
;
}
break
;
case
7
:
/* 0x7000-0x7FFF: misc */
if
(
tp_features
.
hotkey_wlsw
&&
hkey
==
0x7000
)
{
tpacpi_input_send_radiosw
();
hotkey_radio_sw_notify_change
();
send_acpi_ev
=
0
;
break
;
}
/* fallthrough to default */
default:
/* case 2: dock-related */
/* 0x2305 - T43 waking up due to bay lever eject while aslept */
/* case 3: ultra-bay related. maybe bay in dock? */
/* 0x3003 - T43 after wake up by bay lever eject (0x2305) */
printk
(
IBM_NOTICE
"unhandled HKEY event 0x%04x
\n
"
,
hkey
);
send_acpi_ev
=
1
;
unk_ev
=
1
;
}
if
(
unk_ev
)
{
printk
(
TPACPI_NOTICE
"unhandled HKEY event 0x%04x
\n
"
,
hkey
);
}
/* Legacy events */
if
(
!
ignore_acpi_ev
&&
(
send_acpi_ev
||
hotkey_report_mode
<
2
))
{
acpi_bus_generate_proc_event
(
ibm
->
acpi
->
device
,
event
,
hkey
);
if
(
!
ignore_acpi_ev
&&
(
send_acpi_ev
||
hotkey_report_mode
<
2
))
{
acpi_bus_generate_proc_event
(
ibm
->
acpi
->
device
,
event
,
hkey
);
}
/* netlink events */
if
(
!
ignore_acpi_ev
&&
send_acpi_ev
)
{
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
event
,
hkey
);
acpi_bus_generate_netlink_event
(
ibm
->
acpi
->
device
->
pnp
.
device_class
,
ibm
->
acpi
->
device
->
dev
.
bus_id
,
event
,
hkey
);
}
}
}
static
void
hotkey_resume
(
void
)
{
tpacpi_input_send_radiosw
();
}
/*
* Call with hotkey_mutex held
*/
static
int
hotkey_get
(
int
*
status
,
u32
*
mask
)
static
void
hotkey_suspend
(
pm_message_t
state
)
{
if
(
!
acpi_evalf
(
hkey_handle
,
status
,
"DHKC"
,
"d"
))
return
-
EIO
;
if
(
tp_features
.
hotkey_mask
)
if
(
!
acpi_evalf
(
hkey_handle
,
mask
,
"DHKN"
,
"d"
))
return
-
EIO
;
return
0
;
/* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason
=
TP_ACPI_WAKEUP_NONE
;
hotkey_autosleep_ack
=
0
;
}
/*
* Call with hotkey_mutex held
*/
static
int
hotkey_set
(
int
status
,
u32
mask
)
static
void
hotkey_resume
(
void
)
{
int
i
;
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"MHKC"
,
"vd"
,
status
))
return
-
EIO
;
if
(
tp_features
.
hotkey_mask
)
for
(
i
=
0
;
i
<
32
;
i
++
)
{
int
bit
=
((
1
<<
i
)
&
mask
)
!=
0
;
if
(
!
acpi_evalf
(
hkey_handle
,
NULL
,
"MHKM"
,
"vdd"
,
i
+
1
,
bit
))
return
-
EIO
;
}
return
0
;
if
(
hotkey_mask_get
())
printk
(
TPACPI_ERR
"error while trying to read hot key mask "
"from firmware
\n
"
);
tpacpi_input_send_radiosw
();
hotkey_radio_sw_notify_change
();
hotkey_wakeup_reason_notify_change
();
hotkey_wakeup_hotunplug_complete_notify_change
();
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_setup_safe
(
0
);
#endif
}
/* procfs -------------------------------------------------------------- */
static
int
hotkey_read
(
char
*
p
)
{
int
res
,
status
;
u32
mask
;
int
len
=
0
;
if
(
!
tp_features
.
hotkey
)
{
...
...
@@ -1344,14 +2239,16 @@ static int hotkey_read(char *p)
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_get
(
&
status
,
&
mask
);
res
=
hotkey_status_get
(
&
status
);
if
(
!
res
)
res
=
hotkey_mask_get
();
mutex_unlock
(
&
hotkey_mutex
);
if
(
res
)
return
res
;
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
,
enabled
(
status
,
0
));
if
(
tp_features
.
hotkey_mask
)
{
len
+=
sprintf
(
p
+
len
,
"mask:
\t\t
0x%08x
\n
"
,
mask
);
len
+=
sprintf
(
p
+
len
,
"mask:
\t\t
0x%08x
\n
"
,
hotkey_
mask
);
len
+=
sprintf
(
p
+
len
,
"commands:
\t
enable, disable, reset, <mask>
\n
"
);
}
else
{
...
...
@@ -1367,7 +2264,6 @@ static int hotkey_write(char *buf)
int
res
,
status
;
u32
mask
;
char
*
cmd
;
int
do_cmd
=
0
;
if
(
!
tp_features
.
hotkey
)
return
-
ENODEV
;
...
...
@@ -1375,9 +2271,8 @@ static int hotkey_write(char *buf)
if
(
mutex_lock_interruptible
(
&
hotkey_mutex
))
return
-
ERESTARTSYS
;
res
=
hotkey_get
(
&
status
,
&
mask
);
if
(
res
)
goto
errexit
;
status
=
-
1
;
mask
=
hotkey_mask
;
res
=
0
;
while
((
cmd
=
next_cmd
(
&
buf
)))
{
...
...
@@ -1396,11 +2291,12 @@ static int hotkey_write(char *buf)
res
=
-
EINVAL
;
goto
errexit
;
}
do_cmd
=
1
;
}
if
(
status
!=
-
1
)
res
=
hotkey_status_set
(
status
);
if
(
do_cmd
)
res
=
hotkey_
set
(
status
,
mask
);
if
(
!
res
&&
mask
!=
hotkey_mask
)
res
=
hotkey_
mask_set
(
mask
);
errexit:
mutex_unlock
(
&
hotkey_mutex
);
...
...
@@ -1408,7 +2304,7 @@ static int hotkey_write(char *buf)
}
static
const
struct
acpi_device_id
ibm_htk_device_ids
[]
=
{
{
IBM
_HKEY_HID
,
0
},
{
TPACPI_ACPI
_HKEY_HID
,
0
},
{
""
,
0
},
};
...
...
@@ -1425,6 +2321,7 @@ static struct ibm_struct hotkey_driver_data = {
.
write
=
hotkey_write
,
.
exit
=
hotkey_exit
,
.
resume
=
hotkey_resume
,
.
suspend
=
hotkey_suspend
,
.
acpi
=
&
ibm_hotkey_acpidriver
,
};
...
...
@@ -1432,6 +2329,16 @@ static struct ibm_struct hotkey_driver_data = {
* Bluetooth subdriver
*/
enum
{
/* ACPI GBDC/SBDC bits */
TP_ACPI_BLUETOOTH_HWPRESENT
=
0x01
,
/* Bluetooth hw available */
TP_ACPI_BLUETOOTH_RADIOSSW
=
0x02
,
/* Bluetooth radio enabled */
TP_ACPI_BLUETOOTH_UNK
=
0x04
,
/* unknown function */
};
static
int
bluetooth_get_radiosw
(
void
);
static
int
bluetooth_set_radiosw
(
int
radio_on
);
/* sysfs bluetooth enable ---------------------------------------------- */
static
ssize_t
bluetooth_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
...
...
@@ -1483,7 +2390,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing bluetooth subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
hkey
);
TPACPI
_ACPIHANDLE_INIT
(
hkey
);
/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
...
...
@@ -1596,6 +2503,16 @@ static struct ibm_struct bluetooth_driver_data = {
* Wan subdriver
*/
enum
{
/* ACPI GWAN/SWAN bits */
TP_ACPI_WANCARD_HWPRESENT
=
0x01
,
/* Wan hw available */
TP_ACPI_WANCARD_RADIOSSW
=
0x02
,
/* Wan radio enabled */
TP_ACPI_WANCARD_UNK
=
0x04
,
/* unknown function */
};
static
int
wan_get_radiosw
(
void
);
static
int
wan_set_radiosw
(
int
radio_on
);
/* sysfs wan enable ---------------------------------------------------- */
static
ssize_t
wan_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
...
...
@@ -1647,7 +2564,7 @@ static int __init wan_init(struct ibm_init_struct *iibm)
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing wan subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
hkey
);
TPACPI
_ACPIHANDLE_INIT
(
hkey
);
tp_features
.
wan
=
hkey_handle
&&
acpi_evalf
(
hkey_handle
,
&
status
,
"GWAN"
,
"qd"
);
...
...
@@ -1759,17 +2676,41 @@ static struct ibm_struct wan_driver_data = {
* Video subdriver
*/
enum
video_access_mode
{
TPACPI_VIDEO_NONE
=
0
,
TPACPI_VIDEO_570
,
/* 570 */
TPACPI_VIDEO_770
,
/* 600e/x, 770e, 770x */
TPACPI_VIDEO_NEW
,
/* all others */
};
enum
{
/* video status flags, based on VIDEO_570 */
TP_ACPI_VIDEO_S_LCD
=
0x01
,
/* LCD output enabled */
TP_ACPI_VIDEO_S_CRT
=
0x02
,
/* CRT output enabled */
TP_ACPI_VIDEO_S_DVI
=
0x08
,
/* DVI output enabled */
};
enum
{
/* TPACPI_VIDEO_570 constants */
TP_ACPI_VIDEO_570_PHSCMD
=
0x87
,
/* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHSMASK
=
0x03
,
/* PHS bits that map to
* video_status_flags */
TP_ACPI_VIDEO_570_PHS2CMD
=
0x8b
,
/* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHS2SET
=
0x80
,
/* unknown magic constant :( */
};
static
enum
video_access_mode
video_supported
;
static
int
video_orig_autosw
;
IBM_HANDLE
(
vid
,
root
,
"
\\
_SB.PCI.AGP.VGA"
,
/* 570 */
static
int
video_autosw_get
(
void
);
static
int
video_autosw_set
(
int
enable
);
TPACPI_HANDLE
(
vid
,
root
,
"
\\
_SB.PCI.AGP.VGA"
,
/* 570 */
"
\\
_SB.PCI0.AGP0.VID0"
,
/* 600e/x, 770x */
"
\\
_SB.PCI0.VID0"
,
/* 770e */
"
\\
_SB.PCI0.VID"
,
/* A21e, G4x, R50e, X30, X40 */
"
\\
_SB.PCI0.AGP.VID"
,
/* all others */
);
/* R30, R31 */
IBM
_HANDLE
(
vid2
,
root
,
"
\\
_SB.PCI0.AGPB.VID"
);
/* G41 */
TPACPI
_HANDLE
(
vid2
,
root
,
"
\\
_SB.PCI0.AGPB.VID"
);
/* G41 */
static
int
__init
video_init
(
struct
ibm_init_struct
*
iibm
)
{
...
...
@@ -1777,8 +2718,8 @@ static int __init video_init(struct ibm_init_struct *iibm)
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing video subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
vid
);
IBM
_ACPIHANDLE_INIT
(
vid2
);
TPACPI
_ACPIHANDLE_INIT
(
vid
);
TPACPI
_ACPIHANDLE_INIT
(
vid2
);
if
(
vid2_handle
&&
acpi_evalf
(
NULL
,
&
ivga
,
"
\\
IVGA"
,
"d"
)
&&
ivga
)
/* G41, assume IVGA doesn't change */
...
...
@@ -1809,7 +2750,7 @@ static void video_exit(void)
dbg_printk
(
TPACPI_DBG_EXIT
,
"restoring original video autoswitch mode
\n
"
);
if
(
video_autosw_set
(
video_orig_autosw
))
printk
(
IBM
_ERR
"error while trying to restore original "
printk
(
TPACPI
_ERR
"error while trying to restore original "
"video autoswitch mode
\n
"
);
}
...
...
@@ -1882,13 +2823,14 @@ static int video_outputsw_set(int status)
res
=
acpi_evalf
(
vid_handle
,
NULL
,
"ASWT"
,
"vdd"
,
status
*
0x100
,
0
);
if
(
!
autosw
&&
video_autosw_set
(
autosw
))
{
printk
(
IBM_ERR
"video auto-switch left enabled due to error
\n
"
);
printk
(
TPACPI_ERR
"video auto-switch left enabled due to error
\n
"
);
return
-
EIO
;
}
break
;
case
TPACPI_VIDEO_NEW
:
res
=
acpi_evalf
(
NULL
,
NULL
,
"
\\
VUPS"
,
"vd"
,
0x80
)
&&
acpi_evalf
(
NULL
,
NULL
,
"
\\
VSDS"
,
"vdd"
,
status
,
1
);
acpi_evalf
(
NULL
,
NULL
,
"
\\
VSDS"
,
"vdd"
,
status
,
1
);
break
;
default:
return
-
ENOSYS
;
...
...
@@ -1951,7 +2893,8 @@ static int video_outputsw_cycle(void)
return
-
ENOSYS
;
}
if
(
!
autosw
&&
video_autosw_set
(
autosw
))
{
printk
(
IBM_ERR
"video auto-switch left enabled due to error
\n
"
);
printk
(
TPACPI_ERR
"video auto-switch left enabled due to error
\n
"
);
return
-
EIO
;
}
...
...
@@ -2080,16 +3023,16 @@ static struct ibm_struct video_driver_data = {
* Light (thinklight) subdriver
*/
IBM
_HANDLE
(
lght
,
root
,
"
\\
LGHT"
);
/* A21e, A2xm/p, T20-22, X20-21 */
IBM
_HANDLE
(
ledb
,
ec
,
"LEDB"
);
/* G4x */
TPACPI
_HANDLE
(
lght
,
root
,
"
\\
LGHT"
);
/* A21e, A2xm/p, T20-22, X20-21 */
TPACPI
_HANDLE
(
ledb
,
ec
,
"LEDB"
);
/* G4x */
static
int
__init
light_init
(
struct
ibm_init_struct
*
iibm
)
{
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing light subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
ledb
);
IBM
_ACPIHANDLE_INIT
(
lght
);
IBM
_ACPIHANDLE_INIT
(
cmos
);
TPACPI
_ACPIHANDLE_INIT
(
ledb
);
TPACPI
_ACPIHANDLE_INIT
(
lght
);
TPACPI
_ACPIHANDLE_INIT
(
cmos
);
/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
tp_features
.
light
=
(
cmos_handle
||
lght_handle
)
&&
!
ledb_handle
;
...
...
@@ -2167,14 +3110,18 @@ static struct ibm_struct light_driver_data = {
#ifdef CONFIG_THINKPAD_ACPI_DOCK
IBM_HANDLE
(
dock
,
root
,
"
\\
_SB.GDCK"
,
/* X30, X31, X40 */
static
void
dock_notify
(
struct
ibm_struct
*
ibm
,
u32
event
);
static
int
dock_read
(
char
*
p
);
static
int
dock_write
(
char
*
buf
);
TPACPI_HANDLE
(
dock
,
root
,
"
\\
_SB.GDCK"
,
/* X30, X31, X40 */
"
\\
_SB.PCI0.DOCK"
,
/* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
"
\\
_SB.PCI0.PCI1.DOCK"
,
/* all others */
"
\\
_SB.PCI.ISA.SLCE"
,
/* 570 */
);
/* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
/* don't list other alternatives as we install a notify handler on the 570 */
IBM
_HANDLE
(
pci
,
root
,
"
\\
_SB.PCI"
);
/* 570 */
TPACPI
_HANDLE
(
pci
,
root
,
"
\\
_SB.PCI"
);
/* 570 */
static
const
struct
acpi_device_id
ibm_pci_device_ids
[]
=
{
{
PCI_ROOT_HID_STRING
,
0
},
...
...
@@ -2217,7 +3164,7 @@ static int __init dock_init(struct ibm_init_struct *iibm)
{
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing dock subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
dock
);
TPACPI
_ACPIHANDLE_INIT
(
dock
);
vdbg_printk
(
TPACPI_DBG_INIT
,
"dock is %s
\n
"
,
str_supported
(
dock_handle
!=
NULL
));
...
...
@@ -2233,7 +3180,7 @@ static int __init dock_init2(struct ibm_init_struct *iibm)
if
(
dock_driver_data
[
0
].
flags
.
acpi_driver_registered
&&
dock_driver_data
[
0
].
flags
.
acpi_notify_installed
)
{
IBM
_ACPIHANDLE_INIT
(
pci
);
TPACPI
_ACPIHANDLE_INIT
(
pci
);
dock2_needed
=
(
pci_handle
!=
NULL
);
vdbg_printk
(
TPACPI_DBG_INIT
,
"dock PCI handler for the TP 570 is %s
\n
"
,
...
...
@@ -2265,7 +3212,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event)
else
if
(
event
==
0
&&
docked
)
data
=
3
;
/* dock */
else
{
printk
(
IBM
_ERR
"unknown dock event %d, status %d
\n
"
,
printk
(
TPACPI
_ERR
"unknown dock event %d, status %d
\n
"
,
event
,
_sta
(
dock_handle
));
data
=
0
;
/* unknown */
}
...
...
@@ -2321,18 +3268,19 @@ static int dock_write(char *buf)
*/
#ifdef CONFIG_THINKPAD_ACPI_BAY
IBM_HANDLE
(
bay
,
root
,
"
\\
_SB.PCI.IDE.SECN.MAST"
,
/* 570 */
TPACPI_HANDLE
(
bay
,
root
,
"
\\
_SB.PCI.IDE.SECN.MAST"
,
/* 570 */
"
\\
_SB.PCI0.IDE0.IDES.IDSM"
,
/* 600e/x, 770e, 770x */
"
\\
_SB.PCI0.SATA.SCND.MSTR"
,
/* T60, X60, Z60 */
"
\\
_SB.PCI0.IDE0.SCND.MSTR"
,
/* all others */
);
/* A21e, R30, R31 */
IBM
_HANDLE
(
bay_ej
,
bay
,
"_EJ3"
,
/* 600e/x, A2xm/p, A3x */
TPACPI
_HANDLE
(
bay_ej
,
bay
,
"_EJ3"
,
/* 600e/x, A2xm/p, A3x */
"_EJ0"
,
/* all others */
);
/* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
IBM
_HANDLE
(
bay2
,
root
,
"
\\
_SB.PCI0.IDE0.PRIM.SLAV"
,
/* A3x, R32 */
TPACPI
_HANDLE
(
bay2
,
root
,
"
\\
_SB.PCI0.IDE0.PRIM.SLAV"
,
/* A3x, R32 */
"
\\
_SB.PCI0.IDE0.IDEP.IDPS"
,
/* 600e/x, 770e, 770x */
);
/* all others */
IBM
_HANDLE
(
bay2_ej
,
bay2
,
"_EJ3"
,
/* 600e/x, 770e, A3x */
TPACPI
_HANDLE
(
bay2_ej
,
bay2
,
"_EJ3"
,
/* 600e/x, 770e, A3x */
"_EJ0"
,
/* 770x */
);
/* all others */
...
...
@@ -2340,12 +3288,12 @@ static int __init bay_init(struct ibm_init_struct *iibm)
{
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing bay subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
bay
);
TPACPI
_ACPIHANDLE_INIT
(
bay
);
if
(
bay_handle
)
IBM
_ACPIHANDLE_INIT
(
bay_ej
);
IBM
_ACPIHANDLE_INIT
(
bay2
);
TPACPI
_ACPIHANDLE_INIT
(
bay_ej
);
TPACPI
_ACPIHANDLE_INIT
(
bay2
);
if
(
bay2_handle
)
IBM
_ACPIHANDLE_INIT
(
bay2_ej
);
TPACPI
_ACPIHANDLE_INIT
(
bay2_ej
);
tp_features
.
bay_status
=
bay_handle
&&
acpi_evalf
(
bay_handle
,
NULL
,
"_STA"
,
"qv"
);
...
...
@@ -2474,7 +3422,7 @@ static int __init cmos_init(struct ibm_init_struct *iibm)
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing cmos commands subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
cmos
);
TPACPI
_ACPIHANDLE_INIT
(
cmos
);
vdbg_printk
(
TPACPI_DBG_INIT
,
"cmos commands are %s
\n
"
,
str_supported
(
cmos_handle
!=
NULL
));
...
...
@@ -2538,10 +3486,24 @@ static struct ibm_struct cmos_driver_data = {
* LED subdriver
*/
enum
led_access_mode
{
TPACPI_LED_NONE
=
0
,
TPACPI_LED_570
,
/* 570 */
TPACPI_LED_OLD
,
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
TPACPI_LED_NEW
,
/* all others */
};
enum
{
/* For TPACPI_LED_OLD */
TPACPI_LED_EC_HLCL
=
0x0c
,
/* EC reg to get led to power on */
TPACPI_LED_EC_HLBL
=
0x0d
,
/* EC reg to blink a lit led */
TPACPI_LED_EC_HLMS
=
0x0e
,
/* EC reg to select led to command */
};
static
enum
led_access_mode
led_supported
;
IBM_HANDLE
(
led
,
ec
,
"SLED"
,
/* 570 */
"SYSL"
,
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
TPACPI_HANDLE
(
led
,
ec
,
"SLED"
,
/* 570 */
"SYSL"
,
/* 600e/x, 770e, 770x, A21e, A2xm/p, */
/* T20-22, X20-21 */
"LED"
,
/* all others */
);
/* R30, R31 */
...
...
@@ -2549,7 +3511,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
{
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing LED subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
led
);
TPACPI
_ACPIHANDLE_INIT
(
led
);
if
(
!
led_handle
)
/* led not supported on R30, R31 */
...
...
@@ -2638,13 +3600,11 @@ static int led_write(char *buf)
led
=
1
<<
led
;
ret
=
ec_write
(
TPACPI_LED_EC_HLMS
,
led
);
if
(
ret
>=
0
)
ret
=
ec_write
(
TPACPI_LED_EC_HLBL
,
led
*
led_exp_hlbl
[
ind
]);
ret
=
ec_write
(
TPACPI_LED_EC_HLBL
,
led
*
led_exp_hlbl
[
ind
]);
if
(
ret
>=
0
)
ret
=
ec_write
(
TPACPI_LED_EC_HLCL
,
led
*
led_exp_hlcl
[
ind
]);
ret
=
ec_write
(
TPACPI_LED_EC_HLCL
,
led
*
led_exp_hlcl
[
ind
]);
if
(
ret
<
0
)
return
ret
;
}
else
{
...
...
@@ -2668,13 +3628,13 @@ static struct ibm_struct led_driver_data = {
* Beep subdriver
*/
IBM
_HANDLE
(
beep
,
ec
,
"BEEP"
);
/* all except R30, R31 */
TPACPI
_HANDLE
(
beep
,
ec
,
"BEEP"
);
/* all except R30, R31 */
static
int
__init
beep_init
(
struct
ibm_init_struct
*
iibm
)
{
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing beep subdriver
\n
"
);
IBM
_ACPIHANDLE_INIT
(
beep
);
TPACPI
_ACPIHANDLE_INIT
(
beep
);
vdbg_printk
(
TPACPI_DBG_INIT
,
"beep is %s
\n
"
,
str_supported
(
beep_handle
!=
NULL
));
...
...
@@ -2727,90 +3687,192 @@ static struct ibm_struct beep_driver_data = {
* Thermal subdriver
*/
static
enum
thermal_access_mode
thermal_read_mode
;
/* sysfs temp##_input -------------------------------------------------- */
static
ssize_t
thermal_temp_input_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
sensor_device_attribute
*
sensor_attr
=
to_sensor_dev_attr
(
attr
);
int
idx
=
sensor_attr
->
index
;
s32
value
;
int
res
;
res
=
thermal_get_sensor
(
idx
,
&
value
);
if
(
res
)
return
res
;
if
(
value
==
TP_EC_THERMAL_TMP_NA
*
1000
)
return
-
ENXIO
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
value
);
}
#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB)
static
struct
sensor_device_attribute
sensor_dev_attr_thermal_temp_input
[]
=
{
THERMAL_SENSOR_ATTR_TEMP
(
1
,
0
),
THERMAL_SENSOR_ATTR_TEMP
(
2
,
1
),
THERMAL_SENSOR_ATTR_TEMP
(
3
,
2
),
THERMAL_SENSOR_ATTR_TEMP
(
4
,
3
),
THERMAL_SENSOR_ATTR_TEMP
(
5
,
4
),
THERMAL_SENSOR_ATTR_TEMP
(
6
,
5
),
THERMAL_SENSOR_ATTR_TEMP
(
7
,
6
),
THERMAL_SENSOR_ATTR_TEMP
(
8
,
7
),
THERMAL_SENSOR_ATTR_TEMP
(
9
,
8
),
THERMAL_SENSOR_ATTR_TEMP
(
10
,
9
),
THERMAL_SENSOR_ATTR_TEMP
(
11
,
10
),
THERMAL_SENSOR_ATTR_TEMP
(
12
,
11
),
THERMAL_SENSOR_ATTR_TEMP
(
13
,
12
),
THERMAL_SENSOR_ATTR_TEMP
(
14
,
13
),
THERMAL_SENSOR_ATTR_TEMP
(
15
,
14
),
THERMAL_SENSOR_ATTR_TEMP
(
16
,
15
),
};
#define THERMAL_ATTRS(X) \
&sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
static
struct
attribute
*
thermal_temp_input_attr
[]
=
{
THERMAL_ATTRS
(
8
),
THERMAL_ATTRS
(
9
),
THERMAL_ATTRS
(
10
),
THERMAL_ATTRS
(
11
),
THERMAL_ATTRS
(
12
),
THERMAL_ATTRS
(
13
),
THERMAL_ATTRS
(
14
),
THERMAL_ATTRS
(
15
),
THERMAL_ATTRS
(
0
),
THERMAL_ATTRS
(
1
),
THERMAL_ATTRS
(
2
),
THERMAL_ATTRS
(
3
),
THERMAL_ATTRS
(
4
),
THERMAL_ATTRS
(
5
),
THERMAL_ATTRS
(
6
),
THERMAL_ATTRS
(
7
),
NULL
enum
thermal_access_mode
{
TPACPI_THERMAL_NONE
=
0
,
/* No thermal support */
TPACPI_THERMAL_ACPI_TMP07
,
/* Use ACPI TMP0-7 */
TPACPI_THERMAL_ACPI_UPDT
,
/* Use ACPI TMP0-7 with UPDT */
TPACPI_THERMAL_TPEC_8
,
/* Use ACPI EC regs, 8 sensors */
TPACPI_THERMAL_TPEC_16
,
/* Use ACPI EC regs, 16 sensors */
};
static
const
struct
attribute_group
thermal_temp_input16_group
=
{
.
attrs
=
thermal_temp_input_attr
enum
{
/* TPACPI_THERMAL_TPEC_* */
TP_EC_THERMAL_TMP0
=
0x78
,
/* ACPI EC regs TMP 0..7 */
TP_EC_THERMAL_TMP8
=
0xC0
,
/* ACPI EC regs TMP 8..15 */
TP_EC_THERMAL_TMP_NA
=
-
128
,
/* ACPI EC sensor not available */
};
static
const
struct
attribute_group
thermal_temp_input8_group
=
{
.
attrs
=
&
thermal_temp_input_attr
[
8
]
#define TPACPI_MAX_THERMAL_SENSORS 16
/* Max thermal sensors supported */
struct
ibm_thermal_sensors_struct
{
s32
temp
[
TPACPI_MAX_THERMAL_SENSORS
];
};
#undef THERMAL_SENSOR_ATTR_TEMP
#undef THERMAL_ATTRS
/* --------------------------------------------------------------------- */
static
enum
thermal_access_mode
thermal_read_mode
;
static
int
__init
thermal_init
(
struct
ibm_init_struct
*
iibm
)
/* idx is zero-based */
static
int
thermal_get_sensor
(
int
idx
,
s32
*
value
)
{
u8
t
,
ta1
,
ta2
;
int
t
;
s8
tmp
;
char
tmpi
[
5
];
t
=
TP_EC_THERMAL_TMP0
;
switch
(
thermal_read_mode
)
{
#if TPACPI_MAX_THERMAL_SENSORS >= 16
case
TPACPI_THERMAL_TPEC_16
:
if
(
idx
>=
8
&&
idx
<=
15
)
{
t
=
TP_EC_THERMAL_TMP8
;
idx
-=
8
;
}
/* fallthrough */
#endif
case
TPACPI_THERMAL_TPEC_8
:
if
(
idx
<=
7
)
{
if
(
!
acpi_ec_read
(
t
+
idx
,
&
tmp
))
return
-
EIO
;
*
value
=
tmp
*
1000
;
return
0
;
}
break
;
case
TPACPI_THERMAL_ACPI_UPDT
:
if
(
idx
<=
7
)
{
snprintf
(
tmpi
,
sizeof
(
tmpi
),
"TMP%c"
,
'0'
+
idx
);
if
(
!
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"v"
))
return
-
EIO
;
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
*
value
=
(
t
-
2732
)
*
100
;
return
0
;
}
break
;
case
TPACPI_THERMAL_ACPI_TMP07
:
if
(
idx
<=
7
)
{
snprintf
(
tmpi
,
sizeof
(
tmpi
),
"TMP%c"
,
'0'
+
idx
);
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
if
(
t
>
127
||
t
<
-
127
)
t
=
TP_EC_THERMAL_TMP_NA
;
*
value
=
t
*
1000
;
return
0
;
}
break
;
case
TPACPI_THERMAL_NONE
:
default:
return
-
ENOSYS
;
}
return
-
EINVAL
;
}
static
int
thermal_get_sensors
(
struct
ibm_thermal_sensors_struct
*
s
)
{
int
res
,
i
;
int
n
;
n
=
8
;
i
=
0
;
if
(
!
s
)
return
-
EINVAL
;
if
(
thermal_read_mode
==
TPACPI_THERMAL_TPEC_16
)
n
=
16
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
res
=
thermal_get_sensor
(
i
,
&
s
->
temp
[
i
]);
if
(
res
)
return
res
;
}
return
n
;
}
/* sysfs temp##_input -------------------------------------------------- */
static
ssize_t
thermal_temp_input_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
sensor_device_attribute
*
sensor_attr
=
to_sensor_dev_attr
(
attr
);
int
idx
=
sensor_attr
->
index
;
s32
value
;
int
res
;
res
=
thermal_get_sensor
(
idx
,
&
value
);
if
(
res
)
return
res
;
if
(
value
==
TP_EC_THERMAL_TMP_NA
*
1000
)
return
-
ENXIO
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
value
);
}
#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, \
thermal_temp_input_show, NULL, _idxB)
static
struct
sensor_device_attribute
sensor_dev_attr_thermal_temp_input
[]
=
{
THERMAL_SENSOR_ATTR_TEMP
(
1
,
0
),
THERMAL_SENSOR_ATTR_TEMP
(
2
,
1
),
THERMAL_SENSOR_ATTR_TEMP
(
3
,
2
),
THERMAL_SENSOR_ATTR_TEMP
(
4
,
3
),
THERMAL_SENSOR_ATTR_TEMP
(
5
,
4
),
THERMAL_SENSOR_ATTR_TEMP
(
6
,
5
),
THERMAL_SENSOR_ATTR_TEMP
(
7
,
6
),
THERMAL_SENSOR_ATTR_TEMP
(
8
,
7
),
THERMAL_SENSOR_ATTR_TEMP
(
9
,
8
),
THERMAL_SENSOR_ATTR_TEMP
(
10
,
9
),
THERMAL_SENSOR_ATTR_TEMP
(
11
,
10
),
THERMAL_SENSOR_ATTR_TEMP
(
12
,
11
),
THERMAL_SENSOR_ATTR_TEMP
(
13
,
12
),
THERMAL_SENSOR_ATTR_TEMP
(
14
,
13
),
THERMAL_SENSOR_ATTR_TEMP
(
15
,
14
),
THERMAL_SENSOR_ATTR_TEMP
(
16
,
15
),
};
#define THERMAL_ATTRS(X) \
&sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
static
struct
attribute
*
thermal_temp_input_attr
[]
=
{
THERMAL_ATTRS
(
8
),
THERMAL_ATTRS
(
9
),
THERMAL_ATTRS
(
10
),
THERMAL_ATTRS
(
11
),
THERMAL_ATTRS
(
12
),
THERMAL_ATTRS
(
13
),
THERMAL_ATTRS
(
14
),
THERMAL_ATTRS
(
15
),
THERMAL_ATTRS
(
0
),
THERMAL_ATTRS
(
1
),
THERMAL_ATTRS
(
2
),
THERMAL_ATTRS
(
3
),
THERMAL_ATTRS
(
4
),
THERMAL_ATTRS
(
5
),
THERMAL_ATTRS
(
6
),
THERMAL_ATTRS
(
7
),
NULL
};
static
const
struct
attribute_group
thermal_temp_input16_group
=
{
.
attrs
=
thermal_temp_input_attr
};
static
const
struct
attribute_group
thermal_temp_input8_group
=
{
.
attrs
=
&
thermal_temp_input_attr
[
8
]
};
#undef THERMAL_SENSOR_ATTR_TEMP
#undef THERMAL_ATTRS
/* --------------------------------------------------------------------- */
static
int
__init
thermal_init
(
struct
ibm_init_struct
*
iibm
)
{
u8
t
,
ta1
,
ta2
;
int
i
;
int
acpi_tmp7
;
int
res
;
...
...
@@ -2845,12 +3907,13 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
if
(
ta1
==
0
)
{
/* This is sheer paranoia, but we handle it anyway */
if
(
acpi_tmp7
)
{
printk
(
IBM
_ERR
printk
(
TPACPI
_ERR
"ThinkPad ACPI EC access misbehaving, "
"falling back to ACPI TMPx access mode
\n
"
);
"falling back to ACPI TMPx access "
"mode
\n
"
);
thermal_read_mode
=
TPACPI_THERMAL_ACPI_TMP07
;
}
else
{
printk
(
IBM
_ERR
printk
(
TPACPI
_ERR
"ThinkPad ACPI EC access misbehaving, "
"disabling thermal sensors access
\n
"
);
thermal_read_mode
=
TPACPI_THERMAL_NONE
;
...
...
@@ -2877,7 +3940,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
str_supported
(
thermal_read_mode
!=
TPACPI_THERMAL_NONE
),
thermal_read_mode
);
switch
(
thermal_read_mode
)
{
switch
(
thermal_read_mode
)
{
case
TPACPI_THERMAL_TPEC_16
:
res
=
sysfs_create_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
thermal_temp_input16_group
);
...
...
@@ -2902,7 +3965,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
static
void
thermal_exit
(
void
)
{
switch
(
thermal_read_mode
)
{
switch
(
thermal_read_mode
)
{
case
TPACPI_THERMAL_TPEC_16
:
sysfs_remove_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
thermal_temp_input16_group
);
...
...
@@ -2919,88 +3982,6 @@ static void thermal_exit(void)
}
}
/* idx is zero-based */
static
int
thermal_get_sensor
(
int
idx
,
s32
*
value
)
{
int
t
;
s8
tmp
;
char
tmpi
[
5
];
t
=
TP_EC_THERMAL_TMP0
;
switch
(
thermal_read_mode
)
{
#if TPACPI_MAX_THERMAL_SENSORS >= 16
case
TPACPI_THERMAL_TPEC_16
:
if
(
idx
>=
8
&&
idx
<=
15
)
{
t
=
TP_EC_THERMAL_TMP8
;
idx
-=
8
;
}
/* fallthrough */
#endif
case
TPACPI_THERMAL_TPEC_8
:
if
(
idx
<=
7
)
{
if
(
!
acpi_ec_read
(
t
+
idx
,
&
tmp
))
return
-
EIO
;
*
value
=
tmp
*
1000
;
return
0
;
}
break
;
case
TPACPI_THERMAL_ACPI_UPDT
:
if
(
idx
<=
7
)
{
snprintf
(
tmpi
,
sizeof
(
tmpi
),
"TMP%c"
,
'0'
+
idx
);
if
(
!
acpi_evalf
(
ec_handle
,
NULL
,
"UPDT"
,
"v"
))
return
-
EIO
;
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
*
value
=
(
t
-
2732
)
*
100
;
return
0
;
}
break
;
case
TPACPI_THERMAL_ACPI_TMP07
:
if
(
idx
<=
7
)
{
snprintf
(
tmpi
,
sizeof
(
tmpi
),
"TMP%c"
,
'0'
+
idx
);
if
(
!
acpi_evalf
(
ec_handle
,
&
t
,
tmpi
,
"d"
))
return
-
EIO
;
if
(
t
>
127
||
t
<
-
127
)
t
=
TP_EC_THERMAL_TMP_NA
;
*
value
=
t
*
1000
;
return
0
;
}
break
;
case
TPACPI_THERMAL_NONE
:
default:
return
-
ENOSYS
;
}
return
-
EINVAL
;
}
static
int
thermal_get_sensors
(
struct
ibm_thermal_sensors_struct
*
s
)
{
int
res
,
i
;
int
n
;
n
=
8
;
i
=
0
;
if
(
!
s
)
return
-
EINVAL
;
if
(
thermal_read_mode
==
TPACPI_THERMAL_TPEC_16
)
n
=
16
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
res
=
thermal_get_sensor
(
i
,
&
s
->
temp
[
i
]);
if
(
res
)
return
res
;
}
return
n
;
}
static
int
thermal_read
(
char
*
p
)
{
int
len
=
0
;
...
...
@@ -3103,26 +4084,122 @@ static struct ibm_struct ecdump_driver_data = {
* Backlight/brightness subdriver
*/
static
struct
backlight_device
*
ibm_backlight_device
;
#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
static
struct
backlight_
ops
ibm_backlight_data
=
{
.
get_brightness
=
brightness_get
,
.
update_status
=
brightness_update_status
,
};
static
struct
backlight_
device
*
ibm_backlight_device
;
static
int
brightness_offset
=
0x31
;
static
int
brightness_mode
;
static
unsigned
int
brightness_enable
=
2
;
/* 2 = auto, 0 = no, 1 = yes */
static
struct
mutex
brightness_mutex
;
static
int
__init
tpacpi_query_bcll_levels
(
acpi_handle
handle
)
/*
* ThinkPads can read brightness from two places: EC 0x31, or
* CMOS NVRAM byte 0x5E, bits 0-3.
*/
static
int
brightness_get
(
struct
backlight_device
*
bd
)
{
struct
acpi_buffer
buffer
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
union
acpi_object
*
obj
;
u8
lec
=
0
,
lcmos
=
0
,
level
=
0
;
if
(
brightness_mode
&
1
)
{
if
(
!
acpi_ec_read
(
brightness_offset
,
&
lec
))
return
-
EIO
;
lec
&=
(
tp_features
.
bright_16levels
)
?
0x0f
:
0x07
;
level
=
lec
;
};
if
(
brightness_mode
&
2
)
{
lcmos
=
(
nvram_read_byte
(
TP_NVRAM_ADDR_BRIGHTNESS
)
&
TP_NVRAM_MASK_LEVEL_BRIGHTNESS
)
>>
TP_NVRAM_POS_LEVEL_BRIGHTNESS
;
lcmos
&=
(
tp_features
.
bright_16levels
)
?
0x0f
:
0x07
;
level
=
lcmos
;
}
if
(
brightness_mode
==
3
&&
lec
!=
lcmos
)
{
printk
(
TPACPI_ERR
"CMOS NVRAM (%u) and EC (%u) do not agree "
"on display brightness level
\n
"
,
(
unsigned
int
)
lcmos
,
(
unsigned
int
)
lec
);
return
-
EIO
;
}
return
level
;
}
/* May return EINTR which can always be mapped to ERESTARTSYS */
static
int
brightness_set
(
int
value
)
{
int
cmos_cmd
,
inc
,
i
,
res
;
int
current_value
;
if
(
value
>
((
tp_features
.
bright_16levels
)
?
15
:
7
))
return
-
EINVAL
;
res
=
mutex_lock_interruptible
(
&
brightness_mutex
);
if
(
res
<
0
)
return
res
;
current_value
=
brightness_get
(
NULL
);
if
(
current_value
<
0
)
{
res
=
current_value
;
goto
errout
;
}
cmos_cmd
=
value
>
current_value
?
TP_CMOS_BRIGHTNESS_UP
:
TP_CMOS_BRIGHTNESS_DOWN
;
inc
=
(
value
>
current_value
)
?
1
:
-
1
;
res
=
0
;
for
(
i
=
current_value
;
i
!=
value
;
i
+=
inc
)
{
if
((
brightness_mode
&
2
)
&&
issue_thinkpad_cmos_command
(
cmos_cmd
))
{
res
=
-
EIO
;
goto
errout
;
}
if
((
brightness_mode
&
1
)
&&
!
acpi_ec_write
(
brightness_offset
,
i
+
inc
))
{
res
=
-
EIO
;
goto
errout
;;
}
}
errout:
mutex_unlock
(
&
brightness_mutex
);
return
res
;
}
/* sysfs backlight class ----------------------------------------------- */
static
int
brightness_update_status
(
struct
backlight_device
*
bd
)
{
/* it is the backlight class's job (caller) to handle
* EINTR and other errors properly */
return
brightness_set
(
(
bd
->
props
.
fb_blank
==
FB_BLANK_UNBLANK
&&
bd
->
props
.
power
==
FB_BLANK_UNBLANK
)
?
bd
->
props
.
brightness
:
0
);
}
static
struct
backlight_ops
ibm_backlight_data
=
{
.
get_brightness
=
brightness_get
,
.
update_status
=
brightness_update_status
,
};
/* --------------------------------------------------------------------- */
static
int
__init
tpacpi_query_bcll_levels
(
acpi_handle
handle
)
{
struct
acpi_buffer
buffer
=
{
ACPI_ALLOCATE_BUFFER
,
NULL
};
union
acpi_object
*
obj
;
int
rc
;
if
(
ACPI_SUCCESS
(
acpi_evaluate_object
(
handle
,
NULL
,
NULL
,
&
buffer
)))
{
obj
=
(
union
acpi_object
*
)
buffer
.
pointer
;
if
(
!
obj
||
(
obj
->
type
!=
ACPI_TYPE_PACKAGE
))
{
printk
(
IBM
_ERR
"Unknown BCLL data, "
"please report this to %s
\n
"
,
IBM
_MAIL
);
printk
(
TPACPI
_ERR
"Unknown BCLL data, "
"please report this to %s
\n
"
,
TPACPI
_MAIL
);
rc
=
0
;
}
else
{
rc
=
obj
->
package
.
count
;
...
...
@@ -3160,14 +4237,15 @@ static int __init brightness_check_levels(void)
void
*
found_node
=
NULL
;
if
(
!
vid_handle
)
{
IBM
_ACPIHANDLE_INIT
(
vid
);
TPACPI
_ACPIHANDLE_INIT
(
vid
);
}
if
(
!
vid_handle
)
return
0
;
/* Search for a BCLL package with 16 levels */
status
=
acpi_walk_namespace
(
ACPI_TYPE_PACKAGE
,
vid_handle
,
3
,
brightness_find_bcll
,
NULL
,
&
found_node
);
brightness_find_bcll
,
NULL
,
&
found_node
);
return
(
ACPI_SUCCESS
(
status
)
&&
found_node
!=
NULL
);
}
...
...
@@ -3193,14 +4271,14 @@ static int __init brightness_check_std_acpi_support(void)
void
*
found_node
=
NULL
;
if
(
!
vid_handle
)
{
IBM
_ACPIHANDLE_INIT
(
vid
);
TPACPI
_ACPIHANDLE_INIT
(
vid
);
}
if
(
!
vid_handle
)
return
0
;
/* Search for a _BCL method, but don't execute it */
status
=
acpi_walk_namespace
(
ACPI_TYPE_METHOD
,
vid_handle
,
3
,
brightness_find_bcl
,
NULL
,
&
found_node
);
brightness_find_bcl
,
NULL
,
&
found_node
);
return
(
ACPI_SUCCESS
(
status
)
&&
found_node
!=
NULL
);
}
...
...
@@ -3215,12 +4293,14 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if
(
!
brightness_enable
)
{
dbg_printk
(
TPACPI_DBG_INIT
,
"brightness support disabled by module parameter
\n
"
);
"brightness support disabled by "
"module parameter
\n
"
);
return
1
;
}
else
if
(
brightness_enable
>
1
)
{
if
(
brightness_check_std_acpi_support
())
{
printk
(
IBM_NOTICE
"standard ACPI backlight interface available, not loading native one...
\n
"
);
printk
(
TPACPI_NOTICE
"standard ACPI backlight interface "
"available, not loading native one...
\n
"
);
return
1
;
}
}
...
...
@@ -3247,13 +4327,14 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return
1
;
if
(
tp_features
.
bright_16levels
)
printk
(
IBM_INFO
"detected a 16-level brightness capable ThinkPad
\n
"
);
printk
(
TPACPI_INFO
"detected a 16-level brightness capable ThinkPad
\n
"
);
ibm_backlight_device
=
backlight_device_register
(
TPACPI_BACKLIGHT_DEV_NAME
,
NULL
,
NULL
,
&
ibm_backlight_data
);
if
(
IS_ERR
(
ibm_backlight_device
))
{
printk
(
IBM
_ERR
"Could not register backlight device
\n
"
);
printk
(
TPACPI
_ERR
"Could not register backlight device
\n
"
);
return
PTR_ERR
(
ibm_backlight_device
);
}
vdbg_printk
(
TPACPI_DBG_INIT
,
"brightness is supported
\n
"
);
...
...
@@ -3276,99 +4357,13 @@ static void brightness_exit(void)
}
}
static
int
brightness_update_status
(
struct
backlight_device
*
bd
)
{
/* it is the backlight class's job (caller) to handle
* EINTR and other errors properly */
return
brightness_set
(
(
bd
->
props
.
fb_blank
==
FB_BLANK_UNBLANK
&&
bd
->
props
.
power
==
FB_BLANK_UNBLANK
)
?
bd
->
props
.
brightness
:
0
);
}
/*
* ThinkPads can read brightness from two places: EC 0x31, or
* CMOS NVRAM byte 0x5E, bits 0-3.
*/
static
int
brightness_get
(
struct
backlight_device
*
bd
)
{
u8
lec
=
0
,
lcmos
=
0
,
level
=
0
;
if
(
brightness_mode
&
1
)
{
if
(
!
acpi_ec_read
(
brightness_offset
,
&
lec
))
return
-
EIO
;
lec
&=
(
tp_features
.
bright_16levels
)
?
0x0f
:
0x07
;
level
=
lec
;
};
if
(
brightness_mode
&
2
)
{
lcmos
=
(
nvram_read_byte
(
TP_NVRAM_ADDR_BRIGHTNESS
)
&
TP_NVRAM_MASK_LEVEL_BRIGHTNESS
)
>>
TP_NVRAM_POS_LEVEL_BRIGHTNESS
;
lcmos
&=
(
tp_features
.
bright_16levels
)
?
0x0f
:
0x07
;
level
=
lcmos
;
}
if
(
brightness_mode
==
3
&&
lec
!=
lcmos
)
{
printk
(
IBM_ERR
"CMOS NVRAM (%u) and EC (%u) do not agree "
"on display brightness level
\n
"
,
(
unsigned
int
)
lcmos
,
(
unsigned
int
)
lec
);
return
-
EIO
;
}
return
level
;
}
/* May return EINTR which can always be mapped to ERESTARTSYS */
static
int
brightness_set
(
int
value
)
{
int
cmos_cmd
,
inc
,
i
,
res
;
int
current_value
;
if
(
value
>
((
tp_features
.
bright_16levels
)
?
15
:
7
))
return
-
EINVAL
;
res
=
mutex_lock_interruptible
(
&
brightness_mutex
);
if
(
res
<
0
)
return
res
;
current_value
=
brightness_get
(
NULL
);
if
(
current_value
<
0
)
{
res
=
current_value
;
goto
errout
;
}
cmos_cmd
=
value
>
current_value
?
TP_CMOS_BRIGHTNESS_UP
:
TP_CMOS_BRIGHTNESS_DOWN
;
inc
=
(
value
>
current_value
)
?
1
:
-
1
;
res
=
0
;
for
(
i
=
current_value
;
i
!=
value
;
i
+=
inc
)
{
if
((
brightness_mode
&
2
)
&&
issue_thinkpad_cmos_command
(
cmos_cmd
))
{
res
=
-
EIO
;
goto
errout
;
}
if
((
brightness_mode
&
1
)
&&
!
acpi_ec_write
(
brightness_offset
,
i
+
inc
))
{
res
=
-
EIO
;
goto
errout
;;
}
}
errout:
mutex_unlock
(
&
brightness_mutex
);
return
res
;
}
static
int
brightness_read
(
char
*
p
)
{
int
len
=
0
;
int
level
;
if
((
level
=
brightness_get
(
NULL
))
<
0
)
{
level
=
brightness_get
(
NULL
);
if
(
level
<
0
)
{
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
unreadable
\n
"
);
}
else
{
len
+=
sprintf
(
p
+
len
,
"level:
\t\t
%d
\n
"
,
level
);
...
...
@@ -3425,6 +4420,8 @@ static struct ibm_struct brightness_driver_data = {
* Volume subdriver
*/
static
int
volume_offset
=
0x30
;
static
int
volume_read
(
char
*
p
)
{
int
len
=
0
;
...
...
@@ -3474,8 +4471,11 @@ static int volume_write(char *buf)
}
else
return
-
EINVAL
;
if
(
new_level
!=
level
)
{
/* mute doesn't change */
cmos_cmd
=
new_level
>
level
?
TP_CMOS_VOLUME_UP
:
TP_CMOS_VOLUME_DOWN
;
if
(
new_level
!=
level
)
{
/* mute doesn't change */
cmos_cmd
=
(
new_level
>
level
)
?
TP_CMOS_VOLUME_UP
:
TP_CMOS_VOLUME_DOWN
;
inc
=
new_level
>
level
?
1
:
-
1
;
if
(
mute
&&
(
issue_thinkpad_cmos_command
(
cmos_cmd
)
||
...
...
@@ -3487,14 +4487,18 @@ static int volume_write(char *buf)
!
acpi_ec_write
(
volume_offset
,
i
+
inc
))
return
-
EIO
;
if
(
mute
&&
(
issue_thinkpad_cmos_command
(
TP_CMOS_VOLUME_MUTE
)
||
!
acpi_ec_write
(
volume_offset
,
new_level
+
mute
)))
if
(
mute
&&
(
issue_thinkpad_cmos_command
(
TP_CMOS_VOLUME_MUTE
)
||
!
acpi_ec_write
(
volume_offset
,
new_level
+
mute
)))
{
return
-
EIO
;
}
}
if
(
new_mute
!=
mute
)
{
/* level doesn't change */
cmos_cmd
=
new_mute
?
TP_CMOS_VOLUME_MUTE
:
TP_CMOS_VOLUME_UP
;
if
(
new_mute
!=
mute
)
{
/* level doesn't change */
cmos_cmd
=
(
new_mute
)
?
TP_CMOS_VOLUME_MUTE
:
TP_CMOS_VOLUME_UP
;
if
(
issue_thinkpad_cmos_command
(
cmos_cmd
)
||
!
acpi_ec_write
(
volume_offset
,
level
+
new_mute
))
...
...
@@ -3616,478 +4620,333 @@ static struct ibm_struct volume_driver_data = {
* but the ACPI tables just mention level 7.
*/
enum
{
/* Fan control constants */
fan_status_offset
=
0x2f
,
/* EC register 0x2f */
fan_rpm_offset
=
0x84
,
/* EC register 0x84: LSB, 0x85 MSB (RPM)
* 0x84 must be read before 0x85 */
TP_EC_FAN_FULLSPEED
=
0x40
,
/* EC fan mode: full speed */
TP_EC_FAN_AUTO
=
0x80
,
/* EC fan mode: auto fan control */
TPACPI_FAN_LAST_LEVEL
=
0x100
,
/* Use cached last-seen fan level */
};
enum
fan_status_access_mode
{
TPACPI_FAN_NONE
=
0
,
/* No fan status or control */
TPACPI_FAN_RD_ACPI_GFAN
,
/* Use ACPI GFAN */
TPACPI_FAN_RD_TPEC
,
/* Use ACPI EC regs 0x2f, 0x84-0x85 */
};
enum
fan_control_access_mode
{
TPACPI_FAN_WR_NONE
=
0
,
/* No fan control */
TPACPI_FAN_WR_ACPI_SFAN
,
/* Use ACPI SFAN */
TPACPI_FAN_WR_TPEC
,
/* Use ACPI EC reg 0x2f */
TPACPI_FAN_WR_ACPI_FANS
,
/* Use ACPI FANS and EC reg 0x2f */
};
enum
fan_control_commands
{
TPACPI_FAN_CMD_SPEED
=
0x0001
,
/* speed command */
TPACPI_FAN_CMD_LEVEL
=
0x0002
,
/* level command */
TPACPI_FAN_CMD_ENABLE
=
0x0004
,
/* enable/disable cmd,
* and also watchdog cmd */
};
static
int
fan_control_allowed
;
static
enum
fan_status_access_mode
fan_status_access_mode
;
static
enum
fan_control_access_mode
fan_control_access_mode
;
static
enum
fan_control_commands
fan_control_commands
;
static
u8
fan_control_initial_status
;
static
u8
fan_control_desired_level
;
static
int
fan_watchdog_maxinterval
;
static
struct
mutex
fan_mutex
;
static
void
fan_watchdog_fire
(
struct
work_struct
*
ignored
);
static
int
fan_watchdog_maxinterval
;
static
DECLARE_DELAYED_WORK
(
fan_watchdog_task
,
fan_watchdog_fire
);
IBM
_HANDLE
(
fans
,
ec
,
"FANS"
);
/* X31, X40, X41 */
IBM
_HANDLE
(
gfan
,
ec
,
"GFAN"
,
/* 570 */
TPACPI
_HANDLE
(
fans
,
ec
,
"FANS"
);
/* X31, X40, X41 */
TPACPI
_HANDLE
(
gfan
,
ec
,
"GFAN"
,
/* 570 */
"
\\
FSPD"
,
/* 600e/x, 770e, 770x */
);
/* all others */
IBM
_HANDLE
(
sfan
,
ec
,
"SFAN"
,
/* 570 */
TPACPI
_HANDLE
(
sfan
,
ec
,
"SFAN"
,
/* 570 */
"JFNS"
,
/* 770x-JL */
);
/* all others */
/*
* SYSFS fan layout: hwmon compatible (device)
*
* pwm*_enable:
* 0: "disengaged" mode
* 1: manual mode
* 2: native EC "auto" mode (recommended, hardware default)
*
* pwm*: set speed in manual mode, ignored otherwise.
* 0 is level 0; 255 is level 7. Intermediate points done with linear
* interpolation.
*
* fan*_input: tachometer reading, RPM
*
*
* SYSFS fan layout: extensions
*
* fan_watchdog (driver):
* fan watchdog interval in seconds, 0 disables (default), max 120
* Call with fan_mutex held
*/
/* sysfs fan pwm1_enable ----------------------------------------------- */
static
ssize_t
fan_pwm1_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
static
void
fan_update_desired_level
(
u8
status
)
{
int
res
,
mode
;
u8
status
;
res
=
fan_get_status_safe
(
&
status
);
if
(
res
)
return
res
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
/* Return most likely status. In fact, it
* might be the only possible status */
status
=
TP_EC_FAN_AUTO
;
}
if
((
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
==
0
)
{
if
(
status
>
7
)
fan_control_desired_level
=
7
;
else
fan_control_desired_level
=
status
;
}
if
(
status
&
TP_EC_FAN_FULLSPEED
)
{
mode
=
0
;
}
else
if
(
status
&
TP_EC_FAN_AUTO
)
{
mode
=
2
;
}
else
mode
=
1
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
mode
);
}
static
ssize_t
fan_pwm1_enable_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
static
int
fan_get_status
(
u8
*
status
)
{
unsigned
long
t
;
int
res
,
level
;
u8
s
;
if
(
parse_strtoul
(
buf
,
2
,
&
t
))
return
-
EINVAL
;
/* TODO:
* Add TPACPI_FAN_RD_ACPI_FANS ? */
switch
(
t
)
{
case
0
:
level
=
TP_EC_FAN_FULLSPEED
;
break
;
case
1
:
level
=
TPACPI_FAN_LAST_LEVEL
;
break
;
case
2
:
level
=
TP_EC_FAN_AUTO
;
break
;
case
3
:
/* reserved for software-controlled auto mode */
return
-
ENOSYS
;
default:
return
-
EINVAL
;
}
switch
(
fan_status_access_mode
)
{
case
TPACPI_FAN_RD_ACPI_GFAN
:
/* 570, 600e/x, 770e, 770x */
res
=
fan_set_level_safe
(
level
);
if
(
res
==
-
ENXIO
)
return
-
EINVAL
;
else
if
(
res
<
0
)
return
res
;
if
(
unlikely
(
!
acpi_evalf
(
gfan_handle
,
&
s
,
NULL
,
"d"
)))
return
-
EIO
;
fan_watchdog_reset
();
if
(
likely
(
status
))
*
status
=
s
&
0x07
;
return
count
;
}
break
;
static
struct
device_attribute
dev_attr_fan_pwm1_enable
=
__ATTR
(
pwm1_enable
,
S_IWUSR
|
S_IRUGO
,
fan_pwm1_enable_show
,
fan_pwm1_enable_store
);
case
TPACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_ec_read
(
fan_status_offset
,
&
s
)))
return
-
EIO
;
/* sysfs fan pwm1 ------------------------------------------------------ */
static
ssize_t
fan_pwm1_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
res
;
u8
status
;
if
(
likely
(
status
))
*
status
=
s
;
res
=
fan_get_status_safe
(
&
status
);
if
(
res
)
return
res
;
break
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
status
=
TP_EC_FAN_AUTO
;
}
default:
return
-
ENXIO
;
}
if
((
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
!=
0
)
status
=
fan_control_desired_level
;
return
0
;
}
if
(
status
>
7
)
status
=
7
;
static
int
fan_get_status_safe
(
u8
*
status
)
{
int
rc
;
u8
s
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
(
status
*
255
)
/
7
);
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
fan_get_status
(
&
s
);
if
(
!
rc
)
fan_update_desired_level
(
s
);
mutex_unlock
(
&
fan_mutex
);
if
(
status
)
*
status
=
s
;
return
rc
;
}
static
ssize_t
fan_pwm1_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
static
int
fan_get_speed
(
unsigned
int
*
speed
)
{
unsigned
long
s
;
int
rc
;
u8
status
,
newlevel
;
u8
hi
,
lo
;
if
(
parse_strtoul
(
buf
,
255
,
&
s
))
return
-
EINVAL
;
switch
(
fan_status_access_mode
)
{
case
TPACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_ec_read
(
fan_rpm_offset
,
&
lo
)
||
!
acpi_ec_read
(
fan_rpm_offset
+
1
,
&
hi
)))
return
-
EIO
;
/* scale down from 0-255 to 0-7 */
newlevel
=
(
s
>>
5
)
&
0x07
;
if
(
likely
(
speed
))
*
speed
=
(
hi
<<
8
)
|
lo
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
break
;
rc
=
fan_get_status
(
&
status
);
if
(
!
rc
&&
(
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
==
0
)
{
rc
=
fan_set_level
(
newlevel
);
if
(
rc
==
-
ENXIO
)
rc
=
-
EINVAL
;
else
if
(
!
rc
)
{
fan_update_desired_level
(
newlevel
);
fan_watchdog_reset
();
}
default:
return
-
ENXIO
;
}
mutex_unlock
(
&
fan_mutex
);
return
(
rc
)
?
rc
:
count
;
return
0
;
}
static
struct
device_attribute
dev_attr_fan_pwm1
=
__ATTR
(
pwm1
,
S_IWUSR
|
S_IRUGO
,
fan_pwm1_show
,
fan_pwm1_store
);
/* sysfs fan fan1_input ------------------------------------------------ */
static
ssize_t
fan_fan1_input_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
static
int
fan_set_level
(
int
level
)
{
i
nt
res
;
unsigned
int
speed
;
i
f
(
!
fan_control_allowed
)
return
-
EPERM
;
res
=
fan_get_speed
(
&
speed
);
if
(
res
<
0
)
return
res
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_SFAN
:
if
(
level
>=
0
&&
level
<=
7
)
{
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
level
))
return
-
EIO
;
}
else
return
-
EINVAL
;
break
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
speed
);
}
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
if
((
level
!=
TP_EC_FAN_AUTO
)
&&
(
level
!=
TP_EC_FAN_FULLSPEED
)
&&
((
level
<
0
)
||
(
level
>
7
)))
return
-
EINVAL
;
static
struct
device_attribute
dev_attr_fan_fan1_input
=
__ATTR
(
fan1_input
,
S_IRUGO
,
fan_fan1_input_show
,
NULL
);
/* safety net should the EC not support AUTO
* or FULLSPEED mode bits and just ignore them */
if
(
level
&
TP_EC_FAN_FULLSPEED
)
level
|=
7
;
/* safety min speed 7 */
else
if
(
level
&
TP_EC_FAN_AUTO
)
level
|=
4
;
/* safety min speed 4 */
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
static
ssize_t
fan_fan_watchdog_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
fan_watchdog_maxinterval
);
if
(
!
acpi_ec_write
(
fan_status_offset
,
level
))
return
-
EIO
;
else
tp_features
.
fan_ctrl_status_undef
=
0
;
break
;
default:
return
-
ENXIO
;
}
return
0
;
}
static
ssize_t
fan_fan_watchdog_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
static
int
fan_set_level_safe
(
int
level
)
{
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
120
,
&
t
))
return
-
EINVAL
;
int
rc
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
fan_watchdog_maxinterval
=
t
;
fan_watchdog_reset
();
return
count
;
}
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
static
DRIVER_ATTR
(
fan_watchdog
,
S_IWUSR
|
S_IRUGO
,
fan_fan_watchdog_show
,
fan_fan_watchdog_store
)
;
if
(
level
==
TPACPI_FAN_LAST_LEVEL
)
level
=
fan_control_desired_level
;
/* --------------------------------------------------------------------- */
static
struct
attribute
*
fan_attributes
[]
=
{
&
dev_attr_fan_pwm1_enable
.
attr
,
&
dev_attr_fan_pwm1
.
attr
,
&
dev_attr_fan_fan1_input
.
attr
,
NULL
};
rc
=
fan_set_level
(
level
);
if
(
!
rc
)
fan_update_desired_level
(
level
);
static
const
struct
attribute_group
fan_attr_group
=
{
.
attrs
=
fan_attributes
,
}
;
mutex_unlock
(
&
fan_mutex
);
return
rc
;
}
static
int
__init
fan_init
(
struct
ibm_init_struct
*
iibm
)
static
int
fan_set_enable
(
void
)
{
u8
s
;
int
rc
;
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing fan subdriver
\n
"
);
if
(
!
fan_control_allowed
)
return
-
EPERM
;
mutex_init
(
&
fan_mutex
);
fan_status_access_mode
=
TPACPI_FAN_NONE
;
fan_control_access_mode
=
TPACPI_FAN_WR_NONE
;
fan_control_commands
=
0
;
fan_watchdog_maxinterval
=
0
;
tp_features
.
fan_ctrl_status_undef
=
0
;
fan_control_desired_level
=
7
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
IBM_ACPIHANDLE_INIT
(
fans
);
IBM_ACPIHANDLE_INIT
(
gfan
);
IBM_ACPIHANDLE_INIT
(
sfan
);
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
rc
=
fan_get_status
(
&
s
);
if
(
rc
<
0
)
break
;
if
(
gfan_handle
)
{
/* 570, 600e/x, 770e, 770x */
fan_status_access_mode
=
TPACPI_FAN_RD_ACPI_GFAN
;
}
else
{
/* all other ThinkPads: note that even old-style
* ThinkPad ECs supports the fan control register */
if
(
likely
(
acpi_ec_read
(
fan_status_offset
,
&
fan_control_initial_status
)))
{
fan_status_access_mode
=
TPACPI_FAN_RD_TPEC
;
/* Don't go out of emergency fan mode */
if
(
s
!=
7
)
{
s
&=
0x07
;
s
|=
TP_EC_FAN_AUTO
|
4
;
/* min fan speed 4 */
}
/* In some ThinkPads, neither the EC nor the ACPI
* DSDT initialize the fan status, and it ends up
* being set to 0x07 when it *could* be either
* 0x07 or 0x80.
*
* Enable for TP-1Y (T43), TP-78 (R51e),
* TP-76 (R52), TP-70 (T43, R52), which are known
* to be buggy. */
if
(
fan_control_initial_status
==
0x07
)
{
switch
(
thinkpad_id
.
ec_model
)
{
case
0x5931
:
/* TP-1Y */
case
0x3837
:
/* TP-78 */
case
0x3637
:
/* TP-76 */
case
0x3037
:
/* TP-70 */
printk
(
IBM_NOTICE
"fan_init: initial fan status is "
"unknown, assuming it is in auto "
"mode
\n
"
);
tp_features
.
fan_ctrl_status_undef
=
1
;
;;
}
}
}
else
{
printk
(
IBM_ERR
"ThinkPad ACPI EC access misbehaving, "
"fan status and control unavailable
\n
"
);
return
1
;
}
}
if
(
sfan_handle
)
{
/* 570, 770x-JL */
fan_control_access_mode
=
TPACPI_FAN_WR_ACPI_SFAN
;
fan_control_commands
|=
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
else
{
if
(
!
gfan_handle
)
{
/* gfan without sfan means no fan control */
/* all other models implement TP EC 0x2f control */
if
(
fans_handle
)
{
/* X31, X40, X41 */
fan_control_access_mode
=
TPACPI_FAN_WR_ACPI_FANS
;
fan_control_commands
|=
TPACPI_FAN_CMD_SPEED
|
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
else
{
fan_control_access_mode
=
TPACPI_FAN_WR_TPEC
;
fan_control_commands
|=
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
if
(
!
acpi_ec_write
(
fan_status_offset
,
s
))
rc
=
-
EIO
;
else
{
tp_features
.
fan_ctrl_status_undef
=
0
;
rc
=
0
;
}
}
vdbg_printk
(
TPACPI_DBG_INIT
,
"fan is %s, modes %d, %d
\n
"
,
str_supported
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
||
fan_control_access_mode
!=
TPACPI_FAN_WR_NONE
),
fan_status_access_mode
,
fan_control_access_mode
);
/* fan control master switch */
if
(
!
fan_control_allowed
)
{
fan_control_access_mode
=
TPACPI_FAN_WR_NONE
;
fan_control_commands
=
0
;
dbg_printk
(
TPACPI_DBG_INIT
,
"fan control features disabled by parameter
\n
"
);
}
/* update fan_control_desired_level */
if
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
)
fan_get_status_safe
(
NULL
);
break
;
if
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
||
fan_control_access_mode
!=
TPACPI_FAN_WR_NONE
)
{
rc
=
sysfs_create_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
fan_attr_group
);
if
(
!
(
rc
<
0
))
rc
=
driver_create_file
(
&
tpacpi_hwmon_pdriver
.
driver
,
&
driver_attr_fan_watchdog
);
case
TPACPI_FAN_WR_ACPI_SFAN
:
rc
=
fan_get_status
(
&
s
);
if
(
rc
<
0
)
return
rc
;
return
0
;
}
else
return
1
;
}
/*
* Call with fan_mutex held
*/
static
void
fan_update_desired_level
(
u8
status
)
{
if
((
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
==
0
)
{
if
(
status
>
7
)
fan_control_desired_level
=
7
;
else
fan_control_desired_level
=
status
;
}
}
static
int
fan_get_status
(
u8
*
status
)
{
u8
s
;
/* TODO:
* Add TPACPI_FAN_RD_ACPI_FANS ? */
switch
(
fan_status_access_mode
)
{
case
TPACPI_FAN_RD_ACPI_GFAN
:
/* 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_evalf
(
gfan_handle
,
&
s
,
NULL
,
"d"
)))
return
-
EIO
;
if
(
likely
(
status
))
*
status
=
s
&
0x07
;
break
;
break
;
case
TPACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_ec_read
(
fan_status_offset
,
&
s
)))
return
-
EIO
;
s
&=
0x07
;
if
(
likely
(
status
))
*
status
=
s
;
/* Set fan to at least level 4 */
s
|=
4
;
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
s
))
rc
=
-
EIO
;
else
rc
=
0
;
break
;
default:
r
eturn
-
ENXIO
;
r
c
=
-
ENXIO
;
}
return
0
;
mutex_unlock
(
&
fan_mutex
);
return
rc
;
}
static
int
fan_
get_status_safe
(
u8
*
status
)
static
int
fan_
set_disable
(
void
)
{
int
rc
;
u8
s
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
fan_get_status
(
&
s
);
if
(
!
rc
)
fan_update_desired_level
(
s
);
mutex_unlock
(
&
fan_mutex
);
if
(
status
)
*
status
=
s
;
rc
=
0
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
if
(
!
acpi_ec_write
(
fan_status_offset
,
0x00
))
rc
=
-
EIO
;
else
{
fan_control_desired_level
=
0
;
tp_features
.
fan_ctrl_status_undef
=
0
;
}
break
;
return
rc
;
}
case
TPACPI_FAN_WR_ACPI_SFAN
:
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
0x00
))
rc
=
-
EIO
;
else
fan_control_desired_level
=
0
;
break
;
static
void
fan_exit
(
void
)
{
vdbg_printk
(
TPACPI_DBG_EXIT
,
"cancelling any pending fan watchdog tasks
\n
"
);
default:
rc
=
-
ENXIO
;
}
/* FIXME: can we really do this unconditionally? */
sysfs_remove_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
fan_attr_group
);
driver_remove_file
(
&
tpacpi_hwmon_pdriver
.
driver
,
&
driver_attr_fan_watchdog
);
cancel_delayed_work
(
&
fan_watchdog_task
);
flush_scheduled_work
()
;
mutex_unlock
(
&
fan_mutex
);
return
rc
;
}
static
int
fan_
get_speed
(
unsigned
int
*
speed
)
static
int
fan_
set_speed
(
int
speed
)
{
u8
hi
,
lo
;
int
rc
;
switch
(
fan_status_access_mode
)
{
case
TPACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
(
unlikely
(
!
acpi_ec_read
(
fan_rpm_offset
,
&
lo
)
||
!
acpi_ec_read
(
fan_rpm_offset
+
1
,
&
hi
)))
return
-
EIO
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
likely
(
speed
))
*
speed
=
(
hi
<<
8
)
|
lo
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
0
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
if
(
speed
>=
0
&&
speed
<=
65535
)
{
if
(
!
acpi_evalf
(
fans_handle
,
NULL
,
NULL
,
"vddd"
,
speed
,
speed
,
speed
))
rc
=
-
EIO
;
}
else
rc
=
-
EINVAL
;
break
;
default:
r
eturn
-
ENXIO
;
r
c
=
-
ENXIO
;
}
return
0
;
}
static
void
fan_watchdog_fire
(
struct
work_struct
*
ignored
)
{
int
rc
;
if
(
tpacpi_lifecycle
!=
TPACPI_LIFE_RUNNING
)
return
;
printk
(
IBM_NOTICE
"fan watchdog: enabling fan
\n
"
);
rc
=
fan_set_enable
();
if
(
rc
<
0
)
{
printk
(
IBM_ERR
"fan watchdog: error %d while enabling fan, "
"will try again later...
\n
"
,
-
rc
);
/* reschedule for later */
fan_watchdog_reset
();
}
mutex_unlock
(
&
fan_mutex
);
return
rc
;
}
static
void
fan_watchdog_reset
(
void
)
...
...
@@ -4106,195 +4965,378 @@ static void fan_watchdog_reset(void)
if
(
!
schedule_delayed_work
(
&
fan_watchdog_task
,
msecs_to_jiffies
(
fan_watchdog_maxinterval
*
1000
)))
{
printk
(
IBM_ERR
"failed to schedule the fan watchdog, "
printk
(
TPACPI_ERR
"failed to schedule the fan watchdog, "
"watchdog will not trigger
\n
"
);
}
}
else
fan_watchdog_active
=
0
;
}
static
int
fan_set_level
(
int
level
)
static
void
fan_watchdog_fire
(
struct
work_struct
*
ignored
)
{
if
(
!
fan_control_allowed
)
return
-
EPERM
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_SFAN
:
if
(
level
>=
0
&&
level
<=
7
)
{
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
level
))
return
-
EIO
;
}
else
return
-
EINVAL
;
break
;
int
rc
;
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
if
((
level
!=
TP_EC_FAN_AUTO
)
&&
(
level
!=
TP_EC_FAN_FULLSPEED
)
&&
((
level
<
0
)
||
(
level
>
7
)))
return
-
EINVAL
;
if
(
tpacpi_lifecycle
!=
TPACPI_LIFE_RUNNING
)
return
;
/* safety net should the EC not support AUTO
* or FULLSPEED mode bits and just ignore them */
if
(
level
&
TP_EC_FAN_FULLSPEED
)
level
|=
7
;
/* safety min speed 7 */
else
if
(
level
&
TP_EC_FAN_FULLSPEED
)
level
|=
4
;
/* safety min speed 4 */
printk
(
TPACPI_NOTICE
"fan watchdog: enabling fan
\n
"
);
rc
=
fan_set_enable
();
if
(
rc
<
0
)
{
printk
(
TPACPI_ERR
"fan watchdog: error %d while enabling fan, "
"will try again later...
\n
"
,
-
rc
);
/* reschedule for later */
fan_watchdog_reset
();
}
}
if
(
!
acpi_ec_write
(
fan_status_offset
,
level
))
return
-
EIO
;
else
/*
* SYSFS fan layout: hwmon compatible (device)
*
* pwm*_enable:
* 0: "disengaged" mode
* 1: manual mode
* 2: native EC "auto" mode (recommended, hardware default)
*
* pwm*: set speed in manual mode, ignored otherwise.
* 0 is level 0; 255 is level 7. Intermediate points done with linear
* interpolation.
*
* fan*_input: tachometer reading, RPM
*
*
* SYSFS fan layout: extensions
*
* fan_watchdog (driver):
* fan watchdog interval in seconds, 0 disables (default), max 120
*/
/* sysfs fan pwm1_enable ----------------------------------------------- */
static
ssize_t
fan_pwm1_enable_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
res
,
mode
;
u8
status
;
res
=
fan_get_status_safe
(
&
status
);
if
(
res
)
return
res
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
break
;
}
else
{
/* Return most likely status. In fact, it
* might be the only possible status */
status
=
TP_EC_FAN_AUTO
;
}
}
if
(
status
&
TP_EC_FAN_FULLSPEED
)
{
mode
=
0
;
}
else
if
(
status
&
TP_EC_FAN_AUTO
)
{
mode
=
2
;
}
else
mode
=
1
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
mode
);
}
static
ssize_t
fan_pwm1_enable_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
unsigned
long
t
;
int
res
,
level
;
if
(
parse_strtoul
(
buf
,
2
,
&
t
))
return
-
EINVAL
;
switch
(
t
)
{
case
0
:
level
=
TP_EC_FAN_FULLSPEED
;
break
;
case
1
:
level
=
TPACPI_FAN_LAST_LEVEL
;
break
;
case
2
:
level
=
TP_EC_FAN_AUTO
;
break
;
case
3
:
/* reserved for software-controlled auto mode */
return
-
ENOSYS
;
default:
return
-
E
NXIO
;
return
-
E
INVAL
;
}
return
0
;
res
=
fan_set_level_safe
(
level
);
if
(
res
==
-
ENXIO
)
return
-
EINVAL
;
else
if
(
res
<
0
)
return
res
;
fan_watchdog_reset
();
return
count
;
}
static
int
fan_set_level_safe
(
int
level
)
static
struct
device_attribute
dev_attr_fan_pwm1_enable
=
__ATTR
(
pwm1_enable
,
S_IWUSR
|
S_IRUGO
,
fan_pwm1_enable_show
,
fan_pwm1_enable_store
);
/* sysfs fan pwm1 ------------------------------------------------------ */
static
ssize_t
fan_pwm1_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
rc
;
int
res
;
u8
status
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
res
=
fan_get_status_safe
(
&
status
);
if
(
res
)
return
res
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
if
(
status
!=
fan_control_initial_status
)
{
tp_features
.
fan_ctrl_status_undef
=
0
;
}
else
{
status
=
TP_EC_FAN_AUTO
;
}
}
if
(
level
==
TPACPI_FAN_LAST_LEVEL
)
level
=
fan_control_desired_level
;
if
((
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
!=
0
)
status
=
fan_control_desired_level
;
rc
=
fan_set_level
(
level
);
if
(
!
rc
)
fan_update_desired_level
(
level
);
if
(
status
>
7
)
status
=
7
;
mutex_unlock
(
&
fan_mutex
);
return
rc
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
(
status
*
255
)
/
7
);
}
static
int
fan_set_enable
(
void
)
static
ssize_t
fan_pwm1_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
u
8
s
;
u
nsigned
long
s
;
int
rc
;
u8
status
,
newlevel
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
parse_strtoul
(
buf
,
255
,
&
s
))
return
-
EINVAL
;
/* scale down from 0-255 to 0-7 */
newlevel
=
(
s
>>
5
)
&
0x07
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
rc
=
fan_get_status
(
&
s
);
if
(
rc
<
0
)
break
;
/* Don't go out of emergency fan mode */
if
(
s
!=
7
)
{
s
&=
0x07
;
s
|=
TP_EC_FAN_AUTO
|
4
;
/* min fan speed 4 */
rc
=
fan_get_status
(
&
status
);
if
(
!
rc
&&
(
status
&
(
TP_EC_FAN_AUTO
|
TP_EC_FAN_FULLSPEED
))
==
0
)
{
rc
=
fan_set_level
(
newlevel
);
if
(
rc
==
-
ENXIO
)
rc
=
-
EINVAL
;
else
if
(
!
rc
)
{
fan_update_desired_level
(
newlevel
);
fan_watchdog_reset
();
}
}
if
(
!
acpi_ec_write
(
fan_status_offset
,
s
))
rc
=
-
EIO
;
else
{
tp_features
.
fan_ctrl_status_undef
=
0
;
rc
=
0
;
}
break
;
mutex_unlock
(
&
fan_mutex
);
return
(
rc
)
?
rc
:
count
;
}
case
TPACPI_FAN_WR_ACPI_SFAN
:
rc
=
fan_get_status
(
&
s
);
if
(
rc
<
0
)
break
;
static
struct
device_attribute
dev_attr_fan_pwm1
=
__ATTR
(
pwm1
,
S_IWUSR
|
S_IRUGO
,
fan_pwm1_show
,
fan_pwm1_store
);
s
&=
0x07
;
/* sysfs fan fan1_input ------------------------------------------------ */
static
ssize_t
fan_fan1_input_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
int
res
;
unsigned
int
speed
;
/* Set fan to at least level 4 */
s
|=
4
;
res
=
fan_get_speed
(
&
speed
);
if
(
res
<
0
)
return
res
;
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
s
))
rc
=
-
EIO
;
else
rc
=
0
;
break
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
speed
);
}
default:
rc
=
-
ENXIO
;
}
static
struct
device_attribute
dev_attr_fan_fan1_input
=
__ATTR
(
fan1_input
,
S_IRUGO
,
fan_fan1_input_show
,
NULL
);
mutex_unlock
(
&
fan_mutex
);
return
rc
;
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
static
ssize_t
fan_fan_watchdog_show
(
struct
device_driver
*
drv
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%u
\n
"
,
fan_watchdog_maxinterval
);
}
static
int
fan_set_disable
(
void
)
static
ssize_t
fan_fan_watchdog_store
(
struct
device_driver
*
drv
,
const
char
*
buf
,
size_t
count
)
{
int
rc
;
unsigned
long
t
;
if
(
parse_strtoul
(
buf
,
120
,
&
t
))
return
-
EINVAL
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
fan_watchdog_maxinterval
=
t
;
fan_watchdog_reset
()
;
rc
=
0
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
case
TPACPI_FAN_WR_TPEC
:
if
(
!
acpi_ec_write
(
fan_status_offset
,
0x00
))
rc
=
-
EIO
;
else
{
fan_control_desired_level
=
0
;
tp_features
.
fan_ctrl_status_undef
=
0
;
return
count
;
}
static
DRIVER_ATTR
(
fan_watchdog
,
S_IWUSR
|
S_IRUGO
,
fan_fan_watchdog_show
,
fan_fan_watchdog_store
);
/* --------------------------------------------------------------------- */
static
struct
attribute
*
fan_attributes
[]
=
{
&
dev_attr_fan_pwm1_enable
.
attr
,
&
dev_attr_fan_pwm1
.
attr
,
&
dev_attr_fan_fan1_input
.
attr
,
NULL
};
static
const
struct
attribute_group
fan_attr_group
=
{
.
attrs
=
fan_attributes
,
};
static
int
__init
fan_init
(
struct
ibm_init_struct
*
iibm
)
{
int
rc
;
vdbg_printk
(
TPACPI_DBG_INIT
,
"initializing fan subdriver
\n
"
);
mutex_init
(
&
fan_mutex
);
fan_status_access_mode
=
TPACPI_FAN_NONE
;
fan_control_access_mode
=
TPACPI_FAN_WR_NONE
;
fan_control_commands
=
0
;
fan_watchdog_maxinterval
=
0
;
tp_features
.
fan_ctrl_status_undef
=
0
;
fan_control_desired_level
=
7
;
TPACPI_ACPIHANDLE_INIT
(
fans
);
TPACPI_ACPIHANDLE_INIT
(
gfan
);
TPACPI_ACPIHANDLE_INIT
(
sfan
);
if
(
gfan_handle
)
{
/* 570, 600e/x, 770e, 770x */
fan_status_access_mode
=
TPACPI_FAN_RD_ACPI_GFAN
;
}
else
{
/* all other ThinkPads: note that even old-style
* ThinkPad ECs supports the fan control register */
if
(
likely
(
acpi_ec_read
(
fan_status_offset
,
&
fan_control_initial_status
)))
{
fan_status_access_mode
=
TPACPI_FAN_RD_TPEC
;
/* In some ThinkPads, neither the EC nor the ACPI
* DSDT initialize the fan status, and it ends up
* being set to 0x07 when it *could* be either
* 0x07 or 0x80.
*
* Enable for TP-1Y (T43), TP-78 (R51e),
* TP-76 (R52), TP-70 (T43, R52), which are known
* to be buggy. */
if
(
fan_control_initial_status
==
0x07
)
{
switch
(
thinkpad_id
.
ec_model
)
{
case
0x5931
:
/* TP-1Y */
case
0x3837
:
/* TP-78 */
case
0x3637
:
/* TP-76 */
case
0x3037
:
/* TP-70 */
printk
(
TPACPI_NOTICE
"fan_init: initial fan status "
"is unknown, assuming it is "
"in auto mode
\n
"
);
tp_features
.
fan_ctrl_status_undef
=
1
;
;;
}
}
}
else
{
printk
(
TPACPI_ERR
"ThinkPad ACPI EC access misbehaving, "
"fan status and control unavailable
\n
"
);
return
1
;
}
break
;
}
case
TPACPI_FAN_WR_ACPI_SFAN
:
if
(
!
acpi_evalf
(
sfan_handle
,
NULL
,
NULL
,
"vd"
,
0x00
))
rc
=
-
EIO
;
else
fan_control_desired_level
=
0
;
break
;
if
(
sfan_handle
)
{
/* 570, 770x-JL */
fan_control_access_mode
=
TPACPI_FAN_WR_ACPI_SFAN
;
fan_control_commands
|=
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
else
{
if
(
!
gfan_handle
)
{
/* gfan without sfan means no fan control */
/* all other models implement TP EC 0x2f control */
default:
rc
=
-
ENXIO
;
if
(
fans_handle
)
{
/* X31, X40, X41 */
fan_control_access_mode
=
TPACPI_FAN_WR_ACPI_FANS
;
fan_control_commands
|=
TPACPI_FAN_CMD_SPEED
|
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
else
{
fan_control_access_mode
=
TPACPI_FAN_WR_TPEC
;
fan_control_commands
|=
TPACPI_FAN_CMD_LEVEL
|
TPACPI_FAN_CMD_ENABLE
;
}
}
}
vdbg_printk
(
TPACPI_DBG_INIT
,
"fan is %s, modes %d, %d
\n
"
,
str_supported
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
||
fan_control_access_mode
!=
TPACPI_FAN_WR_NONE
),
fan_status_access_mode
,
fan_control_access_mode
);
/* fan control master switch */
if
(
!
fan_control_allowed
)
{
fan_control_access_mode
=
TPACPI_FAN_WR_NONE
;
fan_control_commands
=
0
;
dbg_printk
(
TPACPI_DBG_INIT
,
"fan control features disabled by parameter
\n
"
);
}
/* update fan_control_desired_level */
if
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
)
fan_get_status_safe
(
NULL
);
mutex_unlock
(
&
fan_mutex
);
return
rc
;
if
(
fan_status_access_mode
!=
TPACPI_FAN_NONE
||
fan_control_access_mode
!=
TPACPI_FAN_WR_NONE
)
{
rc
=
sysfs_create_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
fan_attr_group
);
if
(
!
(
rc
<
0
))
rc
=
driver_create_file
(
&
tpacpi_hwmon_pdriver
.
driver
,
&
driver_attr_fan_watchdog
);
if
(
rc
<
0
)
return
rc
;
return
0
;
}
else
return
1
;
}
static
int
fan_set_speed
(
int
spee
d
)
static
void
fan_exit
(
voi
d
)
{
int
rc
;
if
(
!
fan_control_allowed
)
return
-
EPERM
;
if
(
mutex_lock_interruptible
(
&
fan_mutex
))
return
-
ERESTARTSYS
;
rc
=
0
;
switch
(
fan_control_access_mode
)
{
case
TPACPI_FAN_WR_ACPI_FANS
:
if
(
speed
>=
0
&&
speed
<=
65535
)
{
if
(
!
acpi_evalf
(
fans_handle
,
NULL
,
NULL
,
"vddd"
,
speed
,
speed
,
speed
))
rc
=
-
EIO
;
}
else
rc
=
-
EINVAL
;
break
;
vdbg_printk
(
TPACPI_DBG_EXIT
,
"cancelling any pending fan watchdog tasks
\n
"
);
default:
rc
=
-
ENXIO
;
}
/* FIXME: can we really do this unconditionally? */
sysfs_remove_group
(
&
tpacpi_sensors_pdev
->
dev
.
kobj
,
&
fan_attr_group
);
driver_remove_file
(
&
tpacpi_hwmon_pdriver
.
driver
,
&
driver_attr_fan_watchdog
);
mutex_unlock
(
&
fan_mutex
);
return
rc
;
cancel_delayed_work
(
&
fan_watchdog_task
);
flush_scheduled_work
()
;
}
static
int
fan_read
(
char
*
p
)
...
...
@@ -4307,7 +5349,8 @@ static int fan_read(char *p)
switch
(
fan_status_access_mode
)
{
case
TPACPI_FAN_RD_ACPI_GFAN
:
/* 570, 600e/x, 770e, 770x */
if
((
rc
=
fan_get_status_safe
(
&
status
))
<
0
)
rc
=
fan_get_status_safe
(
&
status
);
if
(
rc
<
0
)
return
rc
;
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
...
...
@@ -4317,7 +5360,8 @@ static int fan_read(char *p)
case
TPACPI_FAN_RD_TPEC
:
/* all except 570, 600e/x, 770e, 770x */
if
((
rc
=
fan_get_status_safe
(
&
status
))
<
0
)
rc
=
fan_get_status_safe
(
&
status
);
if
(
rc
<
0
)
return
rc
;
if
(
unlikely
(
tp_features
.
fan_ctrl_status_undef
))
{
...
...
@@ -4332,7 +5376,8 @@ static int fan_read(char *p)
len
+=
sprintf
(
p
+
len
,
"status:
\t\t
%s
\n
"
,
(
status
!=
0
)
?
"enabled"
:
"disabled"
);
if
((
rc
=
fan_get_speed
(
&
speed
))
<
0
)
rc
=
fan_get_speed
(
&
speed
);
if
(
rc
<
0
)
return
rc
;
len
+=
sprintf
(
p
+
len
,
"speed:
\t\t
%d
\n
"
,
speed
);
...
...
@@ -4368,8 +5413,8 @@ static int fan_read(char *p)
if
(
fan_control_commands
&
TPACPI_FAN_CMD_ENABLE
)
len
+=
sprintf
(
p
+
len
,
"commands:
\t
enable, disable
\n
"
"commands:
\t
watchdog <timeout> (<timeout>
is 0 (off),
"
"1-120 (seconds))
\n
"
);
"commands:
\t
watchdog <timeout> (<timeout> "
"
is 0 (off),
1-120 (seconds))
\n
"
);
if
(
fan_control_commands
&
TPACPI_FAN_CMD_SPEED
)
len
+=
sprintf
(
p
+
len
,
"commands:
\t
speed <speed>"
...
...
@@ -4385,13 +5430,14 @@ static int fan_write_cmd_level(const char *cmd, int *rc)
if
(
strlencmp
(
cmd
,
"level auto"
)
==
0
)
level
=
TP_EC_FAN_AUTO
;
else
if
((
strlencmp
(
cmd
,
"level disengaged"
)
==
0
)
|
(
strlencmp
(
cmd
,
"level full-speed"
)
==
0
))
(
strlencmp
(
cmd
,
"level full-speed"
)
==
0
))
level
=
TP_EC_FAN_FULLSPEED
;
else
if
(
sscanf
(
cmd
,
"level %d"
,
&
level
)
!=
1
)
return
0
;
if
((
*
rc
=
fan_set_level_safe
(
level
))
==
-
ENXIO
)
printk
(
IBM_ERR
"level command accepted for unsupported "
*
rc
=
fan_set_level_safe
(
level
);
if
(
*
rc
==
-
ENXIO
)
printk
(
TPACPI_ERR
"level command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
...
...
@@ -4402,8 +5448,9 @@ static int fan_write_cmd_enable(const char *cmd, int *rc)
if
(
strlencmp
(
cmd
,
"enable"
)
!=
0
)
return
0
;
if
((
*
rc
=
fan_set_enable
())
==
-
ENXIO
)
printk
(
IBM_ERR
"enable command accepted for unsupported "
*
rc
=
fan_set_enable
();
if
(
*
rc
==
-
ENXIO
)
printk
(
TPACPI_ERR
"enable command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
...
...
@@ -4414,8 +5461,9 @@ static int fan_write_cmd_disable(const char *cmd, int *rc)
if
(
strlencmp
(
cmd
,
"disable"
)
!=
0
)
return
0
;
if
((
*
rc
=
fan_set_disable
())
==
-
ENXIO
)
printk
(
IBM_ERR
"disable command accepted for unsupported "
*
rc
=
fan_set_disable
();
if
(
*
rc
==
-
ENXIO
)
printk
(
TPACPI_ERR
"disable command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
...
...
@@ -4431,8 +5479,9 @@ static int fan_write_cmd_speed(const char *cmd, int *rc)
if
(
sscanf
(
cmd
,
"speed %d"
,
&
speed
)
!=
1
)
return
0
;
if
((
*
rc
=
fan_set_speed
(
speed
))
==
-
ENXIO
)
printk
(
IBM_ERR
"speed command accepted for unsupported "
*
rc
=
fan_set_speed
(
speed
);
if
(
*
rc
==
-
ENXIO
)
printk
(
TPACPI_ERR
"speed command accepted for unsupported "
"access mode %d"
,
fan_control_access_mode
);
return
1
;
...
...
@@ -4496,7 +5545,7 @@ static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
struct
device_attribute
*
attr
,
char
*
buf
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
IBM
_NAME
);
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
TPACPI
_NAME
);
}
static
struct
device_attribute
dev_attr_thinkpad_acpi_pdev_name
=
...
...
@@ -4507,14 +5556,12 @@ static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
/* /proc support */
static
struct
proc_dir_entry
*
proc_dir
;
/* Subdriver registry */
static
LIST_HEAD
(
tpacpi_all_drivers
);
/*
* Module and infrastructure proble, init and exit handling
*/
static
int
force_load
;
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
static
const
char
*
__init
str_supported
(
int
is_supported
)
{
...
...
@@ -4524,6 +5571,48 @@ static const char * __init str_supported(int is_supported)
}
#endif
/* CONFIG_THINKPAD_ACPI_DEBUG */
static
void
ibm_exit
(
struct
ibm_struct
*
ibm
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"removing %s
\n
"
,
ibm
->
name
);
list_del_init
(
&
ibm
->
all_drivers
);
if
(
ibm
->
flags
.
acpi_notify_installed
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: acpi_remove_notify_handler
\n
"
,
ibm
->
name
);
BUG_ON
(
!
ibm
->
acpi
);
acpi_remove_notify_handler
(
*
ibm
->
acpi
->
handle
,
ibm
->
acpi
->
type
,
dispatch_acpi_notify
);
ibm
->
flags
.
acpi_notify_installed
=
0
;
ibm
->
flags
.
acpi_notify_installed
=
0
;
}
if
(
ibm
->
flags
.
proc_created
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: remove_proc_entry
\n
"
,
ibm
->
name
);
remove_proc_entry
(
ibm
->
name
,
proc_dir
);
ibm
->
flags
.
proc_created
=
0
;
}
if
(
ibm
->
flags
.
acpi_driver_registered
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: acpi_bus_unregister_driver
\n
"
,
ibm
->
name
);
BUG_ON
(
!
ibm
->
acpi
);
acpi_bus_unregister_driver
(
ibm
->
acpi
->
driver
);
kfree
(
ibm
->
acpi
->
driver
);
ibm
->
acpi
->
driver
=
NULL
;
ibm
->
flags
.
acpi_driver_registered
=
0
;
}
if
(
ibm
->
flags
.
init_called
&&
ibm
->
exit
)
{
ibm
->
exit
();
ibm
->
flags
.
init_called
=
0
;
}
dbg_printk
(
TPACPI_DBG_INIT
,
"finished removing %s
\n
"
,
ibm
->
name
);
}
static
int
__init
ibm_init
(
struct
ibm_init_struct
*
iibm
)
{
int
ret
;
...
...
@@ -4560,7 +5649,7 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
if
(
ibm
->
acpi
->
notify
)
{
ret
=
setup_acpi_notify
(
ibm
);
if
(
ret
==
-
ENODEV
)
{
printk
(
IBM
_NOTICE
"disabling subdriver %s
\n
"
,
printk
(
TPACPI
_NOTICE
"disabling subdriver %s
\n
"
,
ibm
->
name
);
ret
=
0
;
goto
err_out
;
...
...
@@ -4578,7 +5667,7 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
S_IFREG
|
S_IRUGO
|
S_IWUSR
,
proc_dir
);
if
(
!
entry
)
{
printk
(
IBM
_ERR
"unable to create proc entry %s
\n
"
,
printk
(
TPACPI
_ERR
"unable to create proc entry %s
\n
"
,
ibm
->
name
);
ret
=
-
ENODEV
;
goto
err_out
;
...
...
@@ -4604,48 +5693,6 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
return
(
ret
<
0
)
?
ret
:
0
;
}
static
void
ibm_exit
(
struct
ibm_struct
*
ibm
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"removing %s
\n
"
,
ibm
->
name
);
list_del_init
(
&
ibm
->
all_drivers
);
if
(
ibm
->
flags
.
acpi_notify_installed
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: acpi_remove_notify_handler
\n
"
,
ibm
->
name
);
BUG_ON
(
!
ibm
->
acpi
);
acpi_remove_notify_handler
(
*
ibm
->
acpi
->
handle
,
ibm
->
acpi
->
type
,
dispatch_acpi_notify
);
ibm
->
flags
.
acpi_notify_installed
=
0
;
ibm
->
flags
.
acpi_notify_installed
=
0
;
}
if
(
ibm
->
flags
.
proc_created
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: remove_proc_entry
\n
"
,
ibm
->
name
);
remove_proc_entry
(
ibm
->
name
,
proc_dir
);
ibm
->
flags
.
proc_created
=
0
;
}
if
(
ibm
->
flags
.
acpi_driver_registered
)
{
dbg_printk
(
TPACPI_DBG_EXIT
,
"%s: acpi_bus_unregister_driver
\n
"
,
ibm
->
name
);
BUG_ON
(
!
ibm
->
acpi
);
acpi_bus_unregister_driver
(
ibm
->
acpi
->
driver
);
kfree
(
ibm
->
acpi
->
driver
);
ibm
->
acpi
->
driver
=
NULL
;
ibm
->
flags
.
acpi_driver_registered
=
0
;
}
if
(
ibm
->
flags
.
init_called
&&
ibm
->
exit
)
{
ibm
->
exit
();
ibm
->
flags
.
init_called
=
0
;
}
dbg_printk
(
TPACPI_DBG_INIT
,
"finished removing %s
\n
"
,
ibm
->
name
);
}
/* Probing */
static
void
__init
get_thinkpad_model_data
(
struct
thinkpad_id_data
*
tp
)
...
...
@@ -4715,10 +5762,10 @@ static int __init probe_for_thinkpad(void)
is_thinkpad
=
(
thinkpad_id
.
model_str
!=
NULL
);
/* ec is required because many other handles are relative to it */
IBM
_ACPIHANDLE_INIT
(
ec
);
TPACPI
_ACPIHANDLE_INIT
(
ec
);
if
(
!
ec_handle
)
{
if
(
is_thinkpad
)
printk
(
IBM
_ERR
printk
(
TPACPI
_ERR
"Not yet supported ThinkPad detected!
\n
"
);
return
-
ENODEV
;
}
...
...
@@ -4839,47 +5886,110 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp)
return
-
EINVAL
;
}
static
int
experimental
;
module_param
(
experimental
,
int
,
0
);
MODULE_PARM_DESC
(
experimental
,
"Enables experimental features when non-zero"
);
static
u32
dbg_level
;
module_param_named
(
debug
,
dbg_level
,
uint
,
0
);
MODULE_PARM_DESC
(
debug
,
"Sets debug level bit-mask"
);
static
int
force_load
;
module_param
(
force_load
,
bool
,
0
);
MODULE_PARM_DESC
(
force_load
,
"Attempts to load the driver even on a "
"mis-identified ThinkPad when true"
);
static
int
fan_control_allowed
;
module_param_named
(
fan_control
,
fan_control_allowed
,
bool
,
0
);
MODULE_PARM_DESC
(
fan_control
,
"Enables setting fan parameters features when true"
);
static
int
brightness_mode
;
module_param_named
(
brightness_mode
,
brightness_mode
,
int
,
0
);
MODULE_PARM_DESC
(
brightness_mode
,
"Selects brightness control strategy: "
"0=auto, 1=EC, 2=CMOS, 3=both"
);
static
unsigned
int
brightness_enable
=
2
;
/* 2 = auto, 0 = no, 1 = yes */
module_param
(
brightness_enable
,
uint
,
0
);
MODULE_PARM_DESC
(
brightness_enable
,
"Enables backlight control when 1, disables when 0"
);
static
unsigned
int
hotkey_report_mode
;
module_param
(
hotkey_report_mode
,
uint
,
0
);
#define IBM_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0)
IBM_PARAM
(
hotkey
);
IBM_PARAM
(
bluetooth
);
IBM_PARAM
(
video
);
IBM_PARAM
(
light
);
MODULE_PARM_DESC
(
hotkey_report_mode
,
"used for backwards compatibility with userspace, "
"see documentation"
);
#define TPACPI_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
MODULE_PARM_DESC(feature, "Simulates thinkpad-aci procfs command " \
"at module load, see documentation")
TPACPI_PARAM
(
hotkey
);
TPACPI_PARAM
(
bluetooth
);
TPACPI_PARAM
(
video
);
TPACPI_PARAM
(
light
);
#ifdef CONFIG_THINKPAD_ACPI_DOCK
IBM
_PARAM
(
dock
);
TPACPI
_PARAM
(
dock
);
#endif
#ifdef CONFIG_THINKPAD_ACPI_BAY
IBM
_PARAM
(
bay
);
TPACPI
_PARAM
(
bay
);
#endif
/* CONFIG_THINKPAD_ACPI_BAY */
IBM_PARAM
(
cmos
);
IBM_PARAM
(
led
);
IBM_PARAM
(
beep
);
IBM_PARAM
(
ecdump
);
IBM_PARAM
(
brightness
);
IBM_PARAM
(
volume
);
IBM_PARAM
(
fan
);
TPACPI_PARAM
(
cmos
);
TPACPI_PARAM
(
led
);
TPACPI_PARAM
(
beep
);
TPACPI_PARAM
(
ecdump
);
TPACPI_PARAM
(
brightness
);
TPACPI_PARAM
(
volume
);
TPACPI_PARAM
(
fan
);
static
void
thinkpad_acpi_module_exit
(
void
)
{
struct
ibm_struct
*
ibm
,
*
itmp
;
tpacpi_lifecycle
=
TPACPI_LIFE_EXITING
;
list_for_each_entry_safe_reverse
(
ibm
,
itmp
,
&
tpacpi_all_drivers
,
all_drivers
)
{
ibm_exit
(
ibm
);
}
dbg_printk
(
TPACPI_DBG_INIT
,
"finished subdriver exit path...
\n
"
);
if
(
tpacpi_inputdev
)
{
if
(
tp_features
.
input_device_registered
)
input_unregister_device
(
tpacpi_inputdev
);
else
input_free_device
(
tpacpi_inputdev
);
}
if
(
tpacpi_hwmon
)
hwmon_device_unregister
(
tpacpi_hwmon
);
if
(
tp_features
.
sensors_pdev_attrs_registered
)
device_remove_file
(
&
tpacpi_sensors_pdev
->
dev
,
&
dev_attr_thinkpad_acpi_pdev_name
);
if
(
tpacpi_sensors_pdev
)
platform_device_unregister
(
tpacpi_sensors_pdev
);
if
(
tpacpi_pdev
)
platform_device_unregister
(
tpacpi_pdev
);
if
(
tp_features
.
sensors_pdrv_attrs_registered
)
tpacpi_remove_driver_attributes
(
&
tpacpi_hwmon_pdriver
.
driver
);
if
(
tp_features
.
platform_drv_attrs_registered
)
tpacpi_remove_driver_attributes
(
&
tpacpi_pdriver
.
driver
);
if
(
tp_features
.
sensors_pdrv_registered
)
platform_driver_unregister
(
&
tpacpi_hwmon_pdriver
);
if
(
tp_features
.
platform_drv_registered
)
platform_driver_unregister
(
&
tpacpi_pdriver
);
if
(
proc_dir
)
remove_proc_entry
(
TPACPI_PROC_DIR
,
acpi_root_dir
);
kfree
(
thinkpad_id
.
bios_version_str
);
kfree
(
thinkpad_id
.
ec_version_str
);
kfree
(
thinkpad_id
.
model_str
);
}
static
int
__init
thinkpad_acpi_module_init
(
void
)
{
...
...
@@ -4902,12 +6012,13 @@ static int __init thinkpad_acpi_module_init(void)
/* Driver initialization */
IBM
_ACPIHANDLE_INIT
(
ecrd
);
IBM
_ACPIHANDLE_INIT
(
ecwr
);
TPACPI
_ACPIHANDLE_INIT
(
ecrd
);
TPACPI
_ACPIHANDLE_INIT
(
ecwr
);
proc_dir
=
proc_mkdir
(
IBM
_PROC_DIR
,
acpi_root_dir
);
proc_dir
=
proc_mkdir
(
TPACPI
_PROC_DIR
,
acpi_root_dir
);
if
(
!
proc_dir
)
{
printk
(
IBM_ERR
"unable to create proc dir "
IBM_PROC_DIR
);
printk
(
TPACPI_ERR
"unable to create proc dir "
TPACPI_PROC_DIR
);
thinkpad_acpi_module_exit
();
return
-
ENODEV
;
}
...
...
@@ -4915,7 +6026,8 @@ static int __init thinkpad_acpi_module_init(void)
ret
=
platform_driver_register
(
&
tpacpi_pdriver
);
if
(
ret
)
{
printk
(
IBM_ERR
"unable to register main platform driver
\n
"
);
printk
(
TPACPI_ERR
"unable to register main platform driver
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
...
...
@@ -4923,7 +6035,8 @@ static int __init thinkpad_acpi_module_init(void)
ret
=
platform_driver_register
(
&
tpacpi_hwmon_pdriver
);
if
(
ret
)
{
printk
(
IBM_ERR
"unable to register hwmon platform driver
\n
"
);
printk
(
TPACPI_ERR
"unable to register hwmon platform driver
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
...
...
@@ -4932,10 +6045,12 @@ static int __init thinkpad_acpi_module_init(void)
ret
=
tpacpi_create_driver_attributes
(
&
tpacpi_pdriver
.
driver
);
if
(
!
ret
)
{
tp_features
.
platform_drv_attrs_registered
=
1
;
ret
=
tpacpi_create_driver_attributes
(
&
tpacpi_hwmon_pdriver
.
driver
);
ret
=
tpacpi_create_driver_attributes
(
&
tpacpi_hwmon_pdriver
.
driver
);
}
if
(
ret
)
{
printk
(
IBM_ERR
"unable to create sysfs driver attributes
\n
"
);
printk
(
TPACPI_ERR
"unable to create sysfs driver attributes
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
...
...
@@ -4943,30 +6058,31 @@ static int __init thinkpad_acpi_module_init(void)
/* Device initialization */
tpacpi_pdev
=
platform_device_register_simple
(
IBM
_DRVR_NAME
,
-
1
,
tpacpi_pdev
=
platform_device_register_simple
(
TPACPI
_DRVR_NAME
,
-
1
,
NULL
,
0
);
if
(
IS_ERR
(
tpacpi_pdev
))
{
ret
=
PTR_ERR
(
tpacpi_pdev
);
tpacpi_pdev
=
NULL
;
printk
(
IBM
_ERR
"unable to register platform device
\n
"
);
printk
(
TPACPI
_ERR
"unable to register platform device
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
tpacpi_sensors_pdev
=
platform_device_register_simple
(
IBM
_HWMON_DRVR_NAME
,
-
1
,
NULL
,
0
);
TPACPI
_HWMON_DRVR_NAME
,
-
1
,
NULL
,
0
);
if
(
IS_ERR
(
tpacpi_sensors_pdev
))
{
ret
=
PTR_ERR
(
tpacpi_sensors_pdev
);
tpacpi_sensors_pdev
=
NULL
;
printk
(
IBM_ERR
"unable to register hwmon platform device
\n
"
);
printk
(
TPACPI_ERR
"unable to register hwmon platform device
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
ret
=
device_create_file
(
&
tpacpi_sensors_pdev
->
dev
,
&
dev_attr_thinkpad_acpi_pdev_name
);
if
(
ret
)
{
printk
(
IBM
_ERR
"unable to create sysfs hwmon device attributes
\n
"
);
printk
(
TPACPI
_ERR
"unable to create sysfs hwmon device attributes
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
...
...
@@ -4975,20 +6091,20 @@ static int __init thinkpad_acpi_module_init(void)
if
(
IS_ERR
(
tpacpi_hwmon
))
{
ret
=
PTR_ERR
(
tpacpi_hwmon
);
tpacpi_hwmon
=
NULL
;
printk
(
IBM
_ERR
"unable to register hwmon device
\n
"
);
printk
(
TPACPI
_ERR
"unable to register hwmon device
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
mutex_init
(
&
tpacpi_inputdev_send_mutex
);
tpacpi_inputdev
=
input_allocate_device
();
if
(
!
tpacpi_inputdev
)
{
printk
(
IBM
_ERR
"unable to allocate input device
\n
"
);
printk
(
TPACPI
_ERR
"unable to allocate input device
\n
"
);
thinkpad_acpi_module_exit
();
return
-
ENOMEM
;
}
else
{
/* Prepare input device, but don't register */
tpacpi_inputdev
->
name
=
"ThinkPad Extra Buttons"
;
tpacpi_inputdev
->
phys
=
IBM
_DRVR_NAME
"/input0"
;
tpacpi_inputdev
->
phys
=
TPACPI
_DRVR_NAME
"/input0"
;
tpacpi_inputdev
->
id
.
bustype
=
BUS_HOST
;
tpacpi_inputdev
->
id
.
vendor
=
(
thinkpad_id
.
vendor
)
?
thinkpad_id
.
vendor
:
...
...
@@ -5007,7 +6123,7 @@ static int __init thinkpad_acpi_module_init(void)
}
ret
=
input_register_device
(
tpacpi_inputdev
);
if
(
ret
<
0
)
{
printk
(
IBM
_ERR
"unable to register input device
\n
"
);
printk
(
TPACPI
_ERR
"unable to register input device
\n
"
);
thinkpad_acpi_module_exit
();
return
ret
;
}
else
{
...
...
@@ -5018,56 +6134,36 @@ static int __init thinkpad_acpi_module_init(void)
return
0
;
}
static
void
thinkpad_acpi_module_exit
(
void
)
{
struct
ibm_struct
*
ibm
,
*
itmp
;
tpacpi_lifecycle
=
TPACPI_LIFE_EXITING
;
list_for_each_entry_safe_reverse
(
ibm
,
itmp
,
&
tpacpi_all_drivers
,
all_drivers
)
{
ibm_exit
(
ibm
);
}
dbg_printk
(
TPACPI_DBG_INIT
,
"finished subdriver exit path...
\n
"
);
if
(
tpacpi_inputdev
)
{
if
(
tp_features
.
input_device_registered
)
input_unregister_device
(
tpacpi_inputdev
);
else
input_free_device
(
tpacpi_inputdev
);
}
if
(
tpacpi_hwmon
)
hwmon_device_unregister
(
tpacpi_hwmon
);
if
(
tp_features
.
sensors_pdev_attrs_registered
)
device_remove_file
(
&
tpacpi_sensors_pdev
->
dev
,
&
dev_attr_thinkpad_acpi_pdev_name
);
if
(
tpacpi_sensors_pdev
)
platform_device_unregister
(
tpacpi_sensors_pdev
);
if
(
tpacpi_pdev
)
platform_device_unregister
(
tpacpi_pdev
);
if
(
tp_features
.
sensors_pdrv_attrs_registered
)
tpacpi_remove_driver_attributes
(
&
tpacpi_hwmon_pdriver
.
driver
);
if
(
tp_features
.
platform_drv_attrs_registered
)
tpacpi_remove_driver_attributes
(
&
tpacpi_pdriver
.
driver
);
/* Please remove this in year 2009 */
MODULE_ALIAS
(
"ibm_acpi"
);
if
(
tp_features
.
sensors_pdrv_registered
)
platform_driver_unregister
(
&
tpacpi_hwmon_pdriver
);
/*
* DMI matching for module autoloading
*
* See http://thinkwiki.org/wiki/List_of_DMI_IDs
* See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
*
* Only models listed in thinkwiki will be supported, so add yours
* if it is not there yet.
*/
#define IBM_BIOS_MODULE_ALIAS(__type) \
MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
if
(
tp_features
.
platform_drv_registered
)
platform_driver_unregister
(
&
tpacpi_pdriver
);
/* Non-ancient thinkpads */
MODULE_ALIAS
(
"dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*"
);
MODULE_ALIAS
(
"dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*"
);
if
(
proc_dir
)
remove_proc_entry
(
IBM_PROC_DIR
,
acpi_root_dir
);
/* Ancient thinkpad BIOSes have to be identified by
* BIOS type or model number, and there are far less
* BIOS types than model numbers... */
IBM_BIOS_MODULE_ALIAS
(
"I[B,D,H,I,M,N,O,T,W,V,Y,Z]"
);
IBM_BIOS_MODULE_ALIAS
(
"1[0,3,6,8,A-G,I,K,M-P,S,T]"
);
IBM_BIOS_MODULE_ALIAS
(
"K[U,X-Z]"
);
kfree
(
thinkpad_id
.
bios_version_str
);
kfree
(
thinkpad_id
.
ec_version_str
);
kfree
(
thinkpad_id
.
model_str
);
}
MODULE_AUTHOR
(
"Borislav Deianov, Henrique de Moraes Holschuh"
);
MODULE_DESCRIPTION
(
TPACPI_DESC
);
MODULE_VERSION
(
TPACPI_VERSION
);
MODULE_LICENSE
(
"GPL"
);
module_init
(
thinkpad_acpi_module_init
);
module_exit
(
thinkpad_acpi_module_exit
);
drivers/misc/thinkpad_acpi.h
已删除
100644 → 0
浏览文件 @
877c357e
/*
* thinkpad_acpi.h - ThinkPad ACPI Extras
*
*
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
* Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __THINKPAD_ACPI_H__
#define __THINKPAD_ACPI_H__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/nvram.h>
#include <linux/proc_fs.h>
#include <linux/sysfs.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
#include <linux/pci_ids.h>
/****************************************************************************
* Main driver
*/
#define IBM_NAME "thinkpad"
#define IBM_DESC "ThinkPad ACPI Extras"
#define IBM_FILE IBM_NAME "_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"
#define IBM_PROC_DIR "ibm"
#define IBM_ACPI_EVENT_PREFIX "ibm"
#define IBM_DRVR_NAME IBM_FILE
#define IBM_HWMON_DRVR_NAME IBM_NAME "_hwmon"
#define IBM_LOG IBM_FILE ": "
#define IBM_ERR KERN_ERR IBM_LOG
#define IBM_NOTICE KERN_NOTICE IBM_LOG
#define IBM_INFO KERN_INFO IBM_LOG
#define IBM_DEBUG KERN_DEBUG IBM_LOG
#define IBM_MAX_ACPI_ARGS 3
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
#define TP_CMOS_VOLUME_UP 1
#define TP_CMOS_VOLUME_MUTE 2
#define TP_CMOS_BRIGHTNESS_UP 4
#define TP_CMOS_BRIGHTNESS_DOWN 5
/* ThinkPad CMOS NVRAM constants */
#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e
#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x0f
#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
/* Debugging */
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_INIT 0x0001
#define TPACPI_DBG_EXIT 0x0002
#define dbg_printk(a_dbg_level, format, arg...) \
do { if (dbg_level & a_dbg_level) \
printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0)
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
dbg_printk(a_dbg_level, format, ## arg)
static
const
char
*
str_supported
(
int
is_supported
);
#else
#define vdbg_printk(a_dbg_level, format, arg...)
#endif
/* Input IDs */
#define TPACPI_HKEY_INPUT_VENDOR PCI_VENDOR_ID_IBM
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054
/* "TP" */
#define TPACPI_HKEY_INPUT_VERSION 0x4101
/* ACPI HIDs */
#define IBM_HKEY_HID "IBM0068"
/* ACPI helpers */
static
int
__must_check
acpi_evalf
(
acpi_handle
handle
,
void
*
res
,
char
*
method
,
char
*
fmt
,
...);
static
int
__must_check
acpi_ec_read
(
int
i
,
u8
*
p
);
static
int
__must_check
acpi_ec_write
(
int
i
,
u8
v
);
static
int
__must_check
_sta
(
acpi_handle
handle
);
/* ACPI handles */
static
acpi_handle
root_handle
;
/* root namespace */
static
acpi_handle
ec_handle
;
/* EC */
static
acpi_handle
ecrd_handle
,
ecwr_handle
;
/* 570 EC access */
static
acpi_handle
cmos_handle
,
hkey_handle
;
/* basic thinkpad handles */
static
void
drv_acpi_handle_init
(
char
*
name
,
acpi_handle
*
handle
,
acpi_handle
parent
,
char
**
paths
,
int
num_paths
,
char
**
path
);
#define IBM_ACPIHANDLE_INIT(object) \
drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
/* ThinkPad ACPI helpers */
static
int
issue_thinkpad_cmos_command
(
int
cmos_cmd
);
/* procfs support */
static
struct
proc_dir_entry
*
proc_dir
;
/* procfs helpers */
static
int
dispatch_procfs_read
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
);
static
int
dispatch_procfs_write
(
struct
file
*
file
,
const
char
__user
*
userbuf
,
unsigned
long
count
,
void
*
data
);
static
char
*
next_cmd
(
char
**
cmds
);
/* sysfs support */
struct
attribute_set
{
unsigned
int
members
,
max_members
;
struct
attribute_group
group
;
};
static
struct
attribute_set
*
create_attr_set
(
unsigned
int
max_members
,
const
char
*
name
);
#define destroy_attr_set(_set) \
kfree(_set);
static
int
add_to_attr_set
(
struct
attribute_set
*
s
,
struct
attribute
*
attr
);
static
int
add_many_to_attr_set
(
struct
attribute_set
*
s
,
struct
attribute
**
attr
,
unsigned
int
count
);
#define register_attr_set_with_sysfs(_attr_set, _kobj) \
sysfs_create_group(_kobj, &_attr_set->group)
static
void
delete_attr_set
(
struct
attribute_set
*
s
,
struct
kobject
*
kobj
);
static
int
parse_strtoul
(
const
char
*
buf
,
unsigned
long
max
,
unsigned
long
*
value
);
/* Device model */
static
struct
platform_device
*
tpacpi_pdev
;
static
struct
platform_device
*
tpacpi_sensors_pdev
;
static
struct
device
*
tpacpi_hwmon
;
static
struct
platform_driver
tpacpi_pdriver
;
static
struct
input_dev
*
tpacpi_inputdev
;
static
int
tpacpi_create_driver_attributes
(
struct
device_driver
*
drv
);
static
void
tpacpi_remove_driver_attributes
(
struct
device_driver
*
drv
);
/* Module */
static
int
experimental
;
static
u32
dbg_level
;
static
int
force_load
;
static
unsigned
int
hotkey_report_mode
;
static
int
thinkpad_acpi_module_init
(
void
);
static
void
thinkpad_acpi_module_exit
(
void
);
/****************************************************************************
* Subdrivers
*/
struct
ibm_struct
;
struct
tp_acpi_drv_struct
{
const
struct
acpi_device_id
*
hid
;
struct
acpi_driver
*
driver
;
void
(
*
notify
)
(
struct
ibm_struct
*
,
u32
);
acpi_handle
*
handle
;
u32
type
;
struct
acpi_device
*
device
;
};
struct
ibm_struct
{
char
*
name
;
int
(
*
read
)
(
char
*
);
int
(
*
write
)
(
char
*
);
void
(
*
exit
)
(
void
);
void
(
*
resume
)
(
void
);
struct
list_head
all_drivers
;
struct
tp_acpi_drv_struct
*
acpi
;
struct
{
u8
acpi_driver_registered
:
1
;
u8
acpi_notify_installed
:
1
;
u8
proc_created
:
1
;
u8
init_called
:
1
;
u8
experimental
:
1
;
}
flags
;
};
struct
ibm_init_struct
{
char
param
[
32
];
int
(
*
init
)
(
struct
ibm_init_struct
*
);
struct
ibm_struct
*
data
;
};
static
struct
{
#ifdef CONFIG_THINKPAD_ACPI_BAY
u32
bay_status
:
1
;
u32
bay_eject
:
1
;
u32
bay_status2
:
1
;
u32
bay_eject2
:
1
;
#endif
u32
bluetooth
:
1
;
u32
hotkey
:
1
;
u32
hotkey_mask
:
1
;
u32
hotkey_wlsw
:
1
;
u32
light
:
1
;
u32
light_status
:
1
;
u32
bright_16levels
:
1
;
u32
wan
:
1
;
u32
fan_ctrl_status_undef
:
1
;
u32
input_device_registered
:
1
;
u32
platform_drv_registered
:
1
;
u32
platform_drv_attrs_registered
:
1
;
u32
sensors_pdrv_registered
:
1
;
u32
sensors_pdrv_attrs_registered
:
1
;
u32
sensors_pdev_attrs_registered
:
1
;
}
tp_features
;
struct
thinkpad_id_data
{
unsigned
int
vendor
;
/* ThinkPad vendor:
* PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
char
*
bios_version_str
;
/* Something like 1ZET51WW (1.03z) */
char
*
ec_version_str
;
/* Something like 1ZHT51WW-1.04a */
u16
bios_model
;
/* Big Endian, TP-1Y = 0x5931, 0 = unknown */
u16
ec_model
;
char
*
model_str
;
};
static
struct
thinkpad_id_data
thinkpad_id
;
static
struct
list_head
tpacpi_all_drivers
;
static
struct
ibm_init_struct
ibms_init
[];
static
int
set_ibm_param
(
const
char
*
val
,
struct
kernel_param
*
kp
);
static
int
ibm_init
(
struct
ibm_init_struct
*
iibm
);
static
void
ibm_exit
(
struct
ibm_struct
*
ibm
);
/*
* procfs master subdriver
*/
static
int
thinkpad_acpi_driver_init
(
struct
ibm_init_struct
*
iibm
);
static
int
thinkpad_acpi_driver_read
(
char
*
p
);
/*
* Bay subdriver
*/
#ifdef CONFIG_THINKPAD_ACPI_BAY
static
acpi_handle
bay_handle
,
bay_ej_handle
;
static
acpi_handle
bay2_handle
,
bay2_ej_handle
;
static
int
bay_init
(
struct
ibm_init_struct
*
iibm
);
static
void
bay_notify
(
struct
ibm_struct
*
ibm
,
u32
event
);
static
int
bay_read
(
char
*
p
);
static
int
bay_write
(
char
*
buf
);
#endif
/* CONFIG_THINKPAD_ACPI_BAY */
/*
* Beep subdriver
*/
static
acpi_handle
beep_handle
;
static
int
beep_read
(
char
*
p
);
static
int
beep_write
(
char
*
buf
);
/*
* Bluetooth subdriver
*/
enum
{
/* ACPI GBDC/SBDC bits */
TP_ACPI_BLUETOOTH_HWPRESENT
=
0x01
,
/* Bluetooth hw available */
TP_ACPI_BLUETOOTH_RADIOSSW
=
0x02
,
/* Bluetooth radio enabled */
TP_ACPI_BLUETOOTH_UNK
=
0x04
,
/* unknown function */
};
static
int
bluetooth_init
(
struct
ibm_init_struct
*
iibm
);
static
int
bluetooth_get_radiosw
(
void
);
static
int
bluetooth_set_radiosw
(
int
radio_on
);
static
int
bluetooth_read
(
char
*
p
);
static
int
bluetooth_write
(
char
*
buf
);
/*
* Brightness (backlight) subdriver
*/
#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
static
struct
backlight_device
*
ibm_backlight_device
;
static
int
brightness_offset
=
0x31
;
static
int
brightness_mode
;
static
unsigned
int
brightness_enable
;
/* 0 = no, 1 = yes, 2 = auto */
static
int
brightness_init
(
struct
ibm_init_struct
*
iibm
);
static
void
brightness_exit
(
void
);
static
int
brightness_get
(
struct
backlight_device
*
bd
);
static
int
brightness_set
(
int
value
);
static
int
brightness_update_status
(
struct
backlight_device
*
bd
);
static
int
brightness_read
(
char
*
p
);
static
int
brightness_write
(
char
*
buf
);
/*
* CMOS subdriver
*/
static
int
cmos_read
(
char
*
p
);
static
int
cmos_write
(
char
*
buf
);
/*
* Dock subdriver
*/
#ifdef CONFIG_THINKPAD_ACPI_DOCK
static
acpi_handle
pci_handle
;
static
acpi_handle
dock_handle
;
static
void
dock_notify
(
struct
ibm_struct
*
ibm
,
u32
event
);
static
int
dock_read
(
char
*
p
);
static
int
dock_write
(
char
*
buf
);
#endif
/* CONFIG_THINKPAD_ACPI_DOCK */
/*
* EC dump subdriver
*/
static
int
ecdump_read
(
char
*
p
)
;
static
int
ecdump_write
(
char
*
buf
);
/*
* Fan subdriver
*/
enum
{
/* Fan control constants */
fan_status_offset
=
0x2f
,
/* EC register 0x2f */
fan_rpm_offset
=
0x84
,
/* EC register 0x84: LSB, 0x85 MSB (RPM)
* 0x84 must be read before 0x85 */
TP_EC_FAN_FULLSPEED
=
0x40
,
/* EC fan mode: full speed */
TP_EC_FAN_AUTO
=
0x80
,
/* EC fan mode: auto fan control */
TPACPI_FAN_LAST_LEVEL
=
0x100
,
/* Use cached last-seen fan level */
};
enum
fan_status_access_mode
{
TPACPI_FAN_NONE
=
0
,
/* No fan status or control */
TPACPI_FAN_RD_ACPI_GFAN
,
/* Use ACPI GFAN */
TPACPI_FAN_RD_TPEC
,
/* Use ACPI EC regs 0x2f, 0x84-0x85 */
};
enum
fan_control_access_mode
{
TPACPI_FAN_WR_NONE
=
0
,
/* No fan control */
TPACPI_FAN_WR_ACPI_SFAN
,
/* Use ACPI SFAN */
TPACPI_FAN_WR_TPEC
,
/* Use ACPI EC reg 0x2f */
TPACPI_FAN_WR_ACPI_FANS
,
/* Use ACPI FANS and EC reg 0x2f */
};
enum
fan_control_commands
{
TPACPI_FAN_CMD_SPEED
=
0x0001
,
/* speed command */
TPACPI_FAN_CMD_LEVEL
=
0x0002
,
/* level command */
TPACPI_FAN_CMD_ENABLE
=
0x0004
,
/* enable/disable cmd,
* and also watchdog cmd */
};
static
int
fan_control_allowed
;
static
enum
fan_status_access_mode
fan_status_access_mode
;
static
enum
fan_control_access_mode
fan_control_access_mode
;
static
enum
fan_control_commands
fan_control_commands
;
static
u8
fan_control_initial_status
;
static
u8
fan_control_desired_level
;
static
int
fan_watchdog_maxinterval
;
static
struct
mutex
fan_mutex
;
static
acpi_handle
fans_handle
,
gfan_handle
,
sfan_handle
;
static
int
fan_init
(
struct
ibm_init_struct
*
iibm
);
static
void
fan_exit
(
void
);
static
int
fan_get_status
(
u8
*
status
);
static
int
fan_get_status_safe
(
u8
*
status
);
static
int
fan_get_speed
(
unsigned
int
*
speed
);
static
void
fan_update_desired_level
(
u8
status
);
static
void
fan_watchdog_fire
(
struct
work_struct
*
ignored
);
static
void
fan_watchdog_reset
(
void
);
static
int
fan_set_level
(
int
level
);
static
int
fan_set_level_safe
(
int
level
);
static
int
fan_set_enable
(
void
);
static
int
fan_set_disable
(
void
);
static
int
fan_set_speed
(
int
speed
);
static
int
fan_read
(
char
*
p
);
static
int
fan_write
(
char
*
buf
);
static
int
fan_write_cmd_level
(
const
char
*
cmd
,
int
*
rc
);
static
int
fan_write_cmd_enable
(
const
char
*
cmd
,
int
*
rc
);
static
int
fan_write_cmd_disable
(
const
char
*
cmd
,
int
*
rc
);
static
int
fan_write_cmd_speed
(
const
char
*
cmd
,
int
*
rc
);
static
int
fan_write_cmd_watchdog
(
const
char
*
cmd
,
int
*
rc
);
/*
* Hotkey subdriver
*/
static
int
hotkey_orig_status
;
static
u32
hotkey_orig_mask
;
static
struct
mutex
hotkey_mutex
;
static
int
hotkey_init
(
struct
ibm_init_struct
*
iibm
);
static
void
hotkey_exit
(
void
);
static
int
hotkey_get
(
int
*
status
,
u32
*
mask
);
static
int
hotkey_set
(
int
status
,
u32
mask
);
static
void
hotkey_notify
(
struct
ibm_struct
*
ibm
,
u32
event
);
static
int
hotkey_read
(
char
*
p
);
static
int
hotkey_write
(
char
*
buf
);
/*
* LED subdriver
*/
enum
led_access_mode
{
TPACPI_LED_NONE
=
0
,
TPACPI_LED_570
,
/* 570 */
TPACPI_LED_OLD
,
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
TPACPI_LED_NEW
,
/* all others */
};
enum
{
/* For TPACPI_LED_OLD */
TPACPI_LED_EC_HLCL
=
0x0c
,
/* EC reg to get led to power on */
TPACPI_LED_EC_HLBL
=
0x0d
,
/* EC reg to blink a lit led */
TPACPI_LED_EC_HLMS
=
0x0e
,
/* EC reg to select led to command */
};
static
enum
led_access_mode
led_supported
;
static
acpi_handle
led_handle
;
static
int
led_init
(
struct
ibm_init_struct
*
iibm
);
static
int
led_read
(
char
*
p
);
static
int
led_write
(
char
*
buf
);
/*
* Light (thinklight) subdriver
*/
static
acpi_handle
lght_handle
,
ledb_handle
;
static
int
light_init
(
struct
ibm_init_struct
*
iibm
);
static
int
light_read
(
char
*
p
);
static
int
light_write
(
char
*
buf
);
/*
* Thermal subdriver
*/
enum
thermal_access_mode
{
TPACPI_THERMAL_NONE
=
0
,
/* No thermal support */
TPACPI_THERMAL_ACPI_TMP07
,
/* Use ACPI TMP0-7 */
TPACPI_THERMAL_ACPI_UPDT
,
/* Use ACPI TMP0-7 with UPDT */
TPACPI_THERMAL_TPEC_8
,
/* Use ACPI EC regs, 8 sensors */
TPACPI_THERMAL_TPEC_16
,
/* Use ACPI EC regs, 16 sensors */
};
enum
{
/* TPACPI_THERMAL_TPEC_* */
TP_EC_THERMAL_TMP0
=
0x78
,
/* ACPI EC regs TMP 0..7 */
TP_EC_THERMAL_TMP8
=
0xC0
,
/* ACPI EC regs TMP 8..15 */
TP_EC_THERMAL_TMP_NA
=
-
128
,
/* ACPI EC sensor not available */
};
#define TPACPI_MAX_THERMAL_SENSORS 16
/* Max thermal sensors supported */
struct
ibm_thermal_sensors_struct
{
s32
temp
[
TPACPI_MAX_THERMAL_SENSORS
];
};
static
enum
thermal_access_mode
thermal_read_mode
;
static
int
thermal_init
(
struct
ibm_init_struct
*
iibm
);
static
int
thermal_get_sensor
(
int
idx
,
s32
*
value
);
static
int
thermal_get_sensors
(
struct
ibm_thermal_sensors_struct
*
s
);
static
int
thermal_read
(
char
*
p
);
/*
* Video subdriver
*/
enum
video_access_mode
{
TPACPI_VIDEO_NONE
=
0
,
TPACPI_VIDEO_570
,
/* 570 */
TPACPI_VIDEO_770
,
/* 600e/x, 770e, 770x */
TPACPI_VIDEO_NEW
,
/* all others */
};
enum
{
/* video status flags, based on VIDEO_570 */
TP_ACPI_VIDEO_S_LCD
=
0x01
,
/* LCD output enabled */
TP_ACPI_VIDEO_S_CRT
=
0x02
,
/* CRT output enabled */
TP_ACPI_VIDEO_S_DVI
=
0x08
,
/* DVI output enabled */
};
enum
{
/* TPACPI_VIDEO_570 constants */
TP_ACPI_VIDEO_570_PHSCMD
=
0x87
,
/* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHSMASK
=
0x03
,
/* PHS bits that map to
* video_status_flags */
TP_ACPI_VIDEO_570_PHS2CMD
=
0x8b
,
/* unknown magic constant :( */
TP_ACPI_VIDEO_570_PHS2SET
=
0x80
,
/* unknown magic constant :( */
};
static
enum
video_access_mode
video_supported
;
static
int
video_orig_autosw
;
static
acpi_handle
vid_handle
,
vid2_handle
;
static
int
video_init
(
struct
ibm_init_struct
*
iibm
);
static
void
video_exit
(
void
);
static
int
video_outputsw_get
(
void
);
static
int
video_outputsw_set
(
int
status
);
static
int
video_autosw_get
(
void
);
static
int
video_autosw_set
(
int
enable
);
static
int
video_outputsw_cycle
(
void
);
static
int
video_expand_toggle
(
void
);
static
int
video_read
(
char
*
p
);
static
int
video_write
(
char
*
buf
);
/*
* Volume subdriver
*/
static
int
volume_offset
=
0x30
;
static
int
volume_read
(
char
*
p
);
static
int
volume_write
(
char
*
buf
);
/*
* Wan subdriver
*/
enum
{
/* ACPI GWAN/SWAN bits */
TP_ACPI_WANCARD_HWPRESENT
=
0x01
,
/* Wan hw available */
TP_ACPI_WANCARD_RADIOSSW
=
0x02
,
/* Wan radio enabled */
TP_ACPI_WANCARD_UNK
=
0x04
,
/* unknown function */
};
static
int
wan_init
(
struct
ibm_init_struct
*
iibm
);
static
int
wan_get_radiosw
(
void
);
static
int
wan_set_radiosw
(
int
radio_on
);
static
int
wan_read
(
char
*
p
);
static
int
wan_write
(
char
*
buf
);
#endif
/* __THINKPAD_ACPI_H */
include/linux/sonypi.h
浏览文件 @
dd07a8db
...
...
@@ -101,6 +101,8 @@
#define SONYPI_EVENT_FNKEY_RELEASED 59
#define SONYPI_EVENT_WIRELESS_ON 60
#define SONYPI_EVENT_WIRELESS_OFF 61
#define SONYPI_EVENT_ZOOM_IN_PRESSED 62
#define SONYPI_EVENT_ZOOM_OUT_PRESSED 63
/* get/set brightness */
#define SONYPI_IOCGBRT _IOR('v', 0, __u8)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录