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

Commit a195dcdc authored by Matthew Garrett's avatar Matthew Garrett Committed by Len Brown
Browse files

eeepc-laptop: Use standard interfaces



eeepc-laptop currently only sends key events via ACPI and has
non-standard rfkill control. Add an input device and use the rfkill
infrastructure.

Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 3fa8749e
Loading
Loading
Loading
Loading
+209 −19
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/rfkill.h>

#define EEEPC_LAPTOP_VERSION	"0.1"

@@ -125,6 +127,10 @@ struct eeepc_hotk {
					   by this BIOS */
	uint init_flag;			/* Init flags */
	u16 event_count[128];		/* count for each event */
	struct input_dev *inputdev;
	u16 *keycode_map;
	struct rfkill *eeepc_wlan_rfkill;
	struct rfkill *eeepc_bluetooth_rfkill;
};

/* The actual device the driver binds to */
@@ -140,6 +146,27 @@ static struct platform_driver platform_driver = {

static struct platform_device *platform_device;

struct key_entry {
	char type;
	u8 code;
	u16 keycode;
};

enum { KE_KEY, KE_END };

static struct key_entry eeepc_keymap[] = {
	/* Sleep already handled via generic ACPI code */
	{KE_KEY, 0x10, KEY_WLAN },
	{KE_KEY, 0x12, KEY_PROG1 },
	{KE_KEY, 0x13, KEY_MUTE },
	{KE_KEY, 0x14, KEY_VOLUMEDOWN },
	{KE_KEY, 0x15, KEY_VOLUMEUP },
	{KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
	{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
	{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
	{KE_END, 0},
};

/*
 * The hotkey driver declaration
 */
@@ -260,6 +287,44 @@ static int update_bl_status(struct backlight_device *bd)
	return set_brightness(bd, bd->props.brightness);
}

/*
 * Rfkill helpers
 */

static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
{
	if (state == RFKILL_STATE_SOFT_BLOCKED)
		return set_acpi(CM_ASL_WLAN, 0);
	else
		return set_acpi(CM_ASL_WLAN, 1);
}

static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
{
	if (get_acpi(CM_ASL_WLAN) == 1)
		*state = RFKILL_STATE_UNBLOCKED;
	else
		*state = RFKILL_STATE_SOFT_BLOCKED;
	return 0;
}

static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
{
	if (state == RFKILL_STATE_SOFT_BLOCKED)
		return set_acpi(CM_ASL_BLUETOOTH, 0);
	else
		return set_acpi(CM_ASL_BLUETOOTH, 1);
}

static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
{
	if (get_acpi(CM_ASL_BLUETOOTH) == 1)
		*state = RFKILL_STATE_UNBLOCKED;
	else
		*state = RFKILL_STATE_SOFT_BLOCKED;
	return 0;
}

/*
 * Sys helpers
 */
@@ -311,13 +376,11 @@ static ssize_t show_sys_acpi(int cm, char *buf)
EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
EEEPC_CREATE_DEVICE_ATTR(wlan, CM_ASL_WLAN);

static struct attribute *platform_attributes[] = {
	&dev_attr_camera.attr,
	&dev_attr_cardr.attr,
	&dev_attr_disp.attr,
	&dev_attr_wlan.attr,
	NULL
};

@@ -328,8 +391,64 @@ static struct attribute_group platform_attribute_group = {
/*
 * Hotkey functions
 */
static struct key_entry *eepc_get_entry_by_scancode(int code)
{
	struct key_entry *key;

	for (key = eeepc_keymap; key->type != KE_END; key++)
		if (code == key->code)
			return key;

	return NULL;
}

static struct key_entry *eepc_get_entry_by_keycode(int code)
{
	struct key_entry *key;

	for (key = eeepc_keymap; key->type != KE_END; key++)
		if (code == key->keycode && key->type == KE_KEY)
			return key;

	return NULL;
}

static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
{
	struct key_entry *key = eepc_get_entry_by_scancode(scancode);

	if (key && key->type == KE_KEY) {
		*keycode = key->keycode;
		return 0;
	}

	return -EINVAL;
}

static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
	struct key_entry *key;
	int old_keycode;

	if (keycode < 0 || keycode > KEY_MAX)
		return -EINVAL;

	key = eepc_get_entry_by_scancode(scancode);
	if (key && key->type == KE_KEY) {
		old_keycode = key->keycode;
		key->keycode = keycode;
		set_bit(keycode, dev->keybit);
		if (!eepc_get_entry_by_keycode(old_keycode))
			clear_bit(old_keycode, dev->keybit);
		return 0;
	}

	return -EINVAL;
}

static int eeepc_hotk_check(void)
{
	const struct key_entry *key;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	int result;

@@ -356,6 +475,31 @@ static int eeepc_hotk_check(void)
			       "Get control methods supported: 0x%x\n",
			       ehotk->cm_supported);
		}
		ehotk->inputdev = input_allocate_device();
		if (!ehotk->inputdev) {
			printk(EEEPC_INFO "Unable to allocate input device\n");
			return 0;
		}
		ehotk->inputdev->name = "Asus EeePC extra buttons";
		ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
		ehotk->inputdev->id.bustype = BUS_HOST;
		ehotk->inputdev->getkeycode = eeepc_getkeycode;
		ehotk->inputdev->setkeycode = eeepc_setkeycode;

		for (key = eeepc_keymap; key->type != KE_END; key++) {
			switch (key->type) {
			case KE_KEY:
				set_bit(EV_KEY, ehotk->inputdev->evbit);
				set_bit(key->keycode, ehotk->inputdev->keybit);
				break;
			}
		}
		result = input_register_device(ehotk->inputdev);
		if (result) {
			printk(EEEPC_INFO "Unable to register input device\n");
			input_free_device(ehotk->inputdev);
			return 0;
		}
	} else {
		printk(EEEPC_ERR "Hotkey device not present, aborting\n");
		return -EINVAL;
@@ -363,21 +507,6 @@ static int eeepc_hotk_check(void)
	return 0;
}

static void notify_wlan(u32 *event)
{
	/* if DISABLE_ASL_WLAN is set, the notify code for fn+f2
	   will always be 0x10 */
	if (ehotk->cm_supported & (0x1 << CM_ASL_WLAN)) {
		const char *method = cm_getv[CM_ASL_WLAN];
		int value;
		if (read_acpi_int(ehotk->handle, method, &value))
			printk(EEEPC_WARNING "Error reading %s\n",
			       method);
		else if (value == 1)
			*event = 0x11;
	}
}

static void notify_brn(void)
{
	struct backlight_device *bd = eeepc_backlight_device;
@@ -386,14 +515,28 @@ static void notify_brn(void)

static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
	static struct key_entry *key;
	if (!ehotk)
		return;
	if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag))
		notify_wlan(&event);
	if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
		notify_brn();
	acpi_bus_generate_proc_event(ehotk->device, event,
				     ehotk->event_count[event % 128]++);
	if (ehotk->inputdev) {
		key = eepc_get_entry_by_scancode(event);
		if (key) {
			switch (key->type) {
			case KE_KEY:
				input_report_key(ehotk->inputdev, key->keycode,
						 1);
				input_sync(ehotk->inputdev);
				input_report_key(ehotk->inputdev, key->keycode,
						 0);
				input_sync(ehotk->inputdev);
				break;
			}
		}
	}
}

static int eeepc_hotk_add(struct acpi_device *device)
@@ -420,6 +563,47 @@ static int eeepc_hotk_add(struct acpi_device *device)
					     eeepc_hotk_notify, ehotk);
	if (ACPI_FAILURE(status))
		printk(EEEPC_ERR "Error installing notify handler\n");

	if (get_acpi(CM_ASL_WLAN) != -1) {
		ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
							   RFKILL_TYPE_WLAN);

		if (!ehotk->eeepc_wlan_rfkill)
			goto end;

		ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
		ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
		ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
		if (get_acpi(CM_ASL_WLAN) == 1)
			ehotk->eeepc_wlan_rfkill->state =
				RFKILL_STATE_UNBLOCKED;
		else
			ehotk->eeepc_wlan_rfkill->state =
				RFKILL_STATE_SOFT_BLOCKED;
		rfkill_register(ehotk->eeepc_wlan_rfkill);
	}

	if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
		ehotk->eeepc_bluetooth_rfkill =
			rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);

		if (!ehotk->eeepc_bluetooth_rfkill)
			goto end;

		ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
		ehotk->eeepc_bluetooth_rfkill->toggle_radio =
			eeepc_bluetooth_rfkill_set;
		ehotk->eeepc_bluetooth_rfkill->get_state =
			eeepc_bluetooth_rfkill_state;
		if (get_acpi(CM_ASL_BLUETOOTH) == 1)
			ehotk->eeepc_bluetooth_rfkill->state =
				RFKILL_STATE_UNBLOCKED;
		else
			ehotk->eeepc_bluetooth_rfkill->state =
				RFKILL_STATE_SOFT_BLOCKED;
		rfkill_register(ehotk->eeepc_bluetooth_rfkill);
	}

 end:
	if (result) {
		kfree(ehotk);
@@ -553,6 +737,12 @@ static void eeepc_backlight_exit(void)
{
	if (eeepc_backlight_device)
		backlight_device_unregister(eeepc_backlight_device);
	if (ehotk->inputdev)
		input_unregister_device(ehotk->inputdev);
	if (ehotk->eeepc_wlan_rfkill)
		rfkill_unregister(ehotk->eeepc_wlan_rfkill);
	if (ehotk->eeepc_bluetooth_rfkill)
		rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
	eeepc_backlight_device = NULL;
}