Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba48fdb9 authored by Corentin Chary's avatar Corentin Chary Committed by Matthew Garrett
Browse files

eeepc-wmi: add rfkill support for wlan, bluetooth and 3g



wimax support is missing because I don't have any DSDT
with WMI and wimax support.

Most of the code comes from eeepc-laptop.

Signed-off-by: default avatarCorentin Chary <corentincj@iksaif.net>
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
parent 084fca63
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -425,6 +425,7 @@ config EEEPC_WMI
	depends on INPUT
	depends on EXPERIMENTAL
	depends on BACKLIGHT_CLASS_DEVICE
	depends on RFKILL || RFKILL = n
	select INPUT_SPARSEKMAP
	select LEDS_CLASS
	select NEW_LEDS
+138 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
#include <linux/platform_device.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -62,6 +63,9 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);

#define EEEPC_WMI_DEVID_BACKLIGHT	0x00050012
#define EEEPC_WMI_DEVID_TPDLED		0x00100011
#define EEEPC_WMI_DEVID_WLAN		0x00010011
#define EEEPC_WMI_DEVID_BLUETOOTH	0x00010013
#define EEEPC_WMI_DEVID_WWAN3G		0x00010019

static const struct key_entry eeepc_wmi_keymap[] = {
	/* Sleep already handled via generic ACPI code */
@@ -94,6 +98,10 @@ struct eeepc_wmi {
	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;
};

/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
@@ -279,6 +287,129 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
		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);
}

static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
{
	int dev_id = (unsigned long)data;
	u32 ctrl_param;
	acpi_status status;

	status = eeepc_wmi_get_devstate(dev_id, &ctrl_param);

	if (ACPI_FAILURE(status))
		return ;

	rfkill_set_sw_state(rfkill, !(ctrl_param & 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 ctrl_param;
	acpi_status status;

	status = eeepc_wmi_get_devstate(dev_id, &ctrl_param);

	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 (!ctrl_param || ctrl_param == 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, !(ctrl_param & 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
 */
@@ -512,6 +643,10 @@ static struct platform_device * __init eeepc_wmi_add(void)
	if (err)
		goto fail_leds;

	err = eeepc_wmi_rfkill_init(eeepc);
	if (err)
		goto fail_rfkill;

	if (!acpi_video_backlight_support()) {
		err = eeepc_wmi_backlight_init(eeepc);
		if (err)
@@ -533,6 +668,8 @@ static struct platform_device * __init eeepc_wmi_add(void)
fail_wmi_handler:
	eeepc_wmi_backlight_exit(eeepc);
fail_backlight:
	eeepc_wmi_rfkill_exit(eeepc);
fail_rfkill:
	eeepc_wmi_led_exit(eeepc);
fail_leds:
	eeepc_wmi_input_exit(eeepc);
@@ -552,6 +689,7 @@ static int eeepc_wmi_remove(struct platform_device *device)
	eeepc_wmi_backlight_exit(eeepc);
	eeepc_wmi_input_exit(eeepc);
	eeepc_wmi_led_exit(eeepc);
	eeepc_wmi_rfkill_exit(eeepc);
	eeepc_wmi_platform_exit(eeepc);

	kfree(eeepc);