#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/dmi.h>
#include <acpi/acpi_drivers.h>
*/
#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
#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_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
static const struct key_entry acer_wmi_keymap[] = {
{KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */
+ {KE_KEY, 0x04, {KEY_WLAN} }, /* WiFi */
{KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */
{KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */
{KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
/*
* 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 */
u32 reserved;
} __attribute__((packed));
+struct hotkey_function_type_aa {
+ u8 type;
+ u8 length;
+ u16 handle;
+ u16 commun_func_bitmap;
+} __attribute__((packed));
+
/*
* Interface capability flags
*/
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;
static struct rfkill *wireless_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *threeg_rfkill;
+static bool rfkill_inited;
/* Each low-level interface must define at least some of the following */
struct wmi_interface {
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};
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;
* 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);
}
return 0;
}
-static struct backlight_ops acer_bl_ops = {
+static const struct backlight_ops acer_bl_ops = {
.get_brightness = read_brightness,
.update_status = update_bl_status,
};
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
*/
{
acpi_status status;
u32 cap = (unsigned long)data;
- status = set_u32(!blocked, cap);
- if (ACPI_FAILURE(status))
- return -ENODEV;
+
+ if (rfkill_inited) {
+ status = set_u32(!blocked, cap);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+ }
+
return 0;
}
{
int err;
struct rfkill *rfkill_dev;
+ u32 state;
+ acpi_status status;
rfkill_dev = rfkill_alloc(name, dev, type,
&acer_rfkill_ops,
if (!rfkill_dev)
return ERR_PTR(-ENOMEM);
+ status = get_device_status(&state, cap);
+
err = rfkill_register(rfkill_dev);
if (err) {
rfkill_destroy(rfkill_dev);
return ERR_PTR(err);
}
+
+ if (ACPI_SUCCESS(status))
+ rfkill_set_sw_state(rfkill_dev, !state);
+
return rfkill_dev;
}
}
}
- schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
+ rfkill_inited = true;
+
+ if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+ schedule_delayed_work(&acer_rfkill_work,
+ round_jiffies_relative(HZ));
return 0;
}
static void acer_rfkill_exit(void)
{
- cancel_delayed_work_sync(&acer_rfkill_work);
+ if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
+ cancel_delayed_work_sync(&acer_rfkill_work);
rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
return -EINVAL;
return count;
}
-static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
+static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
set_bool_threeg);
static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
union acpi_object *obj;
struct event_return_value return_value;
acpi_status status;
+ u16 device_state;
+ const struct key_entry *key;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
switch (return_value.function) {
case WMID_HOTKEY_EVENT:
- if (!sparse_keymap_report_event(acer_wmi_input_dev,
- return_value.key_num, 1, true))
+ device_state = return_value.device_state;
+ pr_debug("device state: 0x%x\n", device_state);
+
+ key = sparse_keymap_entry_from_scancode(acer_wmi_input_dev,
+ return_value.key_num);
+ if (!key) {
printk(ACER_WARNING "Unknown key number - 0x%x\n",
return_value.key_num);
+ } else {
+ switch (key->keycode) {
+ case KEY_WLAN:
+ case KEY_BLUETOOTH:
+ if (has_cap(ACER_CAP_WIRELESS))
+ rfkill_set_sw_state(wireless_rfkill,
+ !(device_state & ACER_WMID3_GDS_WIRELESS));
+ if (has_cap(ACER_CAP_THREEG))
+ rfkill_set_sw_state(threeg_rfkill,
+ !(device_state & ACER_WMID3_GDS_THREEG));
+ if (has_cap(ACER_CAP_BLUETOOTH))
+ rfkill_set_sw_state(bluetooth_rfkill,
+ !(device_state & ACER_WMID3_GDS_BLUETOOTH));
+ break;
+ }
+ sparse_keymap_report_entry(acer_wmi_input_dev, key,
+ 1, true);
+ }
break;
default:
printk(ACER_WARNING "Unknown function number - %d - %d\n",
}
}
+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;
"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)