dell-wmi-descriptor.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Dell WMI descriptor driver
  4. *
  5. * Copyright (C) 2017 Dell Inc. All Rights Reserved.
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/acpi.h>
  9. #include <linux/list.h>
  10. #include <linux/module.h>
  11. #include <linux/wmi.h>
  12. #include "dell-wmi-descriptor.h"
  13. #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
  14. struct descriptor_priv {
  15. struct list_head list;
  16. u32 interface_version;
  17. u32 size;
  18. u32 hotfix;
  19. };
  20. static int descriptor_valid = -EPROBE_DEFER;
  21. static LIST_HEAD(wmi_list);
  22. static DEFINE_MUTEX(list_mutex);
  23. int dell_wmi_get_descriptor_valid(void)
  24. {
  25. if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
  26. return -ENODEV;
  27. return descriptor_valid;
  28. }
  29. EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
  30. bool dell_wmi_get_interface_version(u32 *version)
  31. {
  32. struct descriptor_priv *priv;
  33. bool ret = false;
  34. mutex_lock(&list_mutex);
  35. priv = list_first_entry_or_null(&wmi_list,
  36. struct descriptor_priv,
  37. list);
  38. if (priv) {
  39. *version = priv->interface_version;
  40. ret = true;
  41. }
  42. mutex_unlock(&list_mutex);
  43. return ret;
  44. }
  45. EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
  46. bool dell_wmi_get_size(u32 *size)
  47. {
  48. struct descriptor_priv *priv;
  49. bool ret = false;
  50. mutex_lock(&list_mutex);
  51. priv = list_first_entry_or_null(&wmi_list,
  52. struct descriptor_priv,
  53. list);
  54. if (priv) {
  55. *size = priv->size;
  56. ret = true;
  57. }
  58. mutex_unlock(&list_mutex);
  59. return ret;
  60. }
  61. EXPORT_SYMBOL_GPL(dell_wmi_get_size);
  62. bool dell_wmi_get_hotfix(u32 *hotfix)
  63. {
  64. struct descriptor_priv *priv;
  65. bool ret = false;
  66. mutex_lock(&list_mutex);
  67. priv = list_first_entry_or_null(&wmi_list,
  68. struct descriptor_priv,
  69. list);
  70. if (priv) {
  71. *hotfix = priv->hotfix;
  72. ret = true;
  73. }
  74. mutex_unlock(&list_mutex);
  75. return ret;
  76. }
  77. EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
  78. /*
  79. * Descriptor buffer is 128 byte long and contains:
  80. *
  81. * Name Offset Length Value
  82. * Vendor Signature 0 4 "DELL"
  83. * Object Signature 4 4 " WMI"
  84. * WMI Interface Version 8 4 <version>
  85. * WMI buffer length 12 4 <length>
  86. * WMI hotfix number 16 4 <hotfix>
  87. */
  88. static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
  89. const void *context)
  90. {
  91. union acpi_object *obj = NULL;
  92. struct descriptor_priv *priv;
  93. u32 *buffer;
  94. int ret;
  95. obj = wmidev_block_query(wdev, 0);
  96. if (!obj) {
  97. dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
  98. ret = -EIO;
  99. goto out;
  100. }
  101. if (obj->type != ACPI_TYPE_BUFFER) {
  102. dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
  103. ret = -EINVAL;
  104. descriptor_valid = ret;
  105. goto out;
  106. }
  107. /* Although it's not technically a failure, this would lead to
  108. * unexpected behavior
  109. */
  110. if (obj->buffer.length != 128) {
  111. dev_err(&wdev->dev,
  112. "Dell descriptor buffer has unexpected length (%d)\n",
  113. obj->buffer.length);
  114. ret = -EINVAL;
  115. descriptor_valid = ret;
  116. goto out;
  117. }
  118. buffer = (u32 *)obj->buffer.pointer;
  119. if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
  120. dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
  121. buffer);
  122. ret = -EINVAL;
  123. descriptor_valid = ret;
  124. goto out;
  125. }
  126. descriptor_valid = 0;
  127. if (buffer[2] != 0 && buffer[2] != 1)
  128. dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
  129. (unsigned long) buffer[2]);
  130. priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
  131. GFP_KERNEL);
  132. if (!priv) {
  133. ret = -ENOMEM;
  134. goto out;
  135. }
  136. priv->interface_version = buffer[2];
  137. priv->size = buffer[3];
  138. priv->hotfix = buffer[4];
  139. ret = 0;
  140. dev_set_drvdata(&wdev->dev, priv);
  141. mutex_lock(&list_mutex);
  142. list_add_tail(&priv->list, &wmi_list);
  143. mutex_unlock(&list_mutex);
  144. dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu\n",
  145. (unsigned long) priv->interface_version,
  146. (unsigned long) priv->size,
  147. (unsigned long) priv->hotfix);
  148. out:
  149. kfree(obj);
  150. return ret;
  151. }
  152. static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
  153. {
  154. struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
  155. mutex_lock(&list_mutex);
  156. list_del(&priv->list);
  157. mutex_unlock(&list_mutex);
  158. return 0;
  159. }
  160. static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
  161. { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
  162. { },
  163. };
  164. static struct wmi_driver dell_wmi_descriptor_driver = {
  165. .driver = {
  166. .name = "dell-wmi-descriptor",
  167. },
  168. .probe = dell_wmi_descriptor_probe,
  169. .remove = dell_wmi_descriptor_remove,
  170. .id_table = dell_wmi_descriptor_id_table,
  171. };
  172. module_wmi_driver(dell_wmi_descriptor_driver);
  173. MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
  174. MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
  175. MODULE_DESCRIPTION("Dell WMI descriptor driver");
  176. MODULE_LICENSE("GPL");