tc1100-wmi.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * HP Compaq TC1100 Tablet WMI Extras Driver
  4. *
  5. * Copyright (C) 2007 Carlos Corbacho <[email protected]>
  6. * Copyright (C) 2004 Jamey Hicks <[email protected]>
  7. * Copyright (C) 2001, 2002 Andy Grover <[email protected]>
  8. * Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]>
  9. */
  10. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/slab.h>
  14. #include <linux/init.h>
  15. #include <linux/types.h>
  16. #include <linux/acpi.h>
  17. #include <linux/platform_device.h>
  18. #define GUID "C364AC71-36DB-495A-8494-B439D472A505"
  19. #define TC1100_INSTANCE_WIRELESS 1
  20. #define TC1100_INSTANCE_JOGDIAL 2
  21. MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
  22. MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
  23. MODULE_LICENSE("GPL");
  24. MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
  25. static struct platform_device *tc1100_device;
  26. struct tc1100_data {
  27. u32 wireless;
  28. u32 jogdial;
  29. };
  30. #ifdef CONFIG_PM
  31. static struct tc1100_data suspend_data;
  32. #endif
  33. /* --------------------------------------------------------------------------
  34. Device Management
  35. -------------------------------------------------------------------------- */
  36. static int get_state(u32 *out, u8 instance)
  37. {
  38. u32 tmp;
  39. acpi_status status;
  40. struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
  41. union acpi_object *obj;
  42. if (!out)
  43. return -EINVAL;
  44. if (instance > 2)
  45. return -ENODEV;
  46. status = wmi_query_block(GUID, instance, &result);
  47. if (ACPI_FAILURE(status))
  48. return -ENODEV;
  49. obj = (union acpi_object *) result.pointer;
  50. if (obj && obj->type == ACPI_TYPE_INTEGER) {
  51. tmp = obj->integer.value;
  52. } else {
  53. tmp = 0;
  54. }
  55. if (result.length > 0)
  56. kfree(result.pointer);
  57. switch (instance) {
  58. case TC1100_INSTANCE_WIRELESS:
  59. *out = (tmp == 3) ? 1 : 0;
  60. return 0;
  61. case TC1100_INSTANCE_JOGDIAL:
  62. *out = (tmp == 1) ? 0 : 1;
  63. return 0;
  64. default:
  65. return -ENODEV;
  66. }
  67. }
  68. static int set_state(u32 *in, u8 instance)
  69. {
  70. u32 value;
  71. acpi_status status;
  72. struct acpi_buffer input;
  73. if (!in)
  74. return -EINVAL;
  75. if (instance > 2)
  76. return -ENODEV;
  77. switch (instance) {
  78. case TC1100_INSTANCE_WIRELESS:
  79. value = (*in) ? 1 : 2;
  80. break;
  81. case TC1100_INSTANCE_JOGDIAL:
  82. value = (*in) ? 0 : 1;
  83. break;
  84. default:
  85. return -ENODEV;
  86. }
  87. input.length = sizeof(u32);
  88. input.pointer = &value;
  89. status = wmi_set_block(GUID, instance, &input);
  90. if (ACPI_FAILURE(status))
  91. return -ENODEV;
  92. return 0;
  93. }
  94. /* --------------------------------------------------------------------------
  95. FS Interface (/sys)
  96. -------------------------------------------------------------------------- */
  97. /*
  98. * Read/ write bool sysfs macro
  99. */
  100. #define show_set_bool(value, instance) \
  101. static ssize_t \
  102. show_bool_##value(struct device *dev, struct device_attribute *attr, \
  103. char *buf) \
  104. { \
  105. u32 result; \
  106. acpi_status status = get_state(&result, instance); \
  107. if (ACPI_SUCCESS(status)) \
  108. return sprintf(buf, "%d\n", result); \
  109. return sprintf(buf, "Read error\n"); \
  110. } \
  111. \
  112. static ssize_t \
  113. set_bool_##value(struct device *dev, struct device_attribute *attr, \
  114. const char *buf, size_t count) \
  115. { \
  116. u32 tmp = simple_strtoul(buf, NULL, 10); \
  117. acpi_status status = set_state(&tmp, instance); \
  118. if (ACPI_FAILURE(status)) \
  119. return -EINVAL; \
  120. return count; \
  121. } \
  122. static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \
  123. show_bool_##value, set_bool_##value);
  124. show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
  125. show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
  126. static struct attribute *tc1100_attributes[] = {
  127. &dev_attr_wireless.attr,
  128. &dev_attr_jogdial.attr,
  129. NULL
  130. };
  131. static const struct attribute_group tc1100_attribute_group = {
  132. .attrs = tc1100_attributes,
  133. };
  134. /* --------------------------------------------------------------------------
  135. Driver Model
  136. -------------------------------------------------------------------------- */
  137. static int __init tc1100_probe(struct platform_device *device)
  138. {
  139. return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group);
  140. }
  141. static int tc1100_remove(struct platform_device *device)
  142. {
  143. sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group);
  144. return 0;
  145. }
  146. #ifdef CONFIG_PM
  147. static int tc1100_suspend(struct device *dev)
  148. {
  149. int ret;
  150. ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
  151. if (ret)
  152. return ret;
  153. ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
  154. if (ret)
  155. return ret;
  156. return 0;
  157. }
  158. static int tc1100_resume(struct device *dev)
  159. {
  160. int ret;
  161. ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
  162. if (ret)
  163. return ret;
  164. ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
  165. if (ret)
  166. return ret;
  167. return 0;
  168. }
  169. static const struct dev_pm_ops tc1100_pm_ops = {
  170. .suspend = tc1100_suspend,
  171. .resume = tc1100_resume,
  172. .freeze = tc1100_suspend,
  173. .restore = tc1100_resume,
  174. };
  175. #endif
  176. static struct platform_driver tc1100_driver = {
  177. .driver = {
  178. .name = "tc1100-wmi",
  179. #ifdef CONFIG_PM
  180. .pm = &tc1100_pm_ops,
  181. #endif
  182. },
  183. .remove = tc1100_remove,
  184. };
  185. static int __init tc1100_init(void)
  186. {
  187. int error;
  188. if (!wmi_has_guid(GUID))
  189. return -ENODEV;
  190. tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE);
  191. if (!tc1100_device)
  192. return -ENOMEM;
  193. error = platform_device_add(tc1100_device);
  194. if (error)
  195. goto err_device_put;
  196. error = platform_driver_probe(&tc1100_driver, tc1100_probe);
  197. if (error)
  198. goto err_device_del;
  199. pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n");
  200. return 0;
  201. err_device_del:
  202. platform_device_del(tc1100_device);
  203. err_device_put:
  204. platform_device_put(tc1100_device);
  205. return error;
  206. }
  207. static void __exit tc1100_exit(void)
  208. {
  209. platform_device_unregister(tc1100_device);
  210. platform_driver_unregister(&tc1100_driver);
  211. }
  212. module_init(tc1100_init);
  213. module_exit(tc1100_exit);