From 0606e1abfcb66c3a29722162f5d2c7ad8b638237 Mon Sep 17 00:00:00 2001 From: Carlos Corbacho Date: Wed, 8 Oct 2008 21:40:21 +0100 Subject: acer-wmi: Add rfkill support for wireless and bluetooth This patch implements rfkill support for the wireless and bluetooth devices commonly found on Acer laptops. For now, we will always poll these devices once a second to guarantee we can catch state changes. On newer Acer laptops, it may be possible to rely on WMI events to do this instead, and experimental support for this will be added in a later patch. 3G has been deliberately left off for now, as we still have no way to detect it, (nor, AFAIK, has any Linux user tried the code) and on laptops that don't support 3G, trying to poll for the status will leave the logs full of ACPI tracebacks. The old sysfs interface for wireless and bluetooth will be removed in a later patch. (Thanks to Henrique de Moraes Holschuh and Dmitry Torokhov for reviewing this patch). Signed-off-by: Carlos Corbacho Acked-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/misc/acer-wmi.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) (limited to 'drivers/misc/acer-wmi.c') diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index d8b0d326e45..e3e11e88747 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -157,6 +159,9 @@ struct acer_debug { u32 wmid_devices; }; +static struct rfkill *wireless_rfkill; +static struct rfkill *bluetooth_rfkill; + /* Each low-level interface must define at least some of the following */ struct wmi_interface { /* The WMI device type */ @@ -932,6 +937,125 @@ static void acer_backlight_exit(void) backlight_device_unregister(acer_backlight_device); } +/* + * Rfkill devices + */ +static struct workqueue_struct *rfkill_workqueue; + +static void acer_rfkill_update(struct work_struct *ignored); +static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update); +static void acer_rfkill_update(struct work_struct *ignored) +{ + u32 state; + acpi_status status; + + status = get_u32(&state, ACER_CAP_WIRELESS); + if (ACPI_SUCCESS(status)) + rfkill_force_state(wireless_rfkill, state ? + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED); + + if (has_cap(ACER_CAP_BLUETOOTH)) { + status = get_u32(&state, ACER_CAP_BLUETOOTH); + if (ACPI_SUCCESS(status)) + rfkill_force_state(bluetooth_rfkill, state ? + RFKILL_STATE_UNBLOCKED : + RFKILL_STATE_SOFT_BLOCKED); + } + + queue_delayed_work(rfkill_workqueue, &acer_rfkill_work, + round_jiffies_relative(HZ)); +} + +static int acer_rfkill_set(void *data, enum rfkill_state state) +{ + acpi_status status; + u32 *cap = data; + status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap); + if (ACPI_FAILURE(status)) + return -ENODEV; + return 0; +} + +static struct rfkill * acer_rfkill_register(struct device *dev, +enum rfkill_type type, char *name, u32 cap) +{ + int err; + u32 state; + u32 *data; + struct rfkill *rfkill_dev; + + rfkill_dev = rfkill_allocate(dev, type); + if (!rfkill_dev) + return ERR_PTR(-ENOMEM); + rfkill_dev->name = name; + get_u32(&state, cap); + rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED : + RFKILL_STATE_SOFT_BLOCKED; + data = kzalloc(sizeof(u32), GFP_KERNEL); + if (!data) { + rfkill_free(rfkill_dev); + return ERR_PTR(-ENOMEM); + } + *data = cap; + rfkill_dev->data = data; + rfkill_dev->toggle_radio = acer_rfkill_set; + rfkill_dev->user_claim_unsupported = 1; + + err = rfkill_register(rfkill_dev); + if (err) { + kfree(rfkill_dev->data); + rfkill_free(rfkill_dev); + return ERR_PTR(err); + } + return rfkill_dev; +} + +static int acer_rfkill_init(struct device *dev) +{ + wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, + "acer-wireless", ACER_CAP_WIRELESS); + if (IS_ERR(wireless_rfkill)) + return PTR_ERR(wireless_rfkill); + + if (has_cap(ACER_CAP_BLUETOOTH)) { + bluetooth_rfkill = acer_rfkill_register(dev, + RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", + ACER_CAP_BLUETOOTH); + if (IS_ERR(bluetooth_rfkill)) { + kfree(wireless_rfkill->data); + rfkill_unregister(wireless_rfkill); + return PTR_ERR(bluetooth_rfkill); + } + } + + rfkill_workqueue = create_singlethread_workqueue("rfkill_workqueue"); + if (!rfkill_workqueue) { + if (has_cap(ACER_CAP_BLUETOOTH)) { + kfree(bluetooth_rfkill->data); + rfkill_unregister(bluetooth_rfkill); + } + kfree(wireless_rfkill->data); + rfkill_unregister(wireless_rfkill); + return -ENOMEM; + } + queue_delayed_work(rfkill_workqueue, &acer_rfkill_work, HZ); + + return 0; +} + +static void acer_rfkill_exit(void) +{ + cancel_delayed_work_sync(&acer_rfkill_work); + destroy_workqueue(rfkill_workqueue); + kfree(wireless_rfkill->data); + rfkill_unregister(wireless_rfkill); + if (has_cap(ACER_CAP_BLUETOOTH)) { + kfree(wireless_rfkill->data); + rfkill_unregister(bluetooth_rfkill); + } + return; +} + /* * Read/ write bool sysfs macro */ @@ -1026,7 +1150,9 @@ static int __devinit acer_platform_probe(struct platform_device *device) goto error_brightness; } - return 0; + err = acer_rfkill_init(&device->dev); + + return err; error_brightness: acer_led_exit(); @@ -1040,6 +1166,8 @@ static int acer_platform_remove(struct platform_device *device) acer_led_exit(); if (has_cap(ACER_CAP_BRIGHTNESS)) acer_backlight_exit(); + + acer_rfkill_exit(); return 0; } -- cgit v1.2.3-70-g09d2 From 7d9a06de616f69374dab00396f27cf00962a72c9 Mon Sep 17 00:00:00 2001 From: Carlos Corbacho Date: Wed, 8 Oct 2008 21:40:26 +0100 Subject: acer-wmi: Remove wireless and bluetooth sysfs entries These are now replaced by the rfkill interface. Signed-off-by: Carlos Corbacho Signed-off-by: Len Brown --- Documentation/laptops/acer-wmi.txt | 28 ++-------- drivers/misc/acer-wmi.c | 101 ++++++++----------------------------- 2 files changed, 25 insertions(+), 104 deletions(-) (limited to 'drivers/misc/acer-wmi.c') diff --git a/Documentation/laptops/acer-wmi.txt b/Documentation/laptops/acer-wmi.txt index 69b5dd4e5a5..2b3a6b5260b 100644 --- a/Documentation/laptops/acer-wmi.txt +++ b/Documentation/laptops/acer-wmi.txt @@ -1,7 +1,7 @@ Acer Laptop WMI Extras Driver http://code.google.com/p/aceracpi -Version 0.1 -9th February 2008 +Version 0.2 +18th August 2008 Copyright 2007-2008 Carlos Corbacho @@ -87,17 +87,7 @@ acer-wmi come with built-in wireless. However, should you feel so inclined to ever wish to remove the card, or swap it out at some point, please get in touch with me, as we may well be able to gain some data on wireless card detection. -To read the status of the wireless radio (0=off, 1=on): -cat /sys/devices/platform/acer-wmi/wireless - -To enable the wireless radio: -echo 1 > /sys/devices/platform/acer-wmi/wireless - -To disable the wireless radio: -echo 0 > /sys/devices/platform/acer-wmi/wireless - -To set the state of the wireless radio when loading acer-wmi, pass: -wireless=X (where X is 0 or 1) +The wireless radio is exposed through rfkill. Bluetooth ********* @@ -117,17 +107,7 @@ For the adventurously minded - if you want to buy an internal bluetooth module off the internet that is compatible with your laptop and fit it, then it will work just fine with acer-wmi. -To read the status of the bluetooth module (0=off, 1=on): -cat /sys/devices/platform/acer-wmi/wireless - -To enable the bluetooth module: -echo 1 > /sys/devices/platform/acer-wmi/bluetooth - -To disable the bluetooth module: -echo 0 > /sys/devices/platform/acer-wmi/bluetooth - -To set the state of the bluetooth module when loading acer-wmi, pass: -bluetooth=X (where X is 0 or 1) +Bluetooth is exposed through rfkill. 3G ** diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index e3e11e88747..8708d319378 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -125,21 +125,15 @@ enum interface_flags { static int max_brightness = 0xF; -static int wireless = -1; -static int bluetooth = -1; static int mailled = -1; static int brightness = -1; static int threeg = -1; static int force_series; module_param(mailled, int, 0444); -module_param(wireless, int, 0444); -module_param(bluetooth, int, 0444); module_param(brightness, int, 0444); module_param(threeg, int, 0444); module_param(force_series, int, 0444); -MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware"); -MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware"); 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"); @@ -147,8 +141,6 @@ MODULE_PARM_DESC(force_series, "Force a different laptop series"); struct acer_data { int mailled; - int wireless; - int bluetooth; int threeg; int brightness; }; @@ -851,8 +843,6 @@ static void __init acer_commandline_init(void) * capability isn't available on the given interface */ set_u32(mailled, ACER_CAP_MAILLED); - set_u32(wireless, ACER_CAP_WIRELESS); - set_u32(bluetooth, ACER_CAP_BLUETOOTH); set_u32(threeg, ACER_CAP_THREEG); set_u32(brightness, ACER_CAP_BRIGHTNESS); } @@ -1057,40 +1047,30 @@ static void acer_rfkill_exit(void) } /* - * Read/ write bool sysfs macro + * sysfs interface */ -#define show_set_bool(value, cap) \ -static ssize_t \ -show_bool_##value(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ +static ssize_t show_bool_threeg(struct device *dev, + struct device_attribute *attr, char *buf) +{ u32 result; \ - acpi_status status = get_u32(&result, cap); \ - if (ACPI_SUCCESS(status)) \ - return sprintf(buf, "%u\n", result); \ - return sprintf(buf, "Read error\n"); \ -} \ -\ -static ssize_t \ -set_bool_##value(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - u32 tmp = simple_strtoul(buf, NULL, 10); \ - acpi_status status = set_u32(tmp, cap); \ - if (ACPI_FAILURE(status)) \ - return -EINVAL; \ - return count; \ -} \ -static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ - show_bool_##value, set_bool_##value); - -show_set_bool(wireless, ACER_CAP_WIRELESS); -show_set_bool(bluetooth, ACER_CAP_BLUETOOTH); -show_set_bool(threeg, ACER_CAP_THREEG); + acpi_status status = get_u32(&result, ACER_CAP_THREEG); + if (ACPI_SUCCESS(status)) + return sprintf(buf, "%u\n", result); + return sprintf(buf, "Read error\n"); +} + +static ssize_t set_bool_threeg(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u32 tmp = simple_strtoul(buf, NULL, 10); + acpi_status status = set_u32(tmp, ACER_CAP_THREEG); + if (ACPI_FAILURE(status)) + return -EINVAL; + return count; +} +static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, + set_bool_threeg); -/* - * Read interface sysfs macro - */ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1180,16 +1160,6 @@ pm_message_t state) if (!data) return -ENOMEM; - if (has_cap(ACER_CAP_WIRELESS)) { - get_u32(&value, ACER_CAP_WIRELESS); - data->wireless = value; - } - - if (has_cap(ACER_CAP_BLUETOOTH)) { - get_u32(&value, ACER_CAP_BLUETOOTH); - data->bluetooth = value; - } - if (has_cap(ACER_CAP_MAILLED)) { get_u32(&value, ACER_CAP_MAILLED); data->mailled = value; @@ -1210,15 +1180,6 @@ static int acer_platform_resume(struct platform_device *device) if (!data) return -ENOMEM; - if (has_cap(ACER_CAP_WIRELESS)) - set_u32(data->wireless, ACER_CAP_WIRELESS); - - if (has_cap(ACER_CAP_BLUETOOTH)) - set_u32(data->bluetooth, ACER_CAP_BLUETOOTH); - - if (has_cap(ACER_CAP_THREEG)) - set_u32(data->threeg, ACER_CAP_THREEG); - if (has_cap(ACER_CAP_MAILLED)) set_u32(data->mailled, ACER_CAP_MAILLED); @@ -1243,12 +1204,6 @@ static struct platform_device *acer_platform_device; static int remove_sysfs(struct platform_device *device) { - if (has_cap(ACER_CAP_WIRELESS)) - device_remove_file(&device->dev, &dev_attr_wireless); - - if (has_cap(ACER_CAP_BLUETOOTH)) - device_remove_file(&device->dev, &dev_attr_bluetooth); - if (has_cap(ACER_CAP_THREEG)) device_remove_file(&device->dev, &dev_attr_threeg); @@ -1261,20 +1216,6 @@ static int create_sysfs(void) { int retval = -ENOMEM; - if (has_cap(ACER_CAP_WIRELESS)) { - retval = device_create_file(&acer_platform_device->dev, - &dev_attr_wireless); - if (retval) - goto error_sysfs; - } - - if (has_cap(ACER_CAP_BLUETOOTH)) { - retval = device_create_file(&acer_platform_device->dev, - &dev_attr_bluetooth); - if (retval) - goto error_sysfs; - } - if (has_cap(ACER_CAP_THREEG)) { retval = device_create_file(&acer_platform_device->dev, &dev_attr_threeg); -- cgit v1.2.3-70-g09d2 From ae3a1b46099aa87f1662621580c985432ca1c9d9 Mon Sep 17 00:00:00 2001 From: Carlos Corbacho Date: Fri, 10 Oct 2008 17:33:35 +0100 Subject: acer-wmi: Remove private workqueue As per Dmitry Torokhov's suggestion, acer-wmi doesn't need a private workqueue, so remove it. Signed-off-by: Carlos Corbacho Cc: Dmitry Torokhov Signed-off-by: Len Brown --- drivers/misc/acer-wmi.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers/misc/acer-wmi.c') diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index 8708d319378..cf4c39fbc66 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -930,8 +930,6 @@ static void acer_backlight_exit(void) /* * Rfkill devices */ -static struct workqueue_struct *rfkill_workqueue; - static void acer_rfkill_update(struct work_struct *ignored); static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update); static void acer_rfkill_update(struct work_struct *ignored) @@ -952,8 +950,7 @@ static void acer_rfkill_update(struct work_struct *ignored) RFKILL_STATE_SOFT_BLOCKED); } - queue_delayed_work(rfkill_workqueue, &acer_rfkill_work, - round_jiffies_relative(HZ)); + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); } static int acer_rfkill_set(void *data, enum rfkill_state state) @@ -1018,17 +1015,7 @@ static int acer_rfkill_init(struct device *dev) } } - rfkill_workqueue = create_singlethread_workqueue("rfkill_workqueue"); - if (!rfkill_workqueue) { - if (has_cap(ACER_CAP_BLUETOOTH)) { - kfree(bluetooth_rfkill->data); - rfkill_unregister(bluetooth_rfkill); - } - kfree(wireless_rfkill->data); - rfkill_unregister(wireless_rfkill); - return -ENOMEM; - } - queue_delayed_work(rfkill_workqueue, &acer_rfkill_work, HZ); + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; } @@ -1036,7 +1023,6 @@ static int acer_rfkill_init(struct device *dev) static void acer_rfkill_exit(void) { cancel_delayed_work_sync(&acer_rfkill_work); - destroy_workqueue(rfkill_workqueue); kfree(wireless_rfkill->data); rfkill_unregister(wireless_rfkill); if (has_cap(ACER_CAP_BLUETOOTH)) { -- cgit v1.2.3-70-g09d2 From 08237974af22a97da59869979ef1a515524d5cc3 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 8 Aug 2008 11:57:11 +0800 Subject: ACPI: replace AE_BAD_ADDRESS exception code with AE_ERROR The AE_BAD_ADDRESS exception code is now unused in ACPICA. For linux, it's only used at wmi.c and acer-wmi.c. I checked both wmi.c and acer-wmi.c, the AE_BAD_ADDRESS exception code has no special meaning. The parent functions just call AE_SUCCESS() or AE_FAILURE() to check the return status. So it's safe to replace AE_BAD_ADDRESS with AE_ERROR. Signed-off-by Lin Ming Signed-off-by: Andi Kleen Signed-off-by: Len Brown --- drivers/acpi/wmi.c | 10 +++++----- drivers/misc/acer-wmi.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/misc/acer-wmi.c') diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c index cfe2c833474..5b94be38861 100644 --- a/drivers/acpi/wmi.c +++ b/drivers/acpi/wmi.c @@ -242,7 +242,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) char method[4] = "WM"; if (!find_guid(guid_string, &wblock)) - return AE_BAD_ADDRESS; + return AE_ERROR; block = &wblock->gblock; handle = wblock->handle; @@ -304,7 +304,7 @@ struct acpi_buffer *out) return AE_BAD_PARAMETER; if (!find_guid(guid_string, &wblock)) - return AE_BAD_ADDRESS; + return AE_ERROR; block = &wblock->gblock; handle = wblock->handle; @@ -314,7 +314,7 @@ struct acpi_buffer *out) /* Check GUID is a data block */ if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) - return AE_BAD_ADDRESS; + return AE_ERROR; input.count = 1; input.pointer = wq_params; @@ -385,7 +385,7 @@ const struct acpi_buffer *in) return AE_BAD_DATA; if (!find_guid(guid_string, &wblock)) - return AE_BAD_ADDRESS; + return AE_ERROR; block = &wblock->gblock; handle = wblock->handle; @@ -395,7 +395,7 @@ const struct acpi_buffer *in) /* Check GUID is a data block */ if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) - return AE_BAD_ADDRESS; + return AE_ERROR; input.count = 2; input.pointer = params; diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index d8b0d326e45..a67aef5e284 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -476,7 +476,7 @@ struct wmi_interface *iface) } break; default: - return AE_BAD_ADDRESS; + return AE_ERROR; } return AE_OK; } @@ -514,7 +514,7 @@ static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface) break; } default: - return AE_BAD_ADDRESS; + return AE_ERROR; } /* Actually do the set */ @@ -689,7 +689,7 @@ struct wmi_interface *iface) return 0; } default: - return AE_BAD_ADDRESS; + return AE_ERROR; } status = WMI_execute_u32(method_id, 0, &result); @@ -735,7 +735,7 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) } break; default: - return AE_BAD_ADDRESS; + return AE_ERROR; } return WMI_execute_u32(method_id, (u32)value, NULL); } @@ -785,7 +785,7 @@ static struct wmi_interface wmid_interface = { static acpi_status get_u32(u32 *value, u32 cap) { - acpi_status status = AE_BAD_ADDRESS; + acpi_status status = AE_ERROR; switch (interface->type) { case ACER_AMW0: -- cgit v1.2.3-70-g09d2