topstar-laptop.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * ACPI driver for Topstar notebooks (hotkeys support only)
  3. *
  4. * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
  5. *
  6. * Implementation inspired by existing x86 platform drivers, in special
  7. * asus/eepc/fujitsu-laptop, thanks to their authors
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. */
  13. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/init.h>
  17. #include <linux/slab.h>
  18. #include <linux/acpi.h>
  19. #include <linux/input.h>
  20. #include <linux/input/sparse-keymap.h>
  21. #define ACPI_TOPSTAR_CLASS "topstar"
  22. struct topstar_hkey {
  23. struct input_dev *inputdev;
  24. };
  25. static const struct key_entry topstar_keymap[] = {
  26. { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
  27. { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
  28. { KE_KEY, 0x83, { KEY_VOLUMEUP } },
  29. { KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
  30. { KE_KEY, 0x85, { KEY_MUTE } },
  31. { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
  32. { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
  33. { KE_KEY, 0x88, { KEY_WLAN } },
  34. { KE_KEY, 0x8a, { KEY_WWW } },
  35. { KE_KEY, 0x8b, { KEY_MAIL } },
  36. { KE_KEY, 0x8c, { KEY_MEDIA } },
  37. /* Known non hotkey events don't handled or that we don't care yet */
  38. { KE_IGNORE, 0x82, }, /* backlight event */
  39. { KE_IGNORE, 0x8e, },
  40. { KE_IGNORE, 0x8f, },
  41. { KE_IGNORE, 0x90, },
  42. /*
  43. * 'G key' generate two event codes, convert to only
  44. * one event/key code for now, consider replacing by
  45. * a switch (3G switch - SW_3G?)
  46. */
  47. { KE_KEY, 0x96, { KEY_F14 } },
  48. { KE_KEY, 0x97, { KEY_F14 } },
  49. { KE_END, 0 }
  50. };
  51. static void acpi_topstar_notify(struct acpi_device *device, u32 event)
  52. {
  53. static bool dup_evnt[2];
  54. bool *dup;
  55. struct topstar_hkey *hkey = acpi_driver_data(device);
  56. /* 0x83 and 0x84 key events comes duplicated... */
  57. if (event == 0x83 || event == 0x84) {
  58. dup = &dup_evnt[event - 0x83];
  59. if (*dup) {
  60. *dup = false;
  61. return;
  62. }
  63. *dup = true;
  64. }
  65. if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
  66. pr_info("unknown event = 0x%02x\n", event);
  67. }
  68. static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
  69. {
  70. acpi_status status;
  71. status = acpi_execute_simple_method(device->handle, "FNCX",
  72. state ? 0x86 : 0x87);
  73. if (ACPI_FAILURE(status)) {
  74. pr_err("Unable to switch FNCX notifications\n");
  75. return -ENODEV;
  76. }
  77. return 0;
  78. }
  79. static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
  80. {
  81. struct input_dev *input;
  82. int error;
  83. input = input_allocate_device();
  84. if (!input)
  85. return -ENOMEM;
  86. input->name = "Topstar Laptop extra buttons";
  87. input->phys = "topstar/input0";
  88. input->id.bustype = BUS_HOST;
  89. error = sparse_keymap_setup(input, topstar_keymap, NULL);
  90. if (error) {
  91. pr_err("Unable to setup input device keymap\n");
  92. goto err_free_dev;
  93. }
  94. error = input_register_device(input);
  95. if (error) {
  96. pr_err("Unable to register input device\n");
  97. goto err_free_keymap;
  98. }
  99. hkey->inputdev = input;
  100. return 0;
  101. err_free_keymap:
  102. sparse_keymap_free(input);
  103. err_free_dev:
  104. input_free_device(input);
  105. return error;
  106. }
  107. static int acpi_topstar_add(struct acpi_device *device)
  108. {
  109. struct topstar_hkey *tps_hkey;
  110. tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
  111. if (!tps_hkey)
  112. return -ENOMEM;
  113. strcpy(acpi_device_name(device), "Topstar TPSACPI");
  114. strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
  115. if (acpi_topstar_fncx_switch(device, true))
  116. goto add_err;
  117. if (acpi_topstar_init_hkey(tps_hkey))
  118. goto add_err;
  119. device->driver_data = tps_hkey;
  120. return 0;
  121. add_err:
  122. kfree(tps_hkey);
  123. return -ENODEV;
  124. }
  125. static int acpi_topstar_remove(struct acpi_device *device)
  126. {
  127. struct topstar_hkey *tps_hkey = acpi_driver_data(device);
  128. acpi_topstar_fncx_switch(device, false);
  129. sparse_keymap_free(tps_hkey->inputdev);
  130. input_unregister_device(tps_hkey->inputdev);
  131. kfree(tps_hkey);
  132. return 0;
  133. }
  134. static const struct acpi_device_id topstar_device_ids[] = {
  135. { "TPSACPI01", 0 },
  136. { "", 0 },
  137. };
  138. MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
  139. static struct acpi_driver acpi_topstar_driver = {
  140. .name = "Topstar laptop ACPI driver",
  141. .class = ACPI_TOPSTAR_CLASS,
  142. .ids = topstar_device_ids,
  143. .ops = {
  144. .add = acpi_topstar_add,
  145. .remove = acpi_topstar_remove,
  146. .notify = acpi_topstar_notify,
  147. },
  148. };
  149. module_acpi_driver(acpi_topstar_driver);
  150. MODULE_AUTHOR("Herton Ronaldo Krzesinski");
  151. MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
  152. MODULE_LICENSE("GPL");