topstar-laptop.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. #define ACPI_TOPSTAR_CLASS "topstar"
  21. struct topstar_hkey {
  22. struct input_dev *inputdev;
  23. };
  24. struct tps_key_entry {
  25. u8 code;
  26. u16 keycode;
  27. };
  28. static struct tps_key_entry topstar_keymap[] = {
  29. { 0x80, KEY_BRIGHTNESSUP },
  30. { 0x81, KEY_BRIGHTNESSDOWN },
  31. { 0x83, KEY_VOLUMEUP },
  32. { 0x84, KEY_VOLUMEDOWN },
  33. { 0x85, KEY_MUTE },
  34. { 0x86, KEY_SWITCHVIDEOMODE },
  35. { 0x87, KEY_F13 }, /* touchpad enable/disable key */
  36. { 0x88, KEY_WLAN },
  37. { 0x8a, KEY_WWW },
  38. { 0x8b, KEY_MAIL },
  39. { 0x8c, KEY_MEDIA },
  40. { 0x96, KEY_F14 }, /* G key? */
  41. { }
  42. };
  43. static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
  44. {
  45. struct tps_key_entry *key;
  46. for (key = topstar_keymap; key->code; key++)
  47. if (code == key->code)
  48. return key;
  49. return NULL;
  50. }
  51. static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
  52. {
  53. struct tps_key_entry *key;
  54. for (key = topstar_keymap; key->code; key++)
  55. if (code == key->keycode)
  56. return key;
  57. return NULL;
  58. }
  59. static void acpi_topstar_notify(struct acpi_device *device, u32 event)
  60. {
  61. struct tps_key_entry *key;
  62. static bool dup_evnt[2];
  63. bool *dup;
  64. struct topstar_hkey *hkey = acpi_driver_data(device);
  65. /* 0x83 and 0x84 key events comes duplicated... */
  66. if (event == 0x83 || event == 0x84) {
  67. dup = &dup_evnt[event - 0x83];
  68. if (*dup) {
  69. *dup = false;
  70. return;
  71. }
  72. *dup = true;
  73. }
  74. /*
  75. * 'G key' generate two event codes, convert to only
  76. * one event/key code for now (3G switch?)
  77. */
  78. if (event == 0x97)
  79. event = 0x96;
  80. key = tps_get_key_by_scancode(event);
  81. if (key) {
  82. input_report_key(hkey->inputdev, key->keycode, 1);
  83. input_sync(hkey->inputdev);
  84. input_report_key(hkey->inputdev, key->keycode, 0);
  85. input_sync(hkey->inputdev);
  86. return;
  87. }
  88. /* Known non hotkey events don't handled or that we don't care yet */
  89. if (event == 0x8e || event == 0x8f || event == 0x90)
  90. return;
  91. pr_info("unknown event = 0x%02x\n", event);
  92. }
  93. static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
  94. {
  95. acpi_status status;
  96. union acpi_object fncx_params[1] = {
  97. { .type = ACPI_TYPE_INTEGER }
  98. };
  99. struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] };
  100. fncx_params[0].integer.value = state ? 0x86 : 0x87;
  101. status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL);
  102. if (ACPI_FAILURE(status)) {
  103. pr_err("Unable to switch FNCX notifications\n");
  104. return -ENODEV;
  105. }
  106. return 0;
  107. }
  108. static int topstar_getkeycode(struct input_dev *dev,
  109. unsigned int scancode, unsigned int *keycode)
  110. {
  111. struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
  112. if (!key)
  113. return -EINVAL;
  114. *keycode = key->keycode;
  115. return 0;
  116. }
  117. static int topstar_setkeycode(struct input_dev *dev,
  118. unsigned int scancode, unsigned int keycode)
  119. {
  120. struct tps_key_entry *key;
  121. int old_keycode;
  122. key = tps_get_key_by_scancode(scancode);
  123. if (!key)
  124. return -EINVAL;
  125. old_keycode = key->keycode;
  126. key->keycode = keycode;
  127. set_bit(keycode, dev->keybit);
  128. if (!tps_get_key_by_keycode(old_keycode))
  129. clear_bit(old_keycode, dev->keybit);
  130. return 0;
  131. }
  132. static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
  133. {
  134. struct tps_key_entry *key;
  135. hkey->inputdev = input_allocate_device();
  136. if (!hkey->inputdev) {
  137. pr_err("Unable to allocate input device\n");
  138. return -ENODEV;
  139. }
  140. hkey->inputdev->name = "Topstar Laptop extra buttons";
  141. hkey->inputdev->phys = "topstar/input0";
  142. hkey->inputdev->id.bustype = BUS_HOST;
  143. hkey->inputdev->getkeycode = topstar_getkeycode;
  144. hkey->inputdev->setkeycode = topstar_setkeycode;
  145. for (key = topstar_keymap; key->code; key++) {
  146. set_bit(EV_KEY, hkey->inputdev->evbit);
  147. set_bit(key->keycode, hkey->inputdev->keybit);
  148. }
  149. if (input_register_device(hkey->inputdev)) {
  150. pr_err("Unable to register input device\n");
  151. input_free_device(hkey->inputdev);
  152. return -ENODEV;
  153. }
  154. return 0;
  155. }
  156. static int acpi_topstar_add(struct acpi_device *device)
  157. {
  158. struct topstar_hkey *tps_hkey;
  159. tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
  160. if (!tps_hkey)
  161. return -ENOMEM;
  162. strcpy(acpi_device_name(device), "Topstar TPSACPI");
  163. strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
  164. if (acpi_topstar_fncx_switch(device, true))
  165. goto add_err;
  166. if (acpi_topstar_init_hkey(tps_hkey))
  167. goto add_err;
  168. device->driver_data = tps_hkey;
  169. return 0;
  170. add_err:
  171. kfree(tps_hkey);
  172. return -ENODEV;
  173. }
  174. static int acpi_topstar_remove(struct acpi_device *device, int type)
  175. {
  176. struct topstar_hkey *tps_hkey = acpi_driver_data(device);
  177. acpi_topstar_fncx_switch(device, false);
  178. input_unregister_device(tps_hkey->inputdev);
  179. kfree(tps_hkey);
  180. return 0;
  181. }
  182. static const struct acpi_device_id topstar_device_ids[] = {
  183. { "TPSACPI01", 0 },
  184. { "", 0 },
  185. };
  186. MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
  187. static struct acpi_driver acpi_topstar_driver = {
  188. .name = "Topstar laptop ACPI driver",
  189. .class = ACPI_TOPSTAR_CLASS,
  190. .ids = topstar_device_ids,
  191. .ops = {
  192. .add = acpi_topstar_add,
  193. .remove = acpi_topstar_remove,
  194. .notify = acpi_topstar_notify,
  195. },
  196. };
  197. static int __init topstar_laptop_init(void)
  198. {
  199. int ret;
  200. ret = acpi_bus_register_driver(&acpi_topstar_driver);
  201. if (ret < 0)
  202. return ret;
  203. printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n");
  204. return 0;
  205. }
  206. static void __exit topstar_laptop_exit(void)
  207. {
  208. acpi_bus_unregister_driver(&acpi_topstar_driver);
  209. }
  210. module_init(topstar_laptop_init);
  211. module_exit(topstar_laptop_exit);
  212. MODULE_AUTHOR("Herton Ronaldo Krzesinski");
  213. MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
  214. MODULE_LICENSE("GPL");