eeepc-laptop: Use standard interfaces
authorMatthew Garrett <mjg59@srcf.ucam.org>
Tue, 19 Aug 2008 11:13:20 +0000 (12:13 +0100)
committerLen Brown <len.brown@intel.com>
Fri, 10 Oct 2008 21:55:46 +0000 (17:55 -0400)
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: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/misc/eeepc-laptop.c

index 1ee8501..5baa10b 100644 (file)
@@ -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;
 }