You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
5.3KB

  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  3. *
  4. * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25. * SUCH DAMAGE.
  26. */
  27. #include <sys/cdefs.h>
  28. __FBSDID("$FreeBSD$");
  29. /*
  30. * Digitizer configuration top-level collection support.
  31. * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-precision-touchpad-required-hid-top-level-collections
  32. */
  33. #include <sys/param.h>
  34. #include <sys/bus.h>
  35. #include <sys/kernel.h>
  36. #include <sys/lock.h>
  37. #include <sys/malloc.h>
  38. #include <sys/module.h>
  39. #include <sys/mutex.h>
  40. #include <sys/sysctl.h>
  41. #include <sys/systm.h>
  42. #include "hid.h"
  43. #include "hidbus.h"
  44. #include "hconf.h"
  45. #define HID_DEBUG_VAR hconf_debug
  46. #include "hid_debug.h"
  47. #ifdef HID_DEBUG
  48. static int hconf_debug = 0;
  49. static SYSCTL_NODE(_hw_hid, OID_AUTO, hconf, CTLFLAG_RW, 0,
  50. "Digitizer configuration top-level collection");
  51. SYSCTL_INT(_hw_hid_hconf, OID_AUTO, debug, CTLFLAG_RWTUN,
  52. &hconf_debug, 1, "Debug level");
  53. #endif
  54. struct hconf_softc {
  55. device_t dev;
  56. uint32_t input_mode;
  57. struct hid_location input_mode_loc;
  58. uint32_t input_mode_rlen;
  59. uint8_t input_mode_rid;
  60. };
  61. static device_probe_t hconf_probe;
  62. static device_attach_t hconf_attach;
  63. static device_detach_t hconf_detach;
  64. static device_resume_t hconf_resume;
  65. static devclass_t hconf_devclass;
  66. static device_method_t hconf_methods[] = {
  67. DEVMETHOD(device_probe, hconf_probe),
  68. DEVMETHOD(device_attach, hconf_attach),
  69. DEVMETHOD(device_detach, hconf_detach),
  70. DEVMETHOD(device_resume, hconf_resume),
  71. DEVMETHOD_END
  72. };
  73. static driver_t hconf_driver = {
  74. .name = "hconf",
  75. .methods = hconf_methods,
  76. .size = sizeof(struct hconf_softc),
  77. };
  78. static const struct hid_device_id hconf_devs[] = {
  79. { HID_TLC(HUP_DIGITIZERS, HUD_CONFIG) },
  80. };
  81. static int
  82. hconf_probe(device_t dev)
  83. {
  84. int error;
  85. error = hidbus_lookup_driver_info(dev, hconf_devs, sizeof(hconf_devs));
  86. if (error != 0)
  87. return (error);
  88. return (BUS_PROBE_DEFAULT);
  89. }
  90. static int
  91. hconf_attach(device_t dev)
  92. {
  93. struct hconf_softc *sc = device_get_softc(dev);
  94. const struct hid_device_info *hw = hid_get_device_info(dev);
  95. uint32_t flags;
  96. void *d_ptr;
  97. uint16_t d_len;
  98. uint8_t tlc_index;
  99. int error;
  100. device_set_desc(dev, hw->name);
  101. error = hid_get_report_descr(dev, &d_ptr, &d_len);
  102. if (error) {
  103. device_printf(dev, "could not retrieve report descriptor from "
  104. "device: %d\n", error);
  105. return (ENXIO);
  106. }
  107. sc->dev = dev;
  108. tlc_index = hidbus_get_index(dev);
  109. /* Parse features for input mode switch */
  110. if (hid_tlc_locate(d_ptr, d_len,
  111. HID_USAGE2(HUP_DIGITIZERS, HUD_INPUT_MODE), hid_feature, tlc_index,
  112. 0, &sc->input_mode_loc, &flags, &sc->input_mode_rid, NULL) &&
  113. (flags & (HIO_VARIABLE | HIO_RELATIVE)) == HIO_VARIABLE)
  114. sc->input_mode_rlen = hid_report_size_1(d_ptr, d_len,
  115. hid_feature, sc->input_mode_rid);
  116. return (0);
  117. }
  118. static int
  119. hconf_detach(device_t dev)
  120. {
  121. return (0);
  122. }
  123. static int
  124. hconf_resume(device_t dev)
  125. {
  126. struct hconf_softc *sc = device_get_softc(dev);
  127. int error;
  128. if (sc->input_mode_rlen > 1) {
  129. error = hconf_set_input_mode(sc, sc->input_mode);
  130. if (error)
  131. DPRINTF("Failed to set input mode: %d\n", error);
  132. }
  133. return (0);
  134. }
  135. int
  136. hconf_set_input_mode(struct hconf_softc *sc, enum hconf_input_mode mode)
  137. {
  138. uint8_t *fbuf;
  139. int error;
  140. if (sc->input_mode_rlen <= 1)
  141. return (ENXIO);
  142. fbuf = malloc(sc->input_mode_rlen, M_TEMP, M_WAITOK | M_ZERO);
  143. /* Input Mode report is not strictly required to be readable */
  144. error = hid_get_report(sc->dev, fbuf, sc->input_mode_rlen, NULL,
  145. HID_FEATURE_REPORT, sc->input_mode_rid);
  146. if (error)
  147. bzero(fbuf + 1, sc->input_mode_rlen - 1);
  148. fbuf[0] = sc->input_mode_rid;
  149. hid_put_data_unsigned(fbuf + 1, sc->input_mode_rlen - 1,
  150. &sc->input_mode_loc, mode);
  151. error = hid_set_report(sc->dev, fbuf, sc->input_mode_rlen,
  152. HID_FEATURE_REPORT, sc->input_mode_rid);
  153. free(fbuf, M_TEMP);
  154. if (error == 0)
  155. sc->input_mode = mode;
  156. return (error);
  157. }
  158. DRIVER_MODULE(hconf, hidbus, hconf_driver, hconf_devclass, NULL, 0);
  159. MODULE_DEPEND(hconf, hidbus, 1, 1, 1);
  160. MODULE_DEPEND(hconf, hid, 1, 1, 1);
  161. MODULE_VERSION(hconf, 1);