mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (36 commits) sony-laptop: support new hotkeys on the P, Z and EC series platform/x86: Consistently select LEDS Kconfig options sony-laptop: fix sparse non-ANSI function warning intel_ips: fix sparse non-ANSI function warning Support KHLB2 in the compal laptop driver acer-wmi: Enabled Acer Launch Manager mode [PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem ACPI Thinkpad: We must always call va_end() after va_start() but do not do so in thinkpad_acpi.c::acpi_evalf() acer-wmi: Initialize wlan/bluetooth/wwan rfkill software block state acer-wmi: Detect the WiFi/Bluetooth/3G devices available acer-wmi: Add 3G rfkill sysfs file acer-wmi: Add acer wmi hotkey events support platform/x86: Kconfig: Replace select by depends on ACPI_WMI ideapad: pass ideapad_priv as argument (part 2) ideapad: pass ideapad_priv as argument (part 1) ideapad: add markups, unify comments and return result when init ideapad: add hotkey support ideapad: let camera power control entry under platform driver ideapad: add platform driver for ideapad fujitsu-laptop: fix compiler warning on pnp_ids ...
This commit is contained in:
commit
443e6221e4
|
@ -0,0 +1,6 @@
|
|||
What: /sys/devices/platform/ideapad/camera_power
|
||||
Date: Dec 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: "Ike Panhc <ike.pan@canonical.com>"
|
||||
Description:
|
||||
Control the power of camera module. 1 means on, 0 means off.
|
|
@ -2271,6 +2271,14 @@ W: http://acpi4asus.sf.net
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/eeepc-laptop.c
|
||||
|
||||
EEEPC WMI EXTRAS DRIVER
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://acpi4asus.sf.net
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/eeepc-wmi.c
|
||||
|
||||
EFIFB FRAMEBUFFER DRIVER
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
M: Peter Jones <pjones@redhat.com>
|
||||
|
|
|
@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES
|
|||
config ACER_WMI
|
||||
tristate "Acer WMI Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on LEDS_CLASS
|
||||
depends on NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select NEW_LEDS
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on SERIO_I8042
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select ACPI_WMI
|
||||
depends on ACPI_WMI
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
This is a driver for newer Acer (and Wistron) laptops. It adds
|
||||
wireless radio and bluetooth control, and on some laptops,
|
||||
|
@ -131,7 +133,7 @@ config TC1100_WMI
|
|||
depends on !X86_64
|
||||
depends on EXPERIMENTAL
|
||||
depends on ACPI
|
||||
select ACPI_WMI
|
||||
depends on ACPI_WMI
|
||||
---help---
|
||||
This is a driver for the WMI extensions (wireless and bluetooth power
|
||||
control) of the HP Compaq TC1100 tablet.
|
||||
|
@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP
|
|||
tristate "Lenovo IdeaPad Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on RFKILL
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
|
||||
|
||||
|
@ -425,7 +428,10 @@ config EEEPC_WMI
|
|||
depends on INPUT
|
||||
depends on EXPERIMENTAL
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_SPARSEKMAP
|
||||
select LEDS_CLASS
|
||||
select NEW_LEDS
|
||||
---help---
|
||||
Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
|
||||
|
||||
|
@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP
|
|||
config ACPI_TOSHIBA
|
||||
tristate "Toshiba Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on LEDS_CLASS
|
||||
depends on NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select NEW_LEDS
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
|
@ -576,6 +582,15 @@ config INTEL_SCU_IPC
|
|||
some embedded Intel x86 platforms. This is not needed for PC-type
|
||||
machines.
|
||||
|
||||
config INTEL_SCU_IPC_UTIL
|
||||
tristate "Intel SCU IPC utility driver"
|
||||
depends on INTEL_SCU_IPC
|
||||
default y
|
||||
---help---
|
||||
The IPC Util driver provides an interface with the SCU enabling
|
||||
low level access for debug work and updating the firmware. Say
|
||||
N unless you will be doing this on an Intel MID platform.
|
||||
|
||||
config GPIO_INTEL_PMIC
|
||||
bool "Intel PMIC GPIO support"
|
||||
depends on INTEL_SCU_IPC && GPIOLIB
|
||||
|
|
|
@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
|
|||
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
||||
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
|
||||
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
||||
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include <acpi/acpi_drivers.h>
|
||||
|
||||
|
@ -48,6 +51,7 @@ MODULE_LICENSE("GPL");
|
|||
#define ACER_ERR KERN_ERR ACER_LOGPREFIX
|
||||
#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
|
||||
#define ACER_INFO KERN_INFO ACER_LOGPREFIX
|
||||
#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX
|
||||
|
||||
/*
|
||||
* Magic Number
|
||||
|
@ -82,9 +86,82 @@ MODULE_LICENSE("GPL");
|
|||
#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
|
||||
#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
|
||||
#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
|
||||
#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
|
||||
|
||||
/*
|
||||
* Acer ACPI event GUIDs
|
||||
*/
|
||||
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
|
||||
|
||||
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
|
||||
MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
|
||||
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
|
||||
|
||||
enum acer_wmi_event_ids {
|
||||
WMID_HOTKEY_EVENT = 0x1,
|
||||
};
|
||||
|
||||
static const struct key_entry acer_wmi_keymap[] = {
|
||||
{KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */
|
||||
{KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */
|
||||
{KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */
|
||||
{KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
|
||||
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
|
||||
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
|
||||
{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
|
||||
{KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */
|
||||
{KE_END, 0}
|
||||
};
|
||||
|
||||
static struct input_dev *acer_wmi_input_dev;
|
||||
|
||||
struct event_return_value {
|
||||
u8 function;
|
||||
u8 key_num;
|
||||
u16 device_state;
|
||||
u32 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* GUID3 Get Device Status device flags
|
||||
*/
|
||||
#define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */
|
||||
#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
|
||||
#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
|
||||
|
||||
struct lm_input_params {
|
||||
u8 function_num; /* Function Number */
|
||||
u16 commun_devices; /* Communication type devices default status */
|
||||
u16 devices; /* Other type devices default status */
|
||||
u8 lm_status; /* Launch Manager Status */
|
||||
u16 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct lm_return_value {
|
||||
u8 error_code; /* Error Code */
|
||||
u8 ec_return_value; /* EC Return Value */
|
||||
u16 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct wmid3_gds_input_param { /* Get Device Status input parameter */
|
||||
u8 function_num; /* Function Number */
|
||||
u8 hotkey_number; /* Hotkey Number */
|
||||
u16 devices; /* Get Device */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct wmid3_gds_return_value { /* Get Device Status return value*/
|
||||
u8 error_code; /* Error Code */
|
||||
u8 ec_return_value; /* EC Return Value */
|
||||
u16 devices; /* Current Device Status */
|
||||
u32 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct hotkey_function_type_aa {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u16 handle;
|
||||
u16 commun_func_bitmap;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Interface capability flags
|
||||
|
@ -116,15 +193,19 @@ static int mailled = -1;
|
|||
static int brightness = -1;
|
||||
static int threeg = -1;
|
||||
static int force_series;
|
||||
static bool ec_raw_mode;
|
||||
static bool has_type_aa;
|
||||
|
||||
module_param(mailled, int, 0444);
|
||||
module_param(brightness, int, 0444);
|
||||
module_param(threeg, int, 0444);
|
||||
module_param(force_series, int, 0444);
|
||||
module_param(ec_raw_mode, bool, 0444);
|
||||
MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
|
||||
MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
|
||||
MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
|
||||
MODULE_PARM_DESC(force_series, "Force a different laptop series");
|
||||
MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
|
||||
|
||||
struct acer_data {
|
||||
int mailled;
|
||||
|
@ -140,6 +221,7 @@ struct acer_debug {
|
|||
|
||||
static struct rfkill *wireless_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *threeg_rfkill;
|
||||
|
||||
/* Each low-level interface must define at least some of the following */
|
||||
struct wmi_interface {
|
||||
|
@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
|
|||
return WMI_execute_u32(method_id, (u32)value, NULL);
|
||||
}
|
||||
|
||||
static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
|
||||
{
|
||||
struct hotkey_function_type_aa *type_aa;
|
||||
|
||||
/* We are looking for OEM-specific Type AAh */
|
||||
if (header->type != 0xAA)
|
||||
return;
|
||||
|
||||
has_type_aa = true;
|
||||
type_aa = (struct hotkey_function_type_aa *) header;
|
||||
|
||||
printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n",
|
||||
type_aa->commun_func_bitmap);
|
||||
|
||||
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
|
||||
interface->capability |= ACER_CAP_WIRELESS;
|
||||
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG)
|
||||
interface->capability |= ACER_CAP_THREEG;
|
||||
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
|
||||
interface->capability |= ACER_CAP_BLUETOOTH;
|
||||
}
|
||||
|
||||
static acpi_status WMID_set_capabilities(void)
|
||||
{
|
||||
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
|
@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void)
|
|||
return AE_ERROR;
|
||||
}
|
||||
|
||||
/* Not sure on the meaning of the relevant bits yet to detect these */
|
||||
interface->capability |= ACER_CAP_WIRELESS;
|
||||
interface->capability |= ACER_CAP_THREEG;
|
||||
dmi_walk(type_aa_dmi_decode, NULL);
|
||||
if (!has_type_aa) {
|
||||
interface->capability |= ACER_CAP_WIRELESS;
|
||||
interface->capability |= ACER_CAP_THREEG;
|
||||
if (devices & 0x10)
|
||||
interface->capability |= ACER_CAP_BLUETOOTH;
|
||||
}
|
||||
|
||||
/* WMID always provides brightness methods */
|
||||
interface->capability |= ACER_CAP_BRIGHTNESS;
|
||||
|
||||
if (devices & 0x10)
|
||||
interface->capability |= ACER_CAP_BLUETOOTH;
|
||||
|
||||
if (!(devices & 0x20))
|
||||
max_brightness = 0x9;
|
||||
|
||||
|
@ -861,7 +966,8 @@ static void __init acer_commandline_init(void)
|
|||
* capability isn't available on the given interface
|
||||
*/
|
||||
set_u32(mailled, ACER_CAP_MAILLED);
|
||||
set_u32(threeg, ACER_CAP_THREEG);
|
||||
if (!has_type_aa)
|
||||
set_u32(threeg, ACER_CAP_THREEG);
|
||||
set_u32(brightness, ACER_CAP_BRIGHTNESS);
|
||||
}
|
||||
|
||||
|
@ -948,6 +1054,79 @@ static void acer_backlight_exit(void)
|
|||
backlight_device_unregister(acer_backlight_device);
|
||||
}
|
||||
|
||||
static acpi_status wmid3_get_device_status(u32 *value, u16 device)
|
||||
{
|
||||
struct wmid3_gds_return_value return_value;
|
||||
acpi_status status;
|
||||
union acpi_object *obj;
|
||||
struct wmid3_gds_input_param params = {
|
||||
.function_num = 0x1,
|
||||
.hotkey_number = 0x01,
|
||||
.devices = device,
|
||||
};
|
||||
struct acpi_buffer input = {
|
||||
sizeof(struct wmid3_gds_input_param),
|
||||
¶ms
|
||||
};
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = output.pointer;
|
||||
|
||||
if (!obj)
|
||||
return AE_ERROR;
|
||||
else if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
if (obj->buffer.length != 8) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
|
||||
kfree(obj);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Get Device Status failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
else
|
||||
*value = !!(return_value.devices & device);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status get_device_status(u32 *value, u32 cap)
|
||||
{
|
||||
if (wmi_has_guid(WMID_GUID3)) {
|
||||
u16 device;
|
||||
|
||||
switch (cap) {
|
||||
case ACER_CAP_WIRELESS:
|
||||
device = ACER_WMID3_GDS_WIRELESS;
|
||||
break;
|
||||
case ACER_CAP_BLUETOOTH:
|
||||
device = ACER_WMID3_GDS_BLUETOOTH;
|
||||
break;
|
||||
case ACER_CAP_THREEG:
|
||||
device = ACER_WMID3_GDS_THREEG;
|
||||
break;
|
||||
default:
|
||||
return AE_ERROR;
|
||||
}
|
||||
return wmid3_get_device_status(value, device);
|
||||
|
||||
} else {
|
||||
return get_u32(value, cap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rfkill devices
|
||||
*/
|
||||
|
@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
|
|||
rfkill_set_sw_state(bluetooth_rfkill, !state);
|
||||
}
|
||||
|
||||
if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
|
||||
status = wmid3_get_device_status(&state,
|
||||
ACER_WMID3_GDS_THREEG);
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_set_sw_state(threeg_rfkill, !state);
|
||||
}
|
||||
|
||||
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
|
||||
}
|
||||
|
||||
|
@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
|
|||
{
|
||||
int err;
|
||||
struct rfkill *rfkill_dev;
|
||||
u32 state;
|
||||
acpi_status status;
|
||||
|
||||
rfkill_dev = rfkill_alloc(name, dev, type,
|
||||
&acer_rfkill_ops,
|
||||
|
@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
|
|||
if (!rfkill_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
status = get_device_status(&state, cap);
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_init_sw_state(rfkill_dev, !state);
|
||||
|
||||
err = rfkill_register(rfkill_dev);
|
||||
if (err) {
|
||||
rfkill_destroy(rfkill_dev);
|
||||
|
@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
if (has_cap(ACER_CAP_THREEG)) {
|
||||
threeg_rfkill = acer_rfkill_register(dev,
|
||||
RFKILL_TYPE_WWAN, "acer-threeg",
|
||||
ACER_CAP_THREEG);
|
||||
if (IS_ERR(threeg_rfkill)) {
|
||||
rfkill_unregister(wireless_rfkill);
|
||||
rfkill_destroy(wireless_rfkill);
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
return PTR_ERR(threeg_rfkill);
|
||||
}
|
||||
}
|
||||
|
||||
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
|
||||
|
||||
return 0;
|
||||
|
@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void)
|
|||
rfkill_unregister(bluetooth_rfkill);
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
}
|
||||
|
||||
if (has_cap(ACER_CAP_THREEG)) {
|
||||
rfkill_unregister(threeg_rfkill);
|
||||
rfkill_destroy(threeg_rfkill);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u32 result; \
|
||||
acpi_status status = get_u32(&result, ACER_CAP_THREEG);
|
||||
acpi_status status;
|
||||
if (wmi_has_guid(WMID_GUID3))
|
||||
status = wmid3_get_device_status(&result,
|
||||
ACER_WMID3_GDS_THREEG);
|
||||
else
|
||||
status = get_u32(&result, ACER_CAP_THREEG);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return sprintf(buf, "%u\n", result);
|
||||
return sprintf(buf, "Read error\n");
|
||||
|
@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL);
|
||||
|
||||
static void acer_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
struct event_return_value return_value;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
printk(ACER_WARNING "bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
printk(ACER_WARNING "Unknown response received %d\n",
|
||||
obj->type);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
if (obj->buffer.length != 8) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
return_value = *((struct event_return_value *)obj->buffer.pointer);
|
||||
kfree(obj);
|
||||
|
||||
switch (return_value.function) {
|
||||
case WMID_HOTKEY_EVENT:
|
||||
if (!sparse_keymap_report_event(acer_wmi_input_dev,
|
||||
return_value.key_num, 1, true))
|
||||
printk(ACER_WARNING "Unknown key number - 0x%x\n",
|
||||
return_value.key_num);
|
||||
break;
|
||||
default:
|
||||
printk(ACER_WARNING "Unknown function number - %d - %d\n",
|
||||
return_value.function, return_value.key_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static acpi_status
|
||||
wmid3_set_lm_mode(struct lm_input_params *params,
|
||||
struct lm_return_value *return_value)
|
||||
{
|
||||
acpi_status status;
|
||||
union acpi_object *obj;
|
||||
|
||||
struct acpi_buffer input = { sizeof(struct lm_input_params), params };
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = output.pointer;
|
||||
|
||||
if (!obj)
|
||||
return AE_ERROR;
|
||||
else if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
if (obj->buffer.length != 4) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
*return_value = *((struct lm_return_value *)obj->buffer.pointer);
|
||||
kfree(obj);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acer_wmi_enable_ec_raw(void)
|
||||
{
|
||||
struct lm_return_value return_value;
|
||||
acpi_status status;
|
||||
struct lm_input_params params = {
|
||||
.function_num = 0x1,
|
||||
.commun_devices = 0xFFFF,
|
||||
.devices = 0xFFFF,
|
||||
.lm_status = 0x00, /* Launch Manager Deactive */
|
||||
};
|
||||
|
||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Enabling EC raw mode failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
else
|
||||
printk(ACER_INFO "Enabled EC raw mode");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acer_wmi_enable_lm(void)
|
||||
{
|
||||
struct lm_return_value return_value;
|
||||
acpi_status status;
|
||||
struct lm_input_params params = {
|
||||
.function_num = 0x1,
|
||||
.commun_devices = 0xFFFF,
|
||||
.devices = 0xFFFF,
|
||||
.lm_status = 0x01, /* Launch Manager Active */
|
||||
};
|
||||
|
||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Enabling Launch Manager failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __init acer_wmi_input_setup(void)
|
||||
{
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
acer_wmi_input_dev = input_allocate_device();
|
||||
if (!acer_wmi_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
acer_wmi_input_dev->name = "Acer WMI hotkeys";
|
||||
acer_wmi_input_dev->phys = "wmi/input0";
|
||||
acer_wmi_input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
status = wmi_install_notify_handler(ACERWMID_EVENT_GUID,
|
||||
acer_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err = -EIO;
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
err = input_register_device(acer_wmi_input_dev);
|
||||
if (err)
|
||||
goto err_uninstall_notifier;
|
||||
|
||||
return 0;
|
||||
|
||||
err_uninstall_notifier:
|
||||
wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(acer_wmi_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(acer_wmi_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void acer_wmi_input_destroy(void)
|
||||
{
|
||||
wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
|
||||
sparse_keymap_free(acer_wmi_input_dev);
|
||||
input_unregister_device(acer_wmi_input_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs functions
|
||||
*/
|
||||
|
@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void)
|
|||
"generic video driver\n");
|
||||
}
|
||||
|
||||
if (wmi_has_guid(WMID_GUID3)) {
|
||||
if (ec_raw_mode) {
|
||||
if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
|
||||
printk(ACER_ERR "Cannot enable EC raw mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
|
||||
printk(ACER_ERR "Cannot enable Launch Manager mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if (ec_raw_mode) {
|
||||
printk(ACER_INFO "No WMID EC raw mode enable method\n");
|
||||
}
|
||||
|
||||
if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
|
||||
err = acer_wmi_input_setup();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&acer_platform_driver);
|
||||
if (err) {
|
||||
printk(ACER_ERR "Unable to register platform driver.\n");
|
||||
|
@ -1368,11 +1775,17 @@ static int __init acer_wmi_init(void)
|
|||
error_device_alloc:
|
||||
platform_driver_unregister(&acer_platform_driver);
|
||||
error_platform_register:
|
||||
if (wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||
acer_wmi_input_destroy();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit acer_wmi_exit(void)
|
||||
{
|
||||
if (wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||
acer_wmi_input_destroy();
|
||||
|
||||
remove_sysfs(acer_platform_device);
|
||||
remove_debugfs();
|
||||
platform_device_unregister(acer_platform_device);
|
||||
|
|
|
@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked)
|
|||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
unsigned long long state;
|
||||
bool is_blocked;
|
||||
|
||||
handle = data;
|
||||
status = cmpc_get_rfkill_wlan(handle, &state);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
if (blocked)
|
||||
state &= ~1;
|
||||
else
|
||||
state |= 1;
|
||||
status = cmpc_set_rfkill_wlan(handle, state);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
/* Check if we really need to call cmpc_set_rfkill_wlan */
|
||||
is_blocked = state & 1 ? false : true;
|
||||
if (is_blocked != blocked) {
|
||||
state = blocked ? 0 : 1;
|
||||
status = cmpc_set_rfkill_wlan(handle, state);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
|
|||
|
||||
if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
|
||||
code = cmpc_keys_codes[event & 0x0F];
|
||||
inputdev = dev_get_drvdata(&dev->dev);;
|
||||
inputdev = dev_get_drvdata(&dev->dev);
|
||||
input_report_key(inputdev, code, !(event & 0x10));
|
||||
input_sync(inputdev);
|
||||
}
|
||||
|
||||
static void cmpc_keys_idev_init(struct input_dev *inputdev)
|
||||
|
|
|
@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
|
|||
},
|
||||
.callback = dmi_check_cb_extra
|
||||
},
|
||||
{
|
||||
.ident = "KHLB2",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
|
||||
DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
|
||||
},
|
||||
.callback = dmi_check_cb_extra
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev,
|
|||
queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
|
||||
}
|
||||
|
||||
static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct eeepc_laptop *eeepc;
|
||||
|
||||
eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
|
||||
|
||||
return get_acpi(eeepc, CM_ASL_TPD);
|
||||
}
|
||||
|
||||
static int eeepc_led_init(struct eeepc_laptop *eeepc)
|
||||
{
|
||||
int rv;
|
||||
|
@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
|
|||
|
||||
eeepc->tpd_led.name = "eeepc::touchpad";
|
||||
eeepc->tpd_led.brightness_set = tpd_led_set;
|
||||
if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
|
||||
eeepc->tpd_led.brightness_get = tpd_led_get;
|
||||
eeepc->tpd_led.max_brightness = 1;
|
||||
|
||||
rv = led_classdev_register(&eeepc->platform_device->dev,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Eee PC WMI hotkey driver
|
||||
*
|
||||
* Copyright(C) 2010 Intel Corporation.
|
||||
* Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
|
||||
*
|
||||
* Portions based on wistron_btns.c:
|
||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||
|
@ -34,6 +35,10 @@
|
|||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
|
@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
|
|||
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
|
||||
|
||||
#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
||||
#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
|
||||
|
||||
|
@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
|
|||
#define EEEPC_WMI_METHODID_CFVS 0x53564643
|
||||
|
||||
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
|
||||
#define EEEPC_WMI_DEVID_TPDLED 0x00100011
|
||||
#define EEEPC_WMI_DEVID_WLAN 0x00010011
|
||||
#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
|
||||
#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
|
||||
|
||||
static const struct key_entry eeepc_wmi_keymap[] = {
|
||||
/* Sleep already handled via generic ACPI code */
|
||||
|
@ -83,11 +94,37 @@ struct bios_args {
|
|||
u32 ctrl_param;
|
||||
};
|
||||
|
||||
/*
|
||||
* eeepc-wmi/ - debugfs root directory
|
||||
* dev_id - current dev_id
|
||||
* ctrl_param - current ctrl_param
|
||||
* devs - call DEVS(dev_id, ctrl_param) and print result
|
||||
* dsts - call DSTS(dev_id) and print result
|
||||
*/
|
||||
struct eeepc_wmi_debug {
|
||||
struct dentry *root;
|
||||
u32 dev_id;
|
||||
u32 ctrl_param;
|
||||
};
|
||||
|
||||
struct eeepc_wmi {
|
||||
struct input_dev *inputdev;
|
||||
struct backlight_device *backlight_device;
|
||||
struct platform_device *platform_device;
|
||||
|
||||
struct led_classdev tpd_led;
|
||||
int tpd_led_wk;
|
||||
struct workqueue_struct *led_workqueue;
|
||||
struct work_struct tpd_led_work;
|
||||
|
||||
struct rfkill *wlan_rfkill;
|
||||
struct rfkill *bluetooth_rfkill;
|
||||
struct rfkill *wwan3g_rfkill;
|
||||
|
||||
struct eeepc_wmi_debug debug;
|
||||
};
|
||||
|
||||
/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
|
||||
static struct platform_device *platform_device;
|
||||
|
||||
static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
|
||||
|
@ -101,7 +138,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
|
|||
eeepc->inputdev->name = "Eee PC WMI hotkeys";
|
||||
eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
|
||||
eeepc->inputdev->id.bustype = BUS_HOST;
|
||||
eeepc->inputdev->dev.parent = &platform_device->dev;
|
||||
eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
|
||||
|
||||
err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
|
||||
if (err)
|
||||
|
@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
|
|||
eeepc->inputdev = NULL;
|
||||
}
|
||||
|
||||
static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
|
||||
static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
|
||||
{
|
||||
struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
|
|||
else
|
||||
tmp = 0;
|
||||
|
||||
if (ctrl_param)
|
||||
*ctrl_param = tmp;
|
||||
if (retval)
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
|
||||
|
@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
|
|||
|
||||
}
|
||||
|
||||
static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param)
|
||||
static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
|
||||
u32 *retval)
|
||||
{
|
||||
struct bios_args args = {
|
||||
.dev_id = dev_id,
|
||||
|
@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param)
|
|||
struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
|
||||
1, EEEPC_WMI_METHODID_DEVS, &input, NULL);
|
||||
if (!retval) {
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
||||
EEEPC_WMI_METHODID_DEVS,
|
||||
&input, NULL);
|
||||
} else {
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
u32 tmp;
|
||||
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
||||
EEEPC_WMI_METHODID_DEVS,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
tmp = (u32)obj->integer.value;
|
||||
else
|
||||
tmp = 0;
|
||||
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int read_brightness(struct backlight_device *bd)
|
||||
/*
|
||||
* LEDs
|
||||
*/
|
||||
/*
|
||||
* These functions actually update the LED's, and are called from a
|
||||
* workqueue. By doing this as separate work rather than when the LED
|
||||
* subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
|
||||
* potentially bad time, such as a timer interrupt.
|
||||
*/
|
||||
static void tpd_led_update(struct work_struct *work)
|
||||
{
|
||||
static u32 ctrl_param;
|
||||
int ctrl_param;
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
|
||||
|
||||
ctrl_param = eeepc->tpd_led_wk;
|
||||
eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void tpd_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
||||
|
||||
eeepc->tpd_led_wk = !!value;
|
||||
queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
|
||||
}
|
||||
|
||||
static int read_tpd_state(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param);
|
||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
else if (!retval || retval == 0x00060000)
|
||||
/*
|
||||
* if touchpad led is present, DSTS will set some bits,
|
||||
* usually 0x00020000.
|
||||
* 0x00060000 means that the device is not supported
|
||||
*/
|
||||
return -ENODEV;
|
||||
else
|
||||
/* Status is stored in the first bit */
|
||||
return retval & 0x1;
|
||||
}
|
||||
|
||||
static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
||||
|
||||
return read_tpd_state(eeepc);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (read_tpd_state(eeepc) < 0)
|
||||
return 0;
|
||||
|
||||
eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
|
||||
if (!eeepc->led_workqueue)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
|
||||
|
||||
eeepc->tpd_led.name = "eeepc::touchpad";
|
||||
eeepc->tpd_led.brightness_set = tpd_led_set;
|
||||
eeepc->tpd_led.brightness_get = tpd_led_get;
|
||||
eeepc->tpd_led.max_brightness = 1;
|
||||
|
||||
rv = led_classdev_register(&eeepc->platform_device->dev,
|
||||
&eeepc->tpd_led);
|
||||
if (rv) {
|
||||
destroy_workqueue(eeepc->led_workqueue);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->tpd_led.dev)
|
||||
led_classdev_unregister(&eeepc->tpd_led);
|
||||
if (eeepc->led_workqueue)
|
||||
destroy_workqueue(eeepc->led_workqueue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rfkill devices
|
||||
*/
|
||||
static int eeepc_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
int dev_id = (unsigned long)data;
|
||||
u32 ctrl_param = !blocked;
|
||||
|
||||
return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
int dev_id = (unsigned long)data;
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return ;
|
||||
|
||||
rfkill_set_sw_state(rfkill, !(retval & 0x1));
|
||||
}
|
||||
|
||||
static const struct rfkill_ops eeepc_rfkill_ops = {
|
||||
.set_block = eeepc_rfkill_set,
|
||||
.query = eeepc_rfkill_query,
|
||||
};
|
||||
|
||||
static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
|
||||
struct rfkill **rfkill,
|
||||
const char *name,
|
||||
enum rfkill_type type, int dev_id)
|
||||
{
|
||||
int result;
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
|
||||
/* If the device is present, DSTS will always set some bits
|
||||
* 0x00070000 - 1110000000000000000 - device supported
|
||||
* 0x00060000 - 1100000000000000000 - not supported
|
||||
* 0x00020000 - 0100000000000000000 - device supported
|
||||
* 0x00010000 - 0010000000000000000 - not supported / special mode ?
|
||||
*/
|
||||
if (!retval || retval == 0x00060000)
|
||||
return -ENODEV;
|
||||
|
||||
*rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
|
||||
&eeepc_rfkill_ops, (void *)(long)dev_id);
|
||||
|
||||
if (!*rfkill)
|
||||
return -EINVAL;
|
||||
|
||||
rfkill_init_sw_state(*rfkill, !(retval & 0x1));
|
||||
result = rfkill_register(*rfkill);
|
||||
if (result) {
|
||||
rfkill_destroy(*rfkill);
|
||||
*rfkill = NULL;
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->wlan_rfkill) {
|
||||
rfkill_unregister(eeepc->wlan_rfkill);
|
||||
rfkill_destroy(eeepc->wlan_rfkill);
|
||||
eeepc->wlan_rfkill = NULL;
|
||||
}
|
||||
if (eeepc->bluetooth_rfkill) {
|
||||
rfkill_unregister(eeepc->bluetooth_rfkill);
|
||||
rfkill_destroy(eeepc->bluetooth_rfkill);
|
||||
eeepc->bluetooth_rfkill = NULL;
|
||||
}
|
||||
if (eeepc->wwan3g_rfkill) {
|
||||
rfkill_unregister(eeepc->wwan3g_rfkill);
|
||||
rfkill_destroy(eeepc->wwan3g_rfkill);
|
||||
eeepc->wwan3g_rfkill = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
|
||||
"eeepc-wlan", RFKILL_TYPE_WLAN,
|
||||
EEEPC_WMI_DEVID_WLAN);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
|
||||
"eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
|
||||
EEEPC_WMI_DEVID_BLUETOOTH);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
|
||||
"eeepc-wwan3g", RFKILL_TYPE_WWAN,
|
||||
EEEPC_WMI_DEVID_WWAN3G);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
exit:
|
||||
if (result && result != -ENODEV)
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
|
||||
if (result == -ENODEV)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Backlight
|
||||
*/
|
||||
static int read_brightness(struct backlight_device *bd)
|
||||
{
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
else
|
||||
return ctrl_param & 0xFF;
|
||||
return retval & 0xFF;
|
||||
}
|
||||
|
||||
static int update_bl_status(struct backlight_device *bd)
|
||||
{
|
||||
|
||||
static u32 ctrl_param;
|
||||
u32 ctrl_param;
|
||||
acpi_status status;
|
||||
|
||||
ctrl_param = bd->props.brightness;
|
||||
|
||||
status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param);
|
||||
status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
|
||||
ctrl_param, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
|
@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
|
|||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 15;
|
||||
bd = backlight_device_register(EEEPC_WMI_FILE,
|
||||
&platform_device->dev, eeepc,
|
||||
&eeepc->platform_device->dev, eeepc,
|
||||
&eeepc_wmi_bl_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
pr_err("Could not register backlight device\n");
|
||||
|
@ -321,65 +606,240 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
|
||||
|
||||
static struct attribute *platform_attributes[] = {
|
||||
&dev_attr_cpufv.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group platform_attribute_group = {
|
||||
.attrs = platform_attributes
|
||||
};
|
||||
|
||||
static void eeepc_wmi_sysfs_exit(struct platform_device *device)
|
||||
{
|
||||
device_remove_file(&device->dev, &dev_attr_cpufv);
|
||||
sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_sysfs_init(struct platform_device *device)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
|
||||
retval = device_create_file(&device->dev, &dev_attr_cpufv);
|
||||
if (retval)
|
||||
goto error_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
error_sysfs:
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
return retval;
|
||||
return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
|
||||
}
|
||||
|
||||
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
|
||||
/*
|
||||
* Platform device
|
||||
*/
|
||||
static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
int err;
|
||||
acpi_status status;
|
||||
|
||||
eeepc = platform_get_drvdata(device);
|
||||
eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
|
||||
if (!eeepc->platform_device)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(eeepc->platform_device, eeepc);
|
||||
|
||||
err = eeepc_wmi_input_init(eeepc);
|
||||
err = platform_device_add(eeepc->platform_device);
|
||||
if (err)
|
||||
goto error_input;
|
||||
goto fail_platform_device;
|
||||
|
||||
if (!acpi_video_backlight_support()) {
|
||||
err = eeepc_wmi_backlight_init(eeepc);
|
||||
if (err)
|
||||
goto error_backlight;
|
||||
} else
|
||||
pr_info("Backlight controlled by ACPI video driver\n");
|
||||
err = eeepc_wmi_sysfs_init(eeepc->platform_device);
|
||||
if (err)
|
||||
goto fail_sysfs;
|
||||
return 0;
|
||||
|
||||
status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
|
||||
eeepc_wmi_notify, eeepc);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Unable to register notify handler - %d\n",
|
||||
status);
|
||||
err = -ENODEV;
|
||||
goto error_wmi;
|
||||
fail_sysfs:
|
||||
platform_device_del(eeepc->platform_device);
|
||||
fail_platform_device:
|
||||
platform_device_put(eeepc->platform_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
eeepc_wmi_sysfs_exit(eeepc->platform_device);
|
||||
platform_device_unregister(eeepc->platform_device);
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs
|
||||
*/
|
||||
struct eeepc_wmi_debugfs_node {
|
||||
struct eeepc_wmi *eeepc;
|
||||
char *name;
|
||||
int (*show)(struct seq_file *m, void *data);
|
||||
};
|
||||
|
||||
static int show_dsts(struct seq_file *m, void *data)
|
||||
{
|
||||
struct eeepc_wmi *eeepc = m->private;
|
||||
acpi_status status;
|
||||
u32 retval = -1;
|
||||
|
||||
status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_devs(struct seq_file *m, void *data)
|
||||
{
|
||||
struct eeepc_wmi *eeepc = m->private;
|
||||
acpi_status status;
|
||||
u32 retval = -1;
|
||||
|
||||
status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
|
||||
eeepc->debug.ctrl_param, &retval);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
|
||||
eeepc->debug.ctrl_param, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
|
||||
{ NULL, "devs", show_devs },
|
||||
{ NULL, "dsts", show_dsts },
|
||||
};
|
||||
|
||||
static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct eeepc_wmi_debugfs_node *node = inode->i_private;
|
||||
|
||||
return single_open(file, node->show, node->eeepc);
|
||||
}
|
||||
|
||||
static const struct file_operations eeepc_wmi_debugfs_io_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = eeepc_wmi_debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
debugfs_remove_recursive(eeepc->debug.root);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
struct dentry *dent;
|
||||
int i;
|
||||
|
||||
eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
|
||||
if (!eeepc->debug.root) {
|
||||
pr_err("failed to create debugfs directory");
|
||||
goto error_debugfs;
|
||||
}
|
||||
|
||||
dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
|
||||
eeepc->debug.root, &eeepc->debug.dev_id);
|
||||
if (!dent)
|
||||
goto error_debugfs;
|
||||
|
||||
dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
|
||||
eeepc->debug.root, &eeepc->debug.ctrl_param);
|
||||
if (!dent)
|
||||
goto error_debugfs;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
|
||||
struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
|
||||
|
||||
node->eeepc = eeepc;
|
||||
dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
|
||||
eeepc->debug.root, node,
|
||||
&eeepc_wmi_debugfs_io_ops);
|
||||
if (!dent) {
|
||||
pr_err("failed to create debug file: %s\n", node->name);
|
||||
goto error_debugfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_wmi:
|
||||
eeepc_wmi_backlight_exit(eeepc);
|
||||
error_backlight:
|
||||
eeepc_wmi_input_exit(eeepc);
|
||||
error_input:
|
||||
return err;
|
||||
error_debugfs:
|
||||
eeepc_wmi_debugfs_exit(eeepc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
|
||||
/*
|
||||
* WMI Driver
|
||||
*/
|
||||
static struct platform_device * __init eeepc_wmi_add(void)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
|
||||
if (!eeepc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Register the platform device first. It is used as a parent for the
|
||||
* sub-devices below.
|
||||
*/
|
||||
err = eeepc_wmi_platform_init(eeepc);
|
||||
if (err)
|
||||
goto fail_platform;
|
||||
|
||||
err = eeepc_wmi_input_init(eeepc);
|
||||
if (err)
|
||||
goto fail_input;
|
||||
|
||||
err = eeepc_wmi_led_init(eeepc);
|
||||
if (err)
|
||||
goto fail_leds;
|
||||
|
||||
err = eeepc_wmi_rfkill_init(eeepc);
|
||||
if (err)
|
||||
goto fail_rfkill;
|
||||
|
||||
if (!acpi_video_backlight_support()) {
|
||||
err = eeepc_wmi_backlight_init(eeepc);
|
||||
if (err)
|
||||
goto fail_backlight;
|
||||
} else
|
||||
pr_info("Backlight controlled by ACPI video driver\n");
|
||||
|
||||
status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
|
||||
eeepc_wmi_notify, eeepc);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Unable to register notify handler - %d\n",
|
||||
status);
|
||||
err = -ENODEV;
|
||||
goto fail_wmi_handler;
|
||||
}
|
||||
|
||||
err = eeepc_wmi_debugfs_init(eeepc);
|
||||
if (err)
|
||||
goto fail_debugfs;
|
||||
|
||||
return eeepc->platform_device;
|
||||
|
||||
fail_debugfs:
|
||||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
||||
fail_wmi_handler:
|
||||
eeepc_wmi_backlight_exit(eeepc);
|
||||
fail_backlight:
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
fail_rfkill:
|
||||
eeepc_wmi_led_exit(eeepc);
|
||||
fail_leds:
|
||||
eeepc_wmi_input_exit(eeepc);
|
||||
fail_input:
|
||||
eeepc_wmi_platform_exit(eeepc);
|
||||
fail_platform:
|
||||
kfree(eeepc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_remove(struct platform_device *device)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
|
@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
|
|||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
||||
eeepc_wmi_backlight_exit(eeepc);
|
||||
eeepc_wmi_input_exit(eeepc);
|
||||
eeepc_wmi_led_exit(eeepc);
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
eeepc_wmi_debugfs_exit(eeepc);
|
||||
eeepc_wmi_platform_exit(eeepc);
|
||||
|
||||
kfree(eeepc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -396,13 +861,31 @@ static struct platform_driver platform_driver = {
|
|||
.name = EEEPC_WMI_FILE,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = eeepc_wmi_platform_probe,
|
||||
.remove = __devexit_p(eeepc_wmi_platform_remove),
|
||||
};
|
||||
|
||||
static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||
void *context, void **retval)
|
||||
{
|
||||
pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
|
||||
*(bool *)context = true;
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
static int __init eeepc_wmi_check_atkd(void)
|
||||
{
|
||||
acpi_status status;
|
||||
bool found = false;
|
||||
|
||||
status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
|
||||
&found, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status) || !found)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __init eeepc_wmi_init(void)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
int err;
|
||||
|
||||
if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
|
||||
|
@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
|
||||
if (!eeepc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
|
||||
if (!platform_device) {
|
||||
pr_warning("Unable to allocate platform device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_platform;
|
||||
if (eeepc_wmi_check_atkd()) {
|
||||
pr_warning("WMI device present, but legacy ATKD device is also "
|
||||
"present and enabled.");
|
||||
pr_warning("You probably booted with acpi_osi=\"Linux\" or "
|
||||
"acpi_osi=\"!Windows 2009\"");
|
||||
pr_warning("Can't load eeepc-wmi, use default acpi_osi "
|
||||
"(preferred) or eeepc-laptop");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = platform_device_add(platform_device);
|
||||
if (err) {
|
||||
pr_warning("Unable to add platform device\n");
|
||||
goto put_dev;
|
||||
platform_device = eeepc_wmi_add();
|
||||
if (IS_ERR(platform_device)) {
|
||||
err = PTR_ERR(platform_device);
|
||||
goto fail_eeepc_wmi;
|
||||
}
|
||||
|
||||
platform_set_drvdata(platform_device, eeepc);
|
||||
|
||||
err = platform_driver_register(&platform_driver);
|
||||
if (err) {
|
||||
pr_warning("Unable to register platform driver\n");
|
||||
goto del_dev;
|
||||
goto fail_platform_driver;
|
||||
}
|
||||
|
||||
err = eeepc_wmi_sysfs_init(platform_device);
|
||||
if (err)
|
||||
goto del_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
del_sysfs:
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
del_dev:
|
||||
platform_device_del(platform_device);
|
||||
put_dev:
|
||||
platform_device_put(platform_device);
|
||||
fail_platform:
|
||||
kfree(eeepc);
|
||||
|
||||
fail_platform_driver:
|
||||
eeepc_wmi_remove(platform_device);
|
||||
fail_eeepc_wmi:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit eeepc_wmi_exit(void)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc_wmi_sysfs_exit(platform_device);
|
||||
eeepc = platform_get_drvdata(platform_device);
|
||||
eeepc_wmi_remove(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
platform_device_unregister(platform_device);
|
||||
kfree(eeepc);
|
||||
}
|
||||
|
||||
module_init(eeepc_wmi_init);
|
||||
|
|
|
@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
|
|||
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
|
||||
MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
|
||||
|
||||
static struct pnp_device_id pnp_ids[] = {
|
||||
static struct pnp_device_id pnp_ids[] __used = {
|
||||
{.id = "FUJ02bf"},
|
||||
{.id = "FUJ02B1"},
|
||||
{.id = "FUJ02E3"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
|
||||
* ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
|
||||
*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
|
||||
|
@ -27,31 +27,19 @@
|
|||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
#define IDEAPAD_DEV_CAMERA 0
|
||||
#define IDEAPAD_DEV_WLAN 1
|
||||
#define IDEAPAD_DEV_BLUETOOTH 2
|
||||
#define IDEAPAD_DEV_3G 3
|
||||
#define IDEAPAD_DEV_KILLSW 4
|
||||
#define IDEAPAD_RFKILL_DEV_NUM (3)
|
||||
|
||||
struct ideapad_private {
|
||||
acpi_handle handle;
|
||||
struct rfkill *rfk[5];
|
||||
} *ideapad_priv;
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
int cfgbit;
|
||||
int opcode;
|
||||
int type;
|
||||
} ideapad_rfk_data[] = {
|
||||
{ "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES },
|
||||
{ "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
|
||||
{ "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
|
||||
{ "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
|
||||
{ "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN }
|
||||
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
|
||||
struct platform_device *platform_device;
|
||||
struct input_dev *inputdev;
|
||||
};
|
||||
|
||||
static acpi_handle ideapad_handle;
|
||||
static bool no_bt_rfkill;
|
||||
module_param(no_bt_rfkill, bool, 0444);
|
||||
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
|
||||
|
@ -163,17 +151,17 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
|
|||
pr_err("timeout in write_ec_cmd\n");
|
||||
return -1;
|
||||
}
|
||||
/* the above is ACPI helpers */
|
||||
|
||||
/*
|
||||
* camera power
|
||||
*/
|
||||
static ssize_t show_ideapad_cam(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
acpi_handle handle = priv->handle;
|
||||
unsigned long result;
|
||||
|
||||
if (read_ec_data(handle, 0x1D, &result))
|
||||
if (read_ec_data(ideapad_handle, 0x1D, &result))
|
||||
return sprintf(buf, "-1\n");
|
||||
return sprintf(buf, "%lu\n", result);
|
||||
}
|
||||
|
@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(dev);
|
||||
acpi_handle handle = priv->handle;
|
||||
int ret, state;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
if (sscanf(buf, "%i", &state) != 1)
|
||||
return -EINVAL;
|
||||
ret = write_ec_cmd(handle, 0x1E, state);
|
||||
ret = write_ec_cmd(ideapad_handle, 0x1E, state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return count;
|
||||
|
@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev,
|
|||
|
||||
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
|
||||
|
||||
/*
|
||||
* Rfkill
|
||||
*/
|
||||
struct ideapad_rfk_data {
|
||||
char *name;
|
||||
int cfgbit;
|
||||
int opcode;
|
||||
int type;
|
||||
};
|
||||
|
||||
const struct ideapad_rfk_data ideapad_rfk_data[] = {
|
||||
{ "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
|
||||
{ "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
|
||||
{ "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
|
||||
};
|
||||
|
||||
static int ideapad_rfk_set(void *data, bool blocked)
|
||||
{
|
||||
int device = (unsigned long)data;
|
||||
unsigned long opcode = (unsigned long)data;
|
||||
|
||||
if (device == IDEAPAD_DEV_KILLSW)
|
||||
return -EINVAL;
|
||||
|
||||
return write_ec_cmd(ideapad_priv->handle,
|
||||
ideapad_rfk_data[device].opcode,
|
||||
!blocked);
|
||||
return write_ec_cmd(ideapad_handle, opcode, !blocked);
|
||||
}
|
||||
|
||||
static struct rfkill_ops ideapad_rfk_ops = {
|
||||
|
@ -217,20 +214,20 @@ static struct rfkill_ops ideapad_rfk_ops = {
|
|||
static void ideapad_sync_rfk_state(struct acpi_device *adevice)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
acpi_handle handle = priv->handle;
|
||||
unsigned long hw_blocked;
|
||||
int i;
|
||||
|
||||
if (read_ec_data(handle, 0x23, &hw_blocked))
|
||||
if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
|
||||
return;
|
||||
hw_blocked = !hw_blocked;
|
||||
|
||||
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
|
||||
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
|
||||
if (priv->rfk[i])
|
||||
rfkill_set_hw_state(priv->rfk[i], hw_blocked);
|
||||
}
|
||||
|
||||
static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
|
||||
static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
|
||||
int dev)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
int ret;
|
||||
|
@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
|
|||
if (no_bt_rfkill &&
|
||||
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
|
||||
/* Force to enable bluetooth when no_bt_rfkill=1 */
|
||||
write_ec_cmd(ideapad_priv->handle,
|
||||
write_ec_cmd(ideapad_handle,
|
||||
ideapad_rfk_data[dev].opcode, 1);
|
||||
return 0;
|
||||
}
|
||||
|
@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
|
|||
if (!priv->rfk[dev])
|
||||
return -ENOMEM;
|
||||
|
||||
if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
|
||||
if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
|
||||
&sw_blocked)) {
|
||||
rfkill_init_sw_state(priv->rfk[dev], 0);
|
||||
} else {
|
||||
|
@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
|
||||
static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
|
||||
int dev)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
|
||||
|
@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
|
|||
rfkill_destroy(priv->rfk[dev]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform device
|
||||
*/
|
||||
static struct attribute *ideapad_attributes[] = {
|
||||
&dev_attr_camera_power.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group ideapad_attribute_group = {
|
||||
.attrs = ideapad_attributes
|
||||
};
|
||||
|
||||
static int __devinit ideapad_platform_init(struct ideapad_private *priv)
|
||||
{
|
||||
int result;
|
||||
|
||||
priv->platform_device = platform_device_alloc("ideapad", -1);
|
||||
if (!priv->platform_device)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(priv->platform_device, priv);
|
||||
|
||||
result = platform_device_add(priv->platform_device);
|
||||
if (result)
|
||||
goto fail_platform_device;
|
||||
|
||||
result = sysfs_create_group(&priv->platform_device->dev.kobj,
|
||||
&ideapad_attribute_group);
|
||||
if (result)
|
||||
goto fail_sysfs;
|
||||
return 0;
|
||||
|
||||
fail_sysfs:
|
||||
platform_device_del(priv->platform_device);
|
||||
fail_platform_device:
|
||||
platform_device_put(priv->platform_device);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ideapad_platform_exit(struct ideapad_private *priv)
|
||||
{
|
||||
sysfs_remove_group(&priv->platform_device->dev.kobj,
|
||||
&ideapad_attribute_group);
|
||||
platform_device_unregister(priv->platform_device);
|
||||
}
|
||||
|
||||
/*
|
||||
* input device
|
||||
*/
|
||||
static const struct key_entry ideapad_keymap[] = {
|
||||
{ KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x0D, { KEY_WLAN } },
|
||||
{ KE_END, 0 },
|
||||
};
|
||||
|
||||
static int __devinit ideapad_input_init(struct ideapad_private *priv)
|
||||
{
|
||||
struct input_dev *inputdev;
|
||||
int error;
|
||||
|
||||
inputdev = input_allocate_device();
|
||||
if (!inputdev) {
|
||||
pr_info("Unable to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
inputdev->name = "Ideapad extra buttons";
|
||||
inputdev->phys = "ideapad/input0";
|
||||
inputdev->id.bustype = BUS_HOST;
|
||||
inputdev->dev.parent = &priv->platform_device->dev;
|
||||
|
||||
error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
|
||||
if (error) {
|
||||
pr_err("Unable to setup input device keymap\n");
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
error = input_register_device(inputdev);
|
||||
if (error) {
|
||||
pr_err("Unable to register input device\n");
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
priv->inputdev = inputdev;
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(inputdev);
|
||||
err_free_dev:
|
||||
input_free_device(inputdev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __devexit ideapad_input_exit(struct ideapad_private *priv)
|
||||
{
|
||||
sparse_keymap_free(priv->inputdev);
|
||||
input_unregister_device(priv->inputdev);
|
||||
priv->inputdev = NULL;
|
||||
}
|
||||
|
||||
static void ideapad_input_report(struct ideapad_private *priv,
|
||||
unsigned long scancode)
|
||||
{
|
||||
sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* module init/exit
|
||||
*/
|
||||
static const struct acpi_device_id ideapad_device_ids[] = {
|
||||
{ "VPC2004", 0},
|
||||
{ "", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
|
||||
|
||||
static int ideapad_acpi_add(struct acpi_device *adevice)
|
||||
static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
|
||||
{
|
||||
int i, cfg;
|
||||
int devs_present[5];
|
||||
int ret, i, cfg;
|
||||
struct ideapad_private *priv;
|
||||
|
||||
if (read_method_int(adevice->handle, "_CFG", &cfg))
|
||||
return -ENODEV;
|
||||
|
||||
for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
|
||||
if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
|
||||
devs_present[i] = 1;
|
||||
else
|
||||
devs_present[i] = 0;
|
||||
}
|
||||
|
||||
/* The hardware switch is always present */
|
||||
devs_present[IDEAPAD_DEV_KILLSW] = 1;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (devs_present[IDEAPAD_DEV_CAMERA]) {
|
||||
int ret = device_create_file(&adevice->dev, &dev_attr_camera_power);
|
||||
if (ret) {
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
priv->handle = adevice->handle;
|
||||
dev_set_drvdata(&adevice->dev, priv);
|
||||
ideapad_priv = priv;
|
||||
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
|
||||
if (!devs_present[i])
|
||||
continue;
|
||||
ideapad_handle = adevice->handle;
|
||||
|
||||
ideapad_register_rfkill(adevice, i);
|
||||
ret = ideapad_platform_init(priv);
|
||||
if (ret)
|
||||
goto platform_failed;
|
||||
|
||||
ret = ideapad_input_init(priv);
|
||||
if (ret)
|
||||
goto input_failed;
|
||||
|
||||
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
|
||||
if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
|
||||
ideapad_register_rfkill(adevice, i);
|
||||
else
|
||||
priv->rfk[i] = NULL;
|
||||
}
|
||||
ideapad_sync_rfk_state(adevice);
|
||||
|
||||
return 0;
|
||||
|
||||
input_failed:
|
||||
ideapad_platform_exit(priv);
|
||||
platform_failed:
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
|
||||
static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
int i;
|
||||
|
||||
device_remove_file(&adevice->dev, &dev_attr_camera_power);
|
||||
|
||||
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
|
||||
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
|
||||
ideapad_unregister_rfkill(adevice, i);
|
||||
|
||||
ideapad_input_exit(priv);
|
||||
ideapad_platform_exit(priv);
|
||||
dev_set_drvdata(&adevice->dev, NULL);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
||||
{
|
||||
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
||||
acpi_handle handle = adevice->handle;
|
||||
unsigned long vpc1, vpc2, vpc_bit;
|
||||
|
||||
|
@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
|||
if (test_bit(vpc_bit, &vpc1)) {
|
||||
if (vpc_bit == 9)
|
||||
ideapad_sync_rfk_state(adevice);
|
||||
else
|
||||
ideapad_input_report(priv, vpc_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int __init ideapad_acpi_module_init(void)
|
||||
{
|
||||
acpi_bus_register_driver(&ideapad_acpi_driver);
|
||||
|
||||
return 0;
|
||||
return acpi_bus_register_driver(&ideapad_acpi_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit ideapad_acpi_module_exit(void)
|
||||
{
|
||||
acpi_bus_unregister_driver(&ideapad_acpi_driver);
|
||||
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
|
|
|
@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips)
|
|||
}
|
||||
|
||||
void
|
||||
ips_link_to_i915_driver()
|
||||
ips_link_to_i915_driver(void)
|
||||
{
|
||||
/* We can't cleanly get at the various ips_driver structs from
|
||||
* this caller (the i915 driver), so just set a flag saying
|
||||
|
|
|
@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc)
|
|||
generic_handle_irq(pg->irq_base + gpio);
|
||||
}
|
||||
}
|
||||
desc->chip->eoi(irq);
|
||||
|
||||
if (desc->chip->irq_eoi)
|
||||
desc->chip->irq_eoi(irq_get_irq_data(irq));
|
||||
else
|
||||
dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq);
|
||||
}
|
||||
|
||||
static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
|
|||
"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
return 0;
|
||||
|
@ -642,7 +642,7 @@ int intel_scu_ipc_fw_update(u8 *buffer, u32 length)
|
|||
|
||||
if (status == IPC_FW_UPDATE_SUCCESS)
|
||||
return 0;
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_fw_update);
|
||||
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism
|
||||
*
|
||||
* (C) Copyright 2008-2010 Intel Corporation
|
||||
* Author: Sreedhara DS (sreedhara.ds@intel.com)
|
||||
*
|
||||
* 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; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This driver provides ioctl interfaces to call intel scu ipc driver api
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
static u32 major;
|
||||
|
||||
#define MAX_FW_SIZE 264192
|
||||
|
||||
/* ioctl commnds */
|
||||
#define INTE_SCU_IPC_REGISTER_READ 0
|
||||
#define INTE_SCU_IPC_REGISTER_WRITE 1
|
||||
#define INTE_SCU_IPC_REGISTER_UPDATE 2
|
||||
#define INTE_SCU_IPC_FW_UPDATE 0xA2
|
||||
|
||||
struct scu_ipc_data {
|
||||
u32 count; /* No. of registers */
|
||||
u16 addr[5]; /* Register addresses */
|
||||
u8 data[5]; /* Register data */
|
||||
u8 mask; /* Valid for read-modify-write */
|
||||
};
|
||||
|
||||
/**
|
||||
* scu_reg_access - implement register access ioctls
|
||||
* @cmd: command we are doing (read/write/update)
|
||||
* @data: kernel copy of ioctl data
|
||||
*
|
||||
* Allow the user to perform register accesses on the SCU via the
|
||||
* kernel interface
|
||||
*/
|
||||
|
||||
static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)
|
||||
{
|
||||
int count = data->count;
|
||||
|
||||
if (count == 0 || count == 3 || count > 4)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case INTE_SCU_IPC_REGISTER_READ:
|
||||
return intel_scu_ipc_readv(data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_WRITE:
|
||||
return intel_scu_ipc_writev(data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_UPDATE:
|
||||
return intel_scu_ipc_update_register(data->addr[0],
|
||||
data->data[0], data->mask);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scu_ipc_ioctl - control ioctls for the SCU
|
||||
* @fp: file handle of the SCU device
|
||||
* @cmd: ioctl coce
|
||||
* @arg: pointer to user passed structure
|
||||
*
|
||||
* Support the I/O and firmware flashing interfaces of the SCU
|
||||
*/
|
||||
static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
struct scu_ipc_data data;
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
return -EPERM;
|
||||
|
||||
if (cmd == INTE_SCU_IPC_FW_UPDATE) {
|
||||
u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL);
|
||||
if (fwbuf == NULL)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) {
|
||||
kfree(fwbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE);
|
||||
kfree(fwbuf);
|
||||
return ret;
|
||||
} else {
|
||||
if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
|
||||
return -EFAULT;
|
||||
ret = scu_reg_access(cmd, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations scu_ipc_fops = {
|
||||
.unlocked_ioctl = scu_ipc_ioctl,
|
||||
};
|
||||
|
||||
static int __init ipc_module_init(void)
|
||||
{
|
||||
return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);
|
||||
}
|
||||
|
||||
static void __exit ipc_module_exit(void)
|
||||
{
|
||||
unregister_chrdev(major, "intel_mid_scu");
|
||||
}
|
||||
|
||||
module_init(ipc_module_init);
|
||||
module_exit(ipc_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL V2");
|
||||
MODULE_DESCRIPTION("Utility driver for intel scu ipc");
|
||||
MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>");
|
|
@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = {
|
|||
57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
|
||||
-1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
|
||||
58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */
|
||||
59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */
|
||||
};
|
||||
|
||||
static int sony_laptop_input_keycode_map[] = {
|
||||
|
@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = {
|
|||
KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
|
||||
KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
|
||||
KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */
|
||||
KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */
|
||||
};
|
||||
|
||||
/* release buttons after a short delay if pressed */
|
||||
|
@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = {
|
|||
{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
|
||||
{ 0x8C, SONYPI_EVENT_FNKEY_F12 },
|
||||
{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
|
||||
{ 0x9d, SONYPI_EVENT_ZOOM_PRESSED },
|
||||
{ 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },
|
||||
{ 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0xa1, SONYPI_EVENT_MEDIA_PRESSED },
|
||||
{ 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED },
|
||||
{ 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0xa5, SONYPI_EVENT_VENDOR_PRESSED },
|
||||
{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0xa6, SONYPI_EVENT_HELP_PRESSED },
|
||||
{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void sony_nc_rfkill_update()
|
||||
static void sony_nc_rfkill_update(void)
|
||||
{
|
||||
enum sony_nc_rfkill i;
|
||||
int result;
|
||||
|
|
|
@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle,
|
|||
default:
|
||||
printk(TPACPI_ERR "acpi_evalf() called "
|
||||
"with invalid format character '%c'\n", c);
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||
"as change notification\n");
|
||||
tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
|
||||
| TP_ACPI_HKEY_BRGHTUP_MASK
|
||||
| TP_ACPI_HKEY_BRGHTDWN_MASK);;
|
||||
| TP_ACPI_HKEY_BRGHTDWN_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid,
|
|||
wmi_notify_handler handler, void *data)
|
||||
{
|
||||
struct wmi_block *block;
|
||||
acpi_status status;
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
char tmp[16], guid_input[16];
|
||||
struct list_head *p;
|
||||
|
||||
if (!guid || !handler)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (!find_guid(guid, &block))
|
||||
return AE_NOT_EXIST;
|
||||
wmi_parse_guid(guid, tmp);
|
||||
wmi_swap_bytes(tmp, guid_input);
|
||||
|
||||
if (block->handler && block->handler != wmi_notify_debug)
|
||||
return AE_ALREADY_ACQUIRED;
|
||||
list_for_each(p, &wmi_block_list) {
|
||||
acpi_status wmi_status;
|
||||
block = list_entry(p, struct wmi_block, list);
|
||||
|
||||
block->handler = handler;
|
||||
block->handler_data = data;
|
||||
if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
|
||||
if (block->handler &&
|
||||
block->handler != wmi_notify_debug)
|
||||
return AE_ALREADY_ACQUIRED;
|
||||
|
||||
status = wmi_method_enable(block, 1);
|
||||
block->handler = handler;
|
||||
block->handler_data = data;
|
||||
|
||||
wmi_status = wmi_method_enable(block, 1);
|
||||
if ((wmi_status != AE_OK) ||
|
||||
((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
|
||||
status = wmi_status;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
|
|||
acpi_status wmi_remove_notify_handler(const char *guid)
|
||||
{
|
||||
struct wmi_block *block;
|
||||
acpi_status status = AE_OK;
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
char tmp[16], guid_input[16];
|
||||
struct list_head *p;
|
||||
|
||||
if (!guid)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (!find_guid(guid, &block))
|
||||
return AE_NOT_EXIST;
|
||||
wmi_parse_guid(guid, tmp);
|
||||
wmi_swap_bytes(tmp, guid_input);
|
||||
|
||||
if (!block->handler || block->handler == wmi_notify_debug)
|
||||
return AE_NULL_ENTRY;
|
||||
list_for_each(p, &wmi_block_list) {
|
||||
acpi_status wmi_status;
|
||||
block = list_entry(p, struct wmi_block, list);
|
||||
|
||||
if (debug_event) {
|
||||
block->handler = wmi_notify_debug;
|
||||
} else {
|
||||
status = wmi_method_enable(block, 0);
|
||||
block->handler = NULL;
|
||||
block->handler_data = NULL;
|
||||
if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
|
||||
if (!block->handler ||
|
||||
block->handler == wmi_notify_debug)
|
||||
return AE_NULL_ENTRY;
|
||||
|
||||
if (debug_event) {
|
||||
block->handler = wmi_notify_debug;
|
||||
status = AE_OK;
|
||||
} else {
|
||||
wmi_status = wmi_method_enable(block, 0);
|
||||
block->handler = NULL;
|
||||
block->handler_data = NULL;
|
||||
if ((wmi_status != AE_OK) ||
|
||||
((wmi_status == AE_OK) &&
|
||||
(status == AE_NOT_EXIST)))
|
||||
status = wmi_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
|
||||
|
@ -705,22 +734,11 @@ static struct class wmi_class = {
|
|||
.dev_attrs = wmi_dev_attrs,
|
||||
};
|
||||
|
||||
static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
|
||||
acpi_handle handle)
|
||||
static int wmi_create_device(const struct guid_block *gblock,
|
||||
struct wmi_block *wblock, acpi_handle handle)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
int error;
|
||||
char guid_string[37];
|
||||
|
||||
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
|
||||
if (!wblock) {
|
||||
error = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
wblock->handle = handle;
|
||||
wblock->gblock = *gblock;
|
||||
|
||||
wblock->dev.class = &wmi_class;
|
||||
|
||||
wmi_gtoa(gblock->guid, guid_string);
|
||||
|
@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
|
|||
|
||||
dev_set_drvdata(&wblock->dev, wblock);
|
||||
|
||||
error = device_register(&wblock->dev);
|
||||
if (error)
|
||||
goto err_free;
|
||||
|
||||
list_add_tail(&wblock->list, &wmi_block_list);
|
||||
return wblock;
|
||||
|
||||
err_free:
|
||||
kfree(wblock);
|
||||
err_out:
|
||||
return ERR_PTR(error);
|
||||
return device_register(&wblock->dev);
|
||||
}
|
||||
|
||||
static void wmi_free_devices(void)
|
||||
|
@ -747,7 +755,8 @@ static void wmi_free_devices(void)
|
|||
|
||||
/* Delete devices for all the GUIDs */
|
||||
list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
|
||||
device_unregister(&wblock->dev);
|
||||
if (wblock->dev.class)
|
||||
device_unregister(&wblock->dev);
|
||||
}
|
||||
|
||||
static bool guid_already_parsed(const char *guid_string)
|
||||
|
@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle)
|
|||
union acpi_object *obj;
|
||||
const struct guid_block *gblock;
|
||||
struct wmi_block *wblock;
|
||||
char guid_string[37];
|
||||
acpi_status status;
|
||||
int retval;
|
||||
u32 i, total;
|
||||
|
@ -792,29 +800,32 @@ static acpi_status parse_wdg(acpi_handle handle)
|
|||
total = obj->buffer.length / sizeof(struct guid_block);
|
||||
|
||||
for (i = 0; i < total; i++) {
|
||||
/*
|
||||
Some WMI devices, like those for nVidia hooks, have a
|
||||
duplicate GUID. It's not clear what we should do in this
|
||||
case yet, so for now, we'll just ignore the duplicate.
|
||||
Anyone who wants to add support for that device can come
|
||||
up with a better workaround for the mess then.
|
||||
*/
|
||||
if (guid_already_parsed(gblock[i].guid) == true) {
|
||||
wmi_gtoa(gblock[i].guid, guid_string);
|
||||
pr_info("Skipping duplicate GUID %s\n", guid_string);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (debug_dump_wdg)
|
||||
wmi_dump_wdg(&gblock[i]);
|
||||
|
||||
wblock = wmi_create_device(&gblock[i], handle);
|
||||
if (IS_ERR(wblock)) {
|
||||
retval = PTR_ERR(wblock);
|
||||
wmi_free_devices();
|
||||
break;
|
||||
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
|
||||
if (!wblock)
|
||||
return AE_NO_MEMORY;
|
||||
|
||||
wblock->handle = handle;
|
||||
wblock->gblock = gblock[i];
|
||||
|
||||
/*
|
||||
Some WMI devices, like those for nVidia hooks, have a
|
||||
duplicate GUID. It's not clear what we should do in this
|
||||
case yet, so for now, we'll just ignore the duplicate
|
||||
for device creation.
|
||||
*/
|
||||
if (!guid_already_parsed(gblock[i].guid)) {
|
||||
retval = wmi_create_device(&gblock[i], wblock, handle);
|
||||
if (retval) {
|
||||
wmi_free_devices();
|
||||
goto out_free_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&wblock->list, &wmi_block_list);
|
||||
|
||||
if (debug_event) {
|
||||
wblock->handler = wmi_notify_debug;
|
||||
wmi_method_enable(wblock, 1);
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
#define SONYPI_EVENT_VOLUME_DEC_PRESSED 70
|
||||
#define SONYPI_EVENT_BRIGHTNESS_PRESSED 71
|
||||
#define SONYPI_EVENT_MEDIA_PRESSED 72
|
||||
#define SONYPI_EVENT_VENDOR_PRESSED 73
|
||||
|
||||
/* get/set brightness */
|
||||
#define SONYPI_IOCGBRT _IOR('v', 0, __u8)
|
||||
|
|
Loading…
Reference in New Issue