topstar-laptop.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. union acpi_object fncx_params[1] = {
  72. { .type = ACPI_TYPE_INTEGER }
  73. };
  74. struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] };
  75. fncx_params[0].integer.value = state ? 0x86 : 0x87;
  76. status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL);
  77. if (ACPI_FAILURE(status)) {
  78. pr_err("Unable to switch FNCX notifications\n");
  79. return -ENODEV;
  80. }
  81. return 0;
  82. }
  83. static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
  84. {
  85. struct input_dev *input;
  86. int error;
  87. input = input_allocate_device();
  88. if (!input) {
  89. pr_err("Unable to allocate input device\n");
  90. return -ENOMEM;
  91. }
  92. input->name = "Topstar Laptop extra buttons";
  93. input->phys = "topstar/input0";
  94. input->id.bustype = BUS_HOST;
  95. error = sparse_keymap_setup(input, topstar_keymap, NULL);
  96. if (error) {
  97. pr_err("Unable to setup input device keymap\n");
  98. goto err_free_dev;
  99. }
  100. error = input_register_device(input);
  101. if (error) {
  102. pr_err("Unable to register input device\n");
  103. goto err_free_keymap;
  104. }
  105. hkey->inputdev = input;
  106. return 0;
  107. err_free_keymap:
  108. sparse_keymap_free(input);
  109. err_free_dev:
  110. input_free_device(input);
  111. return error;
  112. }
  113. static int acpi_topstar_add(struct acpi_device *device)
  114. {
  115. struct topstar_hkey *tps_hkey;
  116. tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
  117. if (!tps_hkey)
  118. return -ENOMEM;
  119. strcpy(acpi_device_name(device), "Topstar TPSACPI");
  120. strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
  121. if (acpi_topstar_fncx_switch(device, true))
  122. goto add_err;
  123. if (acpi_topstar_init_hkey(tps_hkey))
  124. goto add_err;
  125. device->driver_data = tps_hkey;
  126. return 0;
  127. add_err:
  128. kfree(tps_hkey);
  129. return -ENODEV;
  130. }
  131. static int acpi_topstar_remove(struct acpi_device *device, int type)
  132. {
  133. struct topstar_hkey *tps_hkey = acpi_driver_data(device);
  134. acpi_topstar_fncx_switch(device, false);
  135. sparse_keymap_free(tps_hkey->inputdev);
  136. input_unregister_device(tps_hkey->inputdev);
  137. kfree(tps_hkey);
  138. return 0;
  139. }
  140. static const struct acpi_device_id topstar_device_ids[] = {
  141. { "TPSACPI01", 0 },
  142. { "", 0 },
  143. };
  144. MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
  145. static struct acpi_driver acpi_topstar_driver = {
  146. .name = "Topstar laptop ACPI driver",
  147. .class = ACPI_TOPSTAR_CLASS,
  148. .ids = topstar_device_ids,
  149. .ops = {
  150. .add = acpi_topstar_add,
  151. .remove = acpi_topstar_remove,
  152. .notify = acpi_topstar_notify,
  153. },
  154. };
  155. static int __init topstar_laptop_init(void)
  156. {
  157. int ret;
  158. ret = acpi_bus_register_driver(&acpi_topstar_driver);
  159. if (ret < 0)
  160. return ret;
  161. pr_info("ACPI extras driver loaded\n");
  162. return 0;
  163. }
  164. static void __exit topstar_laptop_exit(void)
  165. {
  166. acpi_bus_unregister_driver(&acpi_topstar_driver);
  167. }
  168. module_init(topstar_laptop_init);
  169. module_exit(topstar_laptop_exit);
  170. MODULE_AUTHOR("Herton Ronaldo Krzesinski");
  171. MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
  172. MODULE_LICENSE("GPL");