Commit 443e6221 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of...

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
  ...
parents 0caca697 1a7d9469
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 ...@@ -2271,6 +2271,14 @@ W: http://acpi4asus.sf.net
S: Maintained S: Maintained
F: drivers/platform/x86/eeepc-laptop.c 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 EFIFB FRAMEBUFFER DRIVER
L: linux-fbdev@vger.kernel.org L: linux-fbdev@vger.kernel.org
M: Peter Jones <pjones@redhat.com> M: Peter Jones <pjones@redhat.com>
......
...@@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES ...@@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES
config ACER_WMI config ACER_WMI
tristate "Acer WMI Laptop Extras" tristate "Acer WMI Laptop Extras"
depends on ACPI depends on ACPI
depends on LEDS_CLASS select LEDS_CLASS
depends on NEW_LEDS select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042 depends on SERIO_I8042
depends on INPUT
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
select ACPI_WMI depends on ACPI_WMI
select INPUT_SPARSEKMAP
---help--- ---help---
This is a driver for newer Acer (and Wistron) laptops. It adds This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops, wireless radio and bluetooth control, and on some laptops,
...@@ -131,7 +133,7 @@ config TC1100_WMI ...@@ -131,7 +133,7 @@ config TC1100_WMI
depends on !X86_64 depends on !X86_64
depends on EXPERIMENTAL depends on EXPERIMENTAL
depends on ACPI depends on ACPI
select ACPI_WMI depends on ACPI_WMI
---help--- ---help---
This is a driver for the WMI extensions (wireless and bluetooth power This is a driver for the WMI extensions (wireless and bluetooth power
control) of the HP Compaq TC1100 tablet. control) of the HP Compaq TC1100 tablet.
...@@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP ...@@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad Laptop Extras" tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI depends on ACPI
depends on RFKILL depends on RFKILL
select INPUT_SPARSEKMAP
help help
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
...@@ -425,7 +428,10 @@ config EEEPC_WMI ...@@ -425,7 +428,10 @@ config EEEPC_WMI
depends on INPUT depends on INPUT
depends on EXPERIMENTAL depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
select INPUT_SPARSEKMAP select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
---help--- ---help---
Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
...@@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP ...@@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP
config ACPI_TOSHIBA config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras" tristate "Toshiba Laptop Extras"
depends on ACPI depends on ACPI
depends on LEDS_CLASS select LEDS_CLASS
depends on NEW_LEDS select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
...@@ -576,6 +582,15 @@ config INTEL_SCU_IPC ...@@ -576,6 +582,15 @@ config INTEL_SCU_IPC
some embedded Intel x86 platforms. This is not needed for PC-type some embedded Intel x86 platforms. This is not needed for PC-type
machines. 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 config GPIO_INTEL_PMIC
bool "Intel PMIC GPIO support" bool "Intel PMIC GPIO support"
depends on INTEL_SCU_IPC && GPIOLIB depends on INTEL_SCU_IPC && GPIOLIB
......
...@@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o ...@@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.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_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/dmi.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
...@@ -48,6 +51,7 @@ MODULE_LICENSE("GPL"); ...@@ -48,6 +51,7 @@ MODULE_LICENSE("GPL");
#define ACER_ERR KERN_ERR ACER_LOGPREFIX #define ACER_ERR KERN_ERR ACER_LOGPREFIX
#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
#define ACER_INFO KERN_INFO ACER_LOGPREFIX #define ACER_INFO KERN_INFO ACER_LOGPREFIX
#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX
/* /*
* Magic Number * Magic Number
...@@ -82,9 +86,82 @@ MODULE_LICENSE("GPL"); ...@@ -82,9 +86,82 @@ MODULE_LICENSE("GPL");
#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" #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:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); 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 * Interface capability flags
...@@ -116,15 +193,19 @@ static int mailled = -1; ...@@ -116,15 +193,19 @@ static int mailled = -1;
static int brightness = -1; static int brightness = -1;
static int threeg = -1; static int threeg = -1;
static int force_series; static int force_series;
static bool ec_raw_mode;
static bool has_type_aa;
module_param(mailled, int, 0444); module_param(mailled, int, 0444);
module_param(brightness, int, 0444); module_param(brightness, int, 0444);
module_param(threeg, int, 0444); module_param(threeg, int, 0444);
module_param(force_series, 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(mailled, "Set initial state of Mail LED");
MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
MODULE_PARM_DESC(force_series, "Force a different laptop series"); MODULE_PARM_DESC(force_series, "Force a different laptop series");
MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
struct acer_data { struct acer_data {
int mailled; int mailled;
...@@ -140,6 +221,7 @@ struct acer_debug { ...@@ -140,6 +221,7 @@ struct acer_debug {
static struct rfkill *wireless_rfkill; static struct rfkill *wireless_rfkill;
static struct rfkill *bluetooth_rfkill; static struct rfkill *bluetooth_rfkill;
static struct rfkill *threeg_rfkill;
/* Each low-level interface must define at least some of the following */ /* Each low-level interface must define at least some of the following */
struct wmi_interface { struct wmi_interface {
...@@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) ...@@ -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); 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) static acpi_status WMID_set_capabilities(void)
{ {
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
...@@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void) ...@@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void)
return AE_ERROR; return AE_ERROR;
} }
/* Not sure on the meaning of the relevant bits yet to detect these */ dmi_walk(type_aa_dmi_decode, NULL);
if (!has_type_aa) {
interface->capability |= ACER_CAP_WIRELESS; interface->capability |= ACER_CAP_WIRELESS;
interface->capability |= ACER_CAP_THREEG; interface->capability |= ACER_CAP_THREEG;
if (devices & 0x10)
interface->capability |= ACER_CAP_BLUETOOTH;
}
/* WMID always provides brightness methods */ /* WMID always provides brightness methods */
interface->capability |= ACER_CAP_BRIGHTNESS; interface->capability |= ACER_CAP_BRIGHTNESS;
if (devices & 0x10)
interface->capability |= ACER_CAP_BLUETOOTH;
if (!(devices & 0x20)) if (!(devices & 0x20))
max_brightness = 0x9; max_brightness = 0x9;
...@@ -861,6 +966,7 @@ static void __init acer_commandline_init(void) ...@@ -861,6 +966,7 @@ static void __init acer_commandline_init(void)
* capability isn't available on the given interface * capability isn't available on the given interface
*/ */
set_u32(mailled, ACER_CAP_MAILLED); set_u32(mailled, ACER_CAP_MAILLED);
if (!has_type_aa)
set_u32(threeg, ACER_CAP_THREEG); set_u32(threeg, ACER_CAP_THREEG);
set_u32(brightness, ACER_CAP_BRIGHTNESS); set_u32(brightness, ACER_CAP_BRIGHTNESS);
} }
...@@ -948,6 +1054,79 @@ static void acer_backlight_exit(void) ...@@ -948,6 +1054,79 @@ static void acer_backlight_exit(void)
backlight_device_unregister(acer_backlight_device); 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),
&params
};
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 * Rfkill devices
*/ */
...@@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored) ...@@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
rfkill_set_sw_state(bluetooth_rfkill, !state); 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)); schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
} }
...@@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, ...@@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
{ {
int err; int err;
struct rfkill *rfkill_dev; struct rfkill *rfkill_dev;
u32 state;
acpi_status status;
rfkill_dev = rfkill_alloc(name, dev, type, rfkill_dev = rfkill_alloc(name, dev, type,
&acer_rfkill_ops, &acer_rfkill_ops,
...@@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev, ...@@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
if (!rfkill_dev) if (!rfkill_dev)
return ERR_PTR(-ENOMEM); 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); err = rfkill_register(rfkill_dev);
if (err) { if (err) {
rfkill_destroy(rfkill_dev); rfkill_destroy(rfkill_dev);
...@@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *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)); schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
return 0; return 0;
...@@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void) ...@@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void)
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill); rfkill_destroy(bluetooth_rfkill);
} }
if (has_cap(ACER_CAP_THREEG)) {
rfkill_unregister(threeg_rfkill);
rfkill_destroy(threeg_rfkill);
}
return; return;
} }
...@@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev, ...@@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
u32 result; \ 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)) if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result); return sprintf(buf, "%u\n", result);
return sprintf(buf, "Read error\n"); return sprintf(buf, "Read error\n");
...@@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, ...@@ -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 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(&params, &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(&params, &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 * debugfs functions
*/ */
...@@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void) ...@@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void)
"generic video driver\n"); "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); err = platform_driver_register(&acer_platform_driver);
if (err) { if (err) {
printk(ACER_ERR "Unable to register platform driver.\n"); printk(ACER_ERR "Unable to register platform driver.\n");
...@@ -1368,11 +1775,17 @@ static int __init acer_wmi_init(void) ...@@ -1368,11 +1775,17 @@ static int __init acer_wmi_init(void)
error_device_alloc: error_device_alloc:
platform_driver_unregister(&acer_platform_driver); platform_driver_unregister(&acer_platform_driver);
error_platform_register: error_platform_register:
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
return err; return err;
} }
static void __exit acer_wmi_exit(void) static void __exit acer_wmi_exit(void)
{ {
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
remove_sysfs(acer_platform_device); remove_sysfs(acer_platform_device);
remove_debugfs(); remove_debugfs();
platform_device_unregister(acer_platform_device); platform_device_unregister(acer_platform_device);
......
...@@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked) ...@@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked)
acpi_status status; acpi_status status;
acpi_handle handle; acpi_handle handle;
unsigned long long state; unsigned long long state;
bool is_blocked;
handle = data; handle = data;
status = cmpc_get_rfkill_wlan(handle, &state); status = cmpc_get_rfkill_wlan(handle, &state);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
if (blocked) /* Check if we really need to call cmpc_set_rfkill_wlan */
state &= ~1; is_blocked = state & 1 ? false : true;
else if (is_blocked != blocked) {
state |= 1; state = blocked ? 0 : 1;
status = cmpc_set_rfkill_wlan(handle, state); status = cmpc_set_rfkill_wlan(handle, state);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
}
return 0; return 0;
} }
...@@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) ...@@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
code = cmpc_keys_codes[event & 0x0F]; 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_report_key(inputdev, code, !(event & 0x10));
input_sync(inputdev);
} }
static void cmpc_keys_idev_init(struct input_dev *inputdev) static void cmpc_keys_idev_init(struct input_dev *inputdev)
......
...@@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { ...@@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
}, },
.callback = dmi_check_cb_extra .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, ...@@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev,
queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); 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) static int eeepc_led_init(struct eeepc_laptop *eeepc)
{ {
int rv; int rv;
...@@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) ...@@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
eeepc->tpd_led.name = "eeepc::touchpad"; eeepc->tpd_led.name = "eeepc::touchpad";
eeepc->tpd_led.brightness_set = tpd_led_set; 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; eeepc->tpd_led.max_brightness = 1;
rv = led_classdev_register(&eeepc->platform_device->dev, rv = led_classdev_register(&eeepc->platform_device->dev,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Eee PC WMI hotkey driver * Eee PC WMI hotkey driver
* *
* Copyright(C) 2010 Intel Corporation. * Copyright(C) 2010 Intel Corporation.
* Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
* *
* Portions based on wistron_btns.c: * Portions based on wistron_btns.c:
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
...@@ -34,6 +35,10 @@ ...@@ -34,6 +35,10 @@
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/backlight.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 <linux/platform_device.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
...@@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); ...@@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
MODULE_LICENSE("GPL"); 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_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
...@@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); ...@@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
#define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_METHODID_CFVS 0x53564643
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #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[] = { static const struct key_entry eeepc_wmi_keymap[] = {
/* Sleep already handled via generic ACPI code */ /* Sleep already handled via generic ACPI code */
...@@ -83,11 +94,37 @@ struct bios_args { ...@@ -83,11 +94,37 @@ struct bios_args {
u32 ctrl_param; 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 eeepc_wmi {
struct input_dev *inputdev; struct input_dev *inputdev;
struct backlight_device *backlight_device; 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 struct platform_device *platform_device;
static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
...@@ -101,7 +138,7 @@ 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->name = "Eee PC WMI hotkeys";
eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
eeepc->inputdev->id.bustype = BUS_HOST; 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); err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
if (err) if (err)
...@@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) ...@@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
eeepc->inputdev = NULL; 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 input = { (acpi_size)sizeof(u32), &dev_id };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 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) ...@@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
else else
tmp = 0; tmp = 0;
if (ctrl_param) if (retval)
*ctrl_param = tmp; *retval = tmp;
kfree(obj); kfree(obj);
...@@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) ...@@ -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 = { struct bios_args args = {
.dev_id = dev_id, .dev_id = dev_id,
...@@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) ...@@ -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 }; struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
acpi_status status; acpi_status status;
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, if (!retval) {
1, EEEPC_WMI_METHODID_DEVS, &input, NULL); 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; return status;
} }
/*
* 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)
{
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_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) static int read_brightness(struct backlight_device *bd)
{ {
static u32 ctrl_param; u32 retval;
acpi_status status; acpi_status status;
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -1; return -1;
else else
return ctrl_param & 0xFF; return retval & 0xFF;
} }
static int update_bl_status(struct backlight_device *bd) static int update_bl_status(struct backlight_device *bd)
{ {
static u32 ctrl_param; u32 ctrl_param;
acpi_status status; acpi_status status;
ctrl_param = bd->props.brightness; 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)) if (ACPI_FAILURE(status))
return -1; return -1;
...@@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) ...@@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 15; props.max_brightness = 15;
bd = backlight_device_register(EEEPC_WMI_FILE, bd = backlight_device_register(EEEPC_WMI_FILE,
&platform_device->dev, eeepc, &eeepc->platform_device->dev, eeepc,
&eeepc_wmi_bl_ops, &props); &eeepc_wmi_bl_ops, &props);
if (IS_ERR(bd)) { if (IS_ERR(bd)) {
pr_err("Could not register backlight device\n"); pr_err("Could not register backlight device\n");
...@@ -321,42 +606,204 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, ...@@ -321,42 +606,204 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); 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) 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) static int eeepc_wmi_sysfs_init(struct platform_device *device)
{ {
int retval = -ENOMEM; return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
}
retval = device_create_file(&device->dev, &dev_attr_cpufv); /*
if (retval) * Platform device
goto error_sysfs; */
static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
{
int err;
eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
if (!eeepc->platform_device)
return -ENOMEM;
platform_set_drvdata(eeepc->platform_device, eeepc);
err = platform_device_add(eeepc->platform_device);
if (err)
goto fail_platform_device;
err = eeepc_wmi_sysfs_init(eeepc->platform_device);
if (err)
goto fail_sysfs;
return 0; return 0;
error_sysfs: fail_sysfs:
eeepc_wmi_sysfs_exit(platform_device); platform_device_del(eeepc->platform_device);
return retval; fail_platform_device:
platform_device_put(eeepc->platform_device);
return err;
} }
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) 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; struct eeepc_wmi *eeepc;
int err; 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; acpi_status status;
u32 retval = -1;
eeepc = platform_get_drvdata(device); 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_debugfs:
eeepc_wmi_debugfs_exit(eeepc);
return -ENOMEM;
}
/*
* 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); err = eeepc_wmi_input_init(eeepc);
if (err) if (err)
goto error_input; 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()) { if (!acpi_video_backlight_support()) {
err = eeepc_wmi_backlight_init(eeepc); err = eeepc_wmi_backlight_init(eeepc);
if (err) if (err)
goto error_backlight; goto fail_backlight;
} else } else
pr_info("Backlight controlled by ACPI video driver\n"); pr_info("Backlight controlled by ACPI video driver\n");
...@@ -366,20 +813,33 @@ static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) ...@@ -366,20 +813,33 @@ static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
pr_err("Unable to register notify handler - %d\n", pr_err("Unable to register notify handler - %d\n",
status); status);
err = -ENODEV; err = -ENODEV;
goto error_wmi; goto fail_wmi_handler;
} }
return 0; err = eeepc_wmi_debugfs_init(eeepc);
if (err)
goto fail_debugfs;
error_wmi: return eeepc->platform_device;
fail_debugfs:
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
fail_wmi_handler:
eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_backlight_exit(eeepc);
error_backlight: fail_backlight:
eeepc_wmi_rfkill_exit(eeepc);
fail_rfkill:
eeepc_wmi_led_exit(eeepc);
fail_leds:
eeepc_wmi_input_exit(eeepc); eeepc_wmi_input_exit(eeepc);
error_input: fail_input:
return err; eeepc_wmi_platform_exit(eeepc);
fail_platform:
kfree(eeepc);
return ERR_PTR(err);
} }
static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) static int eeepc_wmi_remove(struct platform_device *device)
{ {
struct eeepc_wmi *eeepc; struct eeepc_wmi *eeepc;
...@@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) ...@@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_backlight_exit(eeepc);
eeepc_wmi_input_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; return 0;
} }
...@@ -396,13 +861,31 @@ static struct platform_driver platform_driver = { ...@@ -396,13 +861,31 @@ static struct platform_driver platform_driver = {
.name = EEEPC_WMI_FILE, .name = EEEPC_WMI_FILE,
.owner = THIS_MODULE, .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) static int __init eeepc_wmi_init(void)
{ {
struct eeepc_wmi *eeepc;
int err; int err;
if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
...@@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void) ...@@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void)
return -ENODEV; return -ENODEV;
} }
eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); if (eeepc_wmi_check_atkd()) {
if (!eeepc) pr_warning("WMI device present, but legacy ATKD device is also "
return -ENOMEM; "present and enabled.");
pr_warning("You probably booted with acpi_osi=\"Linux\" or "
platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); "acpi_osi=\"!Windows 2009\"");
if (!platform_device) { pr_warning("Can't load eeepc-wmi, use default acpi_osi "
pr_warning("Unable to allocate platform device\n"); "(preferred) or eeepc-laptop");
err = -ENOMEM; return -ENODEV;
goto fail_platform;
} }
err = platform_device_add(platform_device); platform_device = eeepc_wmi_add();
if (err) { if (IS_ERR(platform_device)) {
pr_warning("Unable to add platform device\n"); err = PTR_ERR(platform_device);
goto put_dev; goto fail_eeepc_wmi;
} }
platform_set_drvdata(platform_device, eeepc);
err = platform_driver_register(&platform_driver); err = platform_driver_register(&platform_driver);
if (err) { if (err) {
pr_warning("Unable to register platform driver\n"); 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; return 0;
del_sysfs: fail_platform_driver:
eeepc_wmi_sysfs_exit(platform_device); eeepc_wmi_remove(platform_device);
del_dev: fail_eeepc_wmi:
platform_device_del(platform_device);
put_dev:
platform_device_put(platform_device);
fail_platform:
kfree(eeepc);
return err; return err;
} }
static void __exit eeepc_wmi_exit(void) static void __exit eeepc_wmi_exit(void)
{ {
struct eeepc_wmi *eeepc; eeepc_wmi_remove(platform_device);
eeepc_wmi_sysfs_exit(platform_device);
eeepc = platform_get_drvdata(platform_device);
platform_driver_unregister(&platform_driver); platform_driver_unregister(&platform_driver);
platform_device_unregister(platform_device);
kfree(eeepc);
} }
module_init(eeepc_wmi_init); module_init(eeepc_wmi_init);
......
...@@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); ...@@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); 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 = "FUJ02bf"},
{.id = "FUJ02B1"}, {.id = "FUJ02B1"},
{.id = "FUJ02E3"}, {.id = "FUJ02E3"},
......
/* /*
* ideapad_acpi.c - Lenovo IdeaPad ACPI Extras * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
* *
* Copyright © 2010 Intel Corporation * Copyright © 2010 Intel Corporation
* Copyright © 2010 David Woodhouse <dwmw2@infradead.org> * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
...@@ -27,31 +27,19 @@ ...@@ -27,31 +27,19 @@
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include <linux/rfkill.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_RFKILL_DEV_NUM (3)
#define IDEAPAD_DEV_WLAN 1
#define IDEAPAD_DEV_BLUETOOTH 2
#define IDEAPAD_DEV_3G 3
#define IDEAPAD_DEV_KILLSW 4
struct ideapad_private { struct ideapad_private {
acpi_handle handle; struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct rfkill *rfk[5]; struct platform_device *platform_device;
} *ideapad_priv; struct input_dev *inputdev;
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 }
}; };
static acpi_handle ideapad_handle;
static bool no_bt_rfkill; static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444); module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 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) ...@@ -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"); pr_err("timeout in write_ec_cmd\n");
return -1; return -1;
} }
/* the above is ACPI helpers */
/*
* camera power
*/
static ssize_t show_ideapad_cam(struct device *dev, static ssize_t show_ideapad_cam(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev);
acpi_handle handle = priv->handle;
unsigned long result; 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, "-1\n");
return sprintf(buf, "%lu\n", result); return sprintf(buf, "%lu\n", result);
} }
...@@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev, ...@@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct ideapad_private *priv = dev_get_drvdata(dev);
acpi_handle handle = priv->handle;
int ret, state; int ret, state;
if (!count) if (!count)
return 0; return 0;
if (sscanf(buf, "%i", &state) != 1) if (sscanf(buf, "%i", &state) != 1)
return -EINVAL; return -EINVAL;
ret = write_ec_cmd(handle, 0x1E, state); ret = write_ec_cmd(ideapad_handle, 0x1E, state);
if (ret < 0) if (ret < 0)
return ret; return ret;
return count; return count;
...@@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev, ...@@ -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); 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) 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, return write_ec_cmd(ideapad_handle, opcode, !blocked);
ideapad_rfk_data[device].opcode,
!blocked);
} }
static struct rfkill_ops ideapad_rfk_ops = { static struct rfkill_ops ideapad_rfk_ops = {
...@@ -217,20 +214,20 @@ 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) static void ideapad_sync_rfk_state(struct acpi_device *adevice)
{ {
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
acpi_handle handle = priv->handle;
unsigned long hw_blocked; unsigned long hw_blocked;
int i; int i;
if (read_ec_data(handle, 0x23, &hw_blocked)) if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
return; return;
hw_blocked = !hw_blocked; 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]) if (priv->rfk[i])
rfkill_set_hw_state(priv->rfk[i], hw_blocked); 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); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int ret; int ret;
...@@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) ...@@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
if (no_bt_rfkill && if (no_bt_rfkill &&
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
/* Force to enable bluetooth when no_bt_rfkill=1 */ /* 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); ideapad_rfk_data[dev].opcode, 1);
return 0; return 0;
} }
...@@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) ...@@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
if (!priv->rfk[dev]) if (!priv->rfk[dev])
return -ENOMEM; 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)) { &sw_blocked)) {
rfkill_init_sw_state(priv->rfk[dev], 0); rfkill_init_sw_state(priv->rfk[dev], 0);
} else { } else {
...@@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) ...@@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
return 0; 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); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
...@@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) ...@@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
rfkill_destroy(priv->rfk[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[] = { static const struct acpi_device_id ideapad_device_ids[] = {
{ "VPC2004", 0}, { "VPC2004", 0},
{ "", 0}, { "", 0},
}; };
MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 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 ret, i, cfg;
int devs_present[5];
struct ideapad_private *priv; struct ideapad_private *priv;
if (read_method_int(adevice->handle, "_CFG", &cfg)) if (read_method_int(adevice->handle, "_CFG", &cfg))
return -ENODEV; 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); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(&adevice->dev, priv);
ideapad_handle = adevice->handle;
if (devs_present[IDEAPAD_DEV_CAMERA]) { ret = ideapad_platform_init(priv);
int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); if (ret)
if (ret) { goto platform_failed;
kfree(priv);
return ret;
}
}
priv->handle = adevice->handle; ret = ideapad_input_init(priv);
dev_set_drvdata(&adevice->dev, priv); if (ret)
ideapad_priv = priv; goto input_failed;
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
if (!devs_present[i])
continue;
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); ideapad_register_rfkill(adevice, i);
else
priv->rfk[i] = NULL;
} }
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(adevice);
return 0; 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); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int i; int i;
device_remove_file(&adevice->dev, &dev_attr_camera_power); for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
ideapad_unregister_rfkill(adevice, i); ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv);
ideapad_platform_exit(priv);
dev_set_drvdata(&adevice->dev, NULL); dev_set_drvdata(&adevice->dev, NULL);
kfree(priv); kfree(priv);
return 0; return 0;
} }
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) 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; acpi_handle handle = adevice->handle;
unsigned long vpc1, vpc2, vpc_bit; unsigned long vpc1, vpc2, vpc_bit;
...@@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
if (test_bit(vpc_bit, &vpc1)) { if (test_bit(vpc_bit, &vpc1)) {
if (vpc_bit == 9) if (vpc_bit == 9)
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(adevice);
else
ideapad_input_report(priv, vpc_bit);
} }
} }
} }
...@@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = { ...@@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int __init ideapad_acpi_module_init(void) static int __init ideapad_acpi_module_init(void)
{ {
acpi_bus_register_driver(&ideapad_acpi_driver); return acpi_bus_register_driver(&ideapad_acpi_driver);
return 0;
} }
static void __exit ideapad_acpi_module_exit(void) static void __exit ideapad_acpi_module_exit(void)
{ {
acpi_bus_unregister_driver(&ideapad_acpi_driver); acpi_bus_unregister_driver(&ideapad_acpi_driver);
} }
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
......
...@@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) ...@@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips)
} }
void void
ips_link_to_i915_driver() ips_link_to_i915_driver(void)
{ {
/* We can't cleanly get at the various ips_driver structs from /* We can't cleanly get at the various ips_driver structs from
* this caller (the i915 driver), so just set a flag saying * 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) ...@@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc)
generic_handle_irq(pg->irq_base + gpio); 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) 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) ...@@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return -1; return -EIO;
} }
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return 0; return 0;
...@@ -642,7 +642,7 @@ int intel_scu_ipc_fw_update(u8 *buffer, u32 length) ...@@ -642,7 +642,7 @@ int intel_scu_ipc_fw_update(u8 *buffer, u32 length)
if (status == IPC_FW_UPDATE_SUCCESS) if (status == IPC_FW_UPDATE_SUCCESS)
return 0; return 0;
return -1; return -EIO;
} }
EXPORT_SYMBOL(intel_scu_ipc_fw_update); EXPORT_SYMBOL(intel_scu_ipc_fw_update);
......
/*
* 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[] = { ...@@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = {
57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
-1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */
59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */
}; };
static int sony_laptop_input_keycode_map[] = { static int sony_laptop_input_keycode_map[] = {
...@@ -297,6 +298,7 @@ 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_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */
KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */
}; };
/* release buttons after a short delay if pressed */ /* release buttons after a short delay if pressed */
...@@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = { ...@@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = {
{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x8C, SONYPI_EVENT_FNKEY_F12 },
{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
{ 0x9d, SONYPI_EVENT_ZOOM_PRESSED },
{ 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },
{ 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, { 0xa1, SONYPI_EVENT_MEDIA_PRESSED },
{ 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 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 }, { 0, 0 },
}; };
...@@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, ...@@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
return err; return err;
} }
static void sony_nc_rfkill_update() static void sony_nc_rfkill_update(void)
{ {
enum sony_nc_rfkill i; enum sony_nc_rfkill i;
int result; int result;
......
...@@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle, ...@@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle,
default: default:
printk(TPACPI_ERR "acpi_evalf() called " printk(TPACPI_ERR "acpi_evalf() called "
"with invalid format character '%c'\n", c); "with invalid format character '%c'\n", c);
va_end(ap);
return 0; return 0;
} }
} }
...@@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
"as change notification\n"); "as change notification\n");
tpacpi_hotkey_driver_mask_set(hotkey_driver_mask tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
| TP_ACPI_HKEY_BRGHTUP_MASK | TP_ACPI_HKEY_BRGHTUP_MASK
| TP_ACPI_HKEY_BRGHTDWN_MASK);; | TP_ACPI_HKEY_BRGHTDWN_MASK);
return 0; return 0;
} }
......
...@@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, ...@@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler handler, void *data) wmi_notify_handler handler, void *data)
{ {
struct wmi_block *block; 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) if (!guid || !handler)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
if (!find_guid(guid, &block)) wmi_parse_guid(guid, tmp);
return AE_NOT_EXIST; wmi_swap_bytes(tmp, guid_input);
list_for_each(p, &wmi_block_list) {
acpi_status wmi_status;
block = list_entry(p, struct wmi_block, list);
if (block->handler && block->handler != wmi_notify_debug) if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
if (block->handler &&
block->handler != wmi_notify_debug)
return AE_ALREADY_ACQUIRED; return AE_ALREADY_ACQUIRED;
block->handler = handler; block->handler = handler;
block->handler_data = data; block->handler_data = data;
status = wmi_method_enable(block, 1); 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; return status;
} }
...@@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); ...@@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
acpi_status wmi_remove_notify_handler(const char *guid) acpi_status wmi_remove_notify_handler(const char *guid)
{ {
struct wmi_block *block; 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) if (!guid)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
if (!find_guid(guid, &block)) wmi_parse_guid(guid, tmp);
return AE_NOT_EXIST; wmi_swap_bytes(tmp, guid_input);
list_for_each(p, &wmi_block_list) {
acpi_status wmi_status;
block = list_entry(p, struct wmi_block, list);
if (!block->handler || block->handler == wmi_notify_debug) if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
if (!block->handler ||
block->handler == wmi_notify_debug)
return AE_NULL_ENTRY; return AE_NULL_ENTRY;
if (debug_event) { if (debug_event) {
block->handler = wmi_notify_debug; block->handler = wmi_notify_debug;
status = AE_OK;
} else { } else {
status = wmi_method_enable(block, 0); wmi_status = wmi_method_enable(block, 0);
block->handler = NULL; block->handler = NULL;
block->handler_data = NULL; block->handler_data = NULL;
if ((wmi_status != AE_OK) ||
((wmi_status == AE_OK) &&
(status == AE_NOT_EXIST)))
status = wmi_status;
} }
}
}
return status; return status;
} }
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
...@@ -705,22 +734,11 @@ static struct class wmi_class = { ...@@ -705,22 +734,11 @@ static struct class wmi_class = {
.dev_attrs = wmi_dev_attrs, .dev_attrs = wmi_dev_attrs,
}; };
static struct wmi_block *wmi_create_device(const struct guid_block *gblock, static int wmi_create_device(const struct guid_block *gblock,
acpi_handle handle) struct wmi_block *wblock, acpi_handle handle)
{ {
struct wmi_block *wblock;
int error;
char guid_string[37]; 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; wblock->dev.class = &wmi_class;
wmi_gtoa(gblock->guid, guid_string); wmi_gtoa(gblock->guid, guid_string);
...@@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, ...@@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
dev_set_drvdata(&wblock->dev, wblock); dev_set_drvdata(&wblock->dev, wblock);
error = device_register(&wblock->dev); return 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);
} }
static void wmi_free_devices(void) static void wmi_free_devices(void)
...@@ -747,6 +755,7 @@ static void wmi_free_devices(void) ...@@ -747,6 +755,7 @@ static void wmi_free_devices(void)
/* Delete devices for all the GUIDs */ /* Delete devices for all the GUIDs */
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
if (wblock->dev.class)
device_unregister(&wblock->dev); device_unregister(&wblock->dev);
} }
...@@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) ...@@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle)
union acpi_object *obj; union acpi_object *obj;
const struct guid_block *gblock; const struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock;
char guid_string[37];
acpi_status status; acpi_status status;
int retval; int retval;
u32 i, total; u32 i, total;
...@@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle) ...@@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle)
total = obj->buffer.length / sizeof(struct guid_block); total = obj->buffer.length / sizeof(struct guid_block);
for (i = 0; i < total; i++) { for (i = 0; i < total; i++) {
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
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 Some WMI devices, like those for nVidia hooks, have a
duplicate GUID. It's not clear what we should do in this duplicate GUID. It's not clear what we should do in this
case yet, so for now, we'll just ignore the duplicate. case yet, so for now, we'll just ignore the duplicate
Anyone who wants to add support for that device can come for device creation.
up with a better workaround for the mess then.
*/ */
if (guid_already_parsed(gblock[i].guid) == true) { if (!guid_already_parsed(gblock[i].guid)) {
wmi_gtoa(gblock[i].guid, guid_string); retval = wmi_create_device(&gblock[i], wblock, handle);
pr_info("Skipping duplicate GUID %s\n", guid_string); if (retval) {
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(); wmi_free_devices();
break; goto out_free_pointer;
} }
}
list_add_tail(&wblock->list, &wmi_block_list);
if (debug_event) { if (debug_event) {
wblock->handler = wmi_notify_debug; wblock->handler = wmi_notify_debug;
......
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
#define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70
#define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71
#define SONYPI_EVENT_MEDIA_PRESSED 72 #define SONYPI_EVENT_MEDIA_PRESSED 72
#define SONYPI_EVENT_VENDOR_PRESSED 73
/* get/set brightness */ /* get/set brightness */
#define SONYPI_IOCGBRT _IOR('v', 0, __u8) #define SONYPI_IOCGBRT _IOR('v', 0, __u8)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment