Re: rfkill-input understanding help

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Well, the good news is that the code I have appears to work. But, I have 
something very strange going on with my kernel (latest ACPI git tree + 
acer-wmi on top) and rfkill-input.

Basically, if I either:

1) Build rfkill-input into the kernel

or

2) Build rfkill-input as a module, and use kmod to load it from acer-wmi

Then rfkill-input doesn't work.

If I load rfkill-input manually (or if already loaded by kmod; unload and
then reload), everything works as expected. The (in progress) code to add
rfkill support to acer-wmi is here (in case the problem is something wrong
with the code) - as you can see, it's mostly based on the b43 work.

My kernel config is here, in case I'm missing something obvious:
http://files.strangeworlds.co.uk/config-acpi-git-20080111

For reference, the patch series that this ('acpi-wmi-rfkill') is part of
(of which only 'wmi' and 'acpi-wmi' are relevant) is here:
http://files.strangeworlds.co.uk/wmi/

-Carlos
---
acer-wmi: Add rfkill support

From: Carlos Corbacho <carlos@xxxxxxxxxxxxxxxxxxx>

Add initial rfkill support for acer-wmi
---

 drivers/misc/acer-wmi.c |  215 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 214 insertions(+), 1 deletions(-)


diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
index 6150120..5f6adc0 100644
--- a/drivers/misc/acer-wmi.c
+++ b/drivers/misc/acer-wmi.c
@@ -34,6 +34,9 @@
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/i8042.h>
+#include <linux/rfkill.h>
+#include <linux/input-polldev.h>
+#include <linux/kmod.h>
 
 #include <acpi/acpi_drivers.h>
 
@@ -710,6 +713,213 @@ static void __init acer_commandline_init(void)
 }
 
 /*
+ * RF Kill devices
+ */
+struct acer_rfkill {
+	struct rfkill *wireless;
+	u32 wcap;
+	struct rfkill *bluetooth;
+	u32 bcap;
+};
+
+static struct acer_rfkill acer_rfk;
+
+struct acer_input {
+	struct input_polled_dev *wireless;
+	struct input_polled_dev *bluetooth;
+};
+
+static struct acer_input acer_poll;
+
+static void acer_rfkill_poll(struct input_polled_dev *poll_dev)
+{
+	struct rfkill *rfk = poll_dev->private;
+	u32 cap = *((u32 *) rfk->data);
+	u32 enabled, keycode, *state;
+
+	get_u32(&enabled, cap);
+
+	switch (cap) {
+	case ACER_CAP_WIRELESS:
+		keycode = KEY_WLAN;
+		state = &interface->data.wireless;
+		break;
+	case ACER_CAP_BLUETOOTH:
+		keycode = KEY_BLUETOOTH;
+		state = &interface->data.bluetooth;
+		break;
+	default:
+		return;
+	}
+
+	if (unlikely(enabled != *state)) {
+		printk(ACER_INFO "Radio state changed\n");
+		*state = enabled;
+		input_report_key(poll_dev->input, keycode, 1);
+		input_report_key(poll_dev->input, keycode, 0);
+	}
+}
+
+static int acer_rfkill_toggle(void *data, enum rfkill_state rfk_state)
+{
+	u32 cap = *((u32 *) data);
+	u32 *state;
+
+	printk(ACER_INFO "Setting cap for %u\n", cap);
+
+	switch (cap) {
+	case ACER_CAP_WIRELESS:
+		state = &interface->data.wireless;
+		break;
+	case ACER_CAP_BLUETOOTH:
+		state = &interface->data.bluetooth;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	switch (rfk_state) {
+	case RFKILL_STATE_ON:
+		printk(ACER_INFO "Turning on\n");
+		set_u32(1, cap);
+		*state = 1;
+		break;
+	case RFKILL_STATE_OFF:
+		printk(ACER_INFO "Turning off\n");
+		set_u32(0, cap);
+		*state = 0;
+		break;
+	}
+
+	return 0;
+}
+
+static int acer_rfkill_init_device(struct device **dev, struct rfkill **rfk_dev,
+struct input_polled_dev **rfk_poll_dev, u32 cap, u32 type, char *name)
+{
+	int err;
+	u32 *data;
+	u32 keycode = 0;
+	struct rfkill *rfk;
+	struct input_polled_dev *rfk_poll;
+
+	printk(ACER_INFO "Allocating rfkill device\n");
+	rfk = rfkill_allocate(*dev, type);
+	if (!rfk_dev)
+		goto error;
+
+	rfk->name = name;
+	rfk->state = RFKILL_STATE_ON;
+	rfk->toggle_radio = acer_rfkill_toggle;
+	data = kzalloc(sizeof(u32), GFP_KERNEL);
+	if (!data)
+		goto error_free_rfk;
+
+	*data = cap;
+	rfk->data = data;
+
+	printk(ACER_INFO "Allocating polled device\n");
+	rfk_poll = input_allocate_polled_device();
+	if (!rfk_poll)
+		goto error_free_rfk;
+
+	rfk_poll->private = rfk;
+	rfk_poll->poll = acer_rfkill_poll;
+	rfk_poll->poll_interval = 1000;
+
+	rfk_poll->input->name = rfk->name;
+	rfk_poll->input->id.bustype = BUS_HOST;
+	rfk_poll->input->evbit[0] = BIT(EV_KEY);
+
+	switch (cap) {
+	case ACER_CAP_WIRELESS:
+		keycode = KEY_WLAN;
+		break;
+	case ACER_CAP_BLUETOOTH:
+		keycode = KEY_BLUETOOTH;
+		break;
+	}
+	set_bit(keycode, rfk_poll->input->keybit);
+
+	printk(ACER_INFO "Registering rfkill device\n");
+	err = rfkill_register(rfk);
+	if (err)
+		goto error_free_polldev;
+
+	err = input_register_polled_device(rfk_poll);
+	if (err)
+		goto error_unreg_rfk;
+
+	*rfk_dev = rfk;
+	*rfk_poll_dev = rfk_poll;
+
+	return 0;
+
+error_unreg_rfk:
+	rfkill_unregister(*rfk_dev);
+error_free_polldev:
+	input_free_polled_device(*rfk_poll_dev);
+error_free_rfk:
+	kfree(data);
+	rfkill_free(*rfk_dev);
+error:
+	return -ENODEV;
+}
+
+static int acer_rfkill_init(struct device *dev)
+{
+	int err;
+
+	printk(ACER_INFO "Initialising RFKILL devices\n");
+
+	if (has_cap(ACER_CAP_WIRELESS)) {
+		err = acer_rfkill_init_device(&dev, &acer_rfk.wireless,
+			&acer_poll.wireless, ACER_CAP_WIRELESS,
+			RFKILL_TYPE_WLAN, "acer-wireless");
+		if (err)
+			return -ENODEV;
+	}
+
+	if (has_cap(ACER_CAP_BLUETOOTH)) {
+		err = acer_rfkill_init_device(&dev, &acer_rfk.bluetooth,
+			&acer_poll.bluetooth, ACER_CAP_BLUETOOTH,
+			RFKILL_TYPE_BLUETOOTH, "acer-bluetooth");
+		if (err)
+			return -ENODEV;
+	}
+
+#ifdef CONFIG_RFKILL_INPUT_MODULE
+	/* acer-wmi RF-kill isn't useful without the rfkill-input subsystem.
+	 * Try to load the module. */
+	err = request_module("rfkill-input");
+	if (err)
+		printk(ACER_INFO "Failed to load the rfkill-input module. "
+			"The built-in radio LED will not work.\n");
+#endif /* CONFIG_RFKILL_INPUT */
+
+	return 0;
+}
+
+static void acer_rfkill_exit_device(struct rfkill *rfk_dev,
+struct input_polled_dev *rfk_poll_dev)
+{
+	input_unregister_polled_device(rfk_poll_dev);
+	rfkill_unregister(rfk_dev);
+	input_free_polled_device(rfk_poll_dev);
+	kfree(rfk_dev->data);
+	rfkill_free(rfk_dev);
+}
+
+static void acer_rfkill_exit(void)
+{
+	if (has_cap(ACER_CAP_WIRELESS))
+		acer_rfkill_exit_device(acer_rfk.wireless, acer_poll.wireless);
+
+	if (has_cap(ACER_CAP_BLUETOOTH))
+		acer_rfkill_exit_device(acer_rfk.bluetooth, acer_poll.bluetooth);
+}
+
+/*
  * LED device (Mail LED only, no other LEDs known yet)
  */
 static void mail_led_set(struct led_classdev *led_cdev,
@@ -842,7 +1052,8 @@ static int __devinit acer_platform_probe(struct platform_device *device)
 		acer_led_init(&device->dev);
 	if (has_cap(ACER_CAP_BRIGHTNESS))
 		acer_backlight_init(&device->dev);
-	return 0;
+
+	return acer_rfkill_init(&device->dev);
 }
 
 static int acer_platform_remove(struct platform_device *device)
@@ -851,6 +1062,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;
 }
 
-
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux