hid_enabled.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // hid - Gopher Interface Devices (USB HID)
  2. // Copyright (c) 2017 Péter Szilágyi. All rights reserved.
  3. //
  4. // This file is released under the 3-clause BSD license. Note however that Linux
  5. // support depends on libusb, released under LGNU GPL 2.1 or later.
  6. // +build linux,cgo darwin,!ios,cgo windows,cgo
  7. package hid
  8. /*
  9. #cgo CFLAGS: -I./hidapi/hidapi
  10. #cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int
  11. #cgo linux,!android LDFLAGS: -lrt
  12. #cgo darwin CFLAGS: -DOS_DARWIN
  13. #cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit
  14. #cgo windows CFLAGS: -DOS_WINDOWS
  15. #cgo windows LDFLAGS: -lsetupapi
  16. #ifdef OS_LINUX
  17. #include <sys/poll.h>
  18. #include "os/threads_posix.c"
  19. #include "os/poll_posix.c"
  20. #include "os/linux_usbfs.c"
  21. #include "os/linux_netlink.c"
  22. #include "core.c"
  23. #include "descriptor.c"
  24. #include "hotplug.c"
  25. #include "io.c"
  26. #include "strerror.c"
  27. #include "sync.c"
  28. #include "hidapi/libusb/hid.c"
  29. #elif OS_DARWIN
  30. #include "hidapi/mac/hid.c"
  31. #elif OS_WINDOWS
  32. #include "hidapi/windows/hid.c"
  33. #endif
  34. */
  35. import "C"
  36. import (
  37. "errors"
  38. "runtime"
  39. "sync"
  40. "unsafe"
  41. )
  42. // enumerateLock is a mutex serializing access to USB device enumeration needed
  43. // by the macOS USB HID system calls, which require 2 consecutive method calls
  44. // for enumeration, causing crashes if called concurrently.
  45. //
  46. // For more details, see:
  47. // https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching
  48. // > "subsequent calls will cause the hid manager to release previously enumerated devices"
  49. var enumerateLock sync.Mutex
  50. func init() {
  51. // Initialize the HIDAPI library
  52. C.hid_init()
  53. }
  54. // Supported returns whether this platform is supported by the HID library or not.
  55. // The goal of this method is to allow programatically handling platforms that do
  56. // not support USB HID and not having to fall back to build constraints.
  57. func Supported() bool {
  58. return true
  59. }
  60. // Enumerate returns a list of all the HID devices attached to the system which
  61. // match the vendor and product id:
  62. // - If the vendor id is set to 0 then any vendor matches.
  63. // - If the product id is set to 0 then any product matches.
  64. // - If the vendor and product id are both 0, all HID devices are returned.
  65. func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
  66. enumerateLock.Lock()
  67. defer enumerateLock.Unlock()
  68. // Gather all device infos and ensure they are freed before returning
  69. head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID))
  70. if head == nil {
  71. return nil
  72. }
  73. defer C.hid_free_enumeration(head)
  74. // Iterate the list and retrieve the device details
  75. var infos []DeviceInfo
  76. for ; head != nil; head = head.next {
  77. info := DeviceInfo{
  78. Path: C.GoString(head.path),
  79. VendorID: uint16(head.vendor_id),
  80. ProductID: uint16(head.product_id),
  81. Release: uint16(head.release_number),
  82. UsagePage: uint16(head.usage_page),
  83. Usage: uint16(head.usage),
  84. Interface: int(head.interface_number),
  85. }
  86. if head.serial_number != nil {
  87. info.Serial, _ = wcharTToString(head.serial_number)
  88. }
  89. if head.product_string != nil {
  90. info.Product, _ = wcharTToString(head.product_string)
  91. }
  92. if head.manufacturer_string != nil {
  93. info.Manufacturer, _ = wcharTToString(head.manufacturer_string)
  94. }
  95. infos = append(infos, info)
  96. }
  97. return infos
  98. }
  99. // Open connects to an HID device by its path name.
  100. func (info DeviceInfo) Open() (*Device, error) {
  101. path := C.CString(info.Path)
  102. defer C.free(unsafe.Pointer(path))
  103. device := C.hid_open_path(path)
  104. if device == nil {
  105. return nil, errors.New("hidapi: failed to open device")
  106. }
  107. return &Device{
  108. DeviceInfo: info,
  109. device: device,
  110. }, nil
  111. }
  112. // Device is a live HID USB connected device handle.
  113. type Device struct {
  114. DeviceInfo // Embed the infos for easier access
  115. device *C.hid_device // Low level HID device to communicate through
  116. lock sync.Mutex
  117. }
  118. // Close releases the HID USB device handle.
  119. func (dev *Device) Close() {
  120. dev.lock.Lock()
  121. defer dev.lock.Unlock()
  122. if dev.device != nil {
  123. C.hid_close(dev.device)
  124. dev.device = nil
  125. }
  126. }
  127. // Write sends an output report to a HID device.
  128. //
  129. // Write will send the data on the first OUT endpoint, if one exists. If it does
  130. // not, it will send the data through the Control Endpoint (Endpoint 0).
  131. func (dev *Device) Write(b []byte) (int, error) {
  132. // Abort if nothing to write
  133. if len(b) == 0 {
  134. return 0, nil
  135. }
  136. // Abort if device closed in between
  137. dev.lock.Lock()
  138. device := dev.device
  139. dev.lock.Unlock()
  140. if device == nil {
  141. return 0, ErrDeviceClosed
  142. }
  143. // Prepend a HID report ID on Windows, other OSes don't need it
  144. var report []byte
  145. if runtime.GOOS == "windows" {
  146. report = append([]byte{0x00}, b...)
  147. } else {
  148. report = b
  149. }
  150. // Execute the write operation
  151. written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report))))
  152. if written == -1 {
  153. // If the write failed, verify if closed or other error
  154. dev.lock.Lock()
  155. device = dev.device
  156. dev.lock.Unlock()
  157. if device == nil {
  158. return 0, ErrDeviceClosed
  159. }
  160. // Device not closed, some other error occurred
  161. message := C.hid_error(device)
  162. if message == nil {
  163. return 0, errors.New("hidapi: unknown failure")
  164. }
  165. failure, _ := wcharTToString(message)
  166. return 0, errors.New("hidapi: " + failure)
  167. }
  168. return written, nil
  169. }
  170. // Read retrieves an input report from a HID device.
  171. func (dev *Device) Read(b []byte) (int, error) {
  172. // Aborth if nothing to read
  173. if len(b) == 0 {
  174. return 0, nil
  175. }
  176. // Abort if device closed in between
  177. dev.lock.Lock()
  178. device := dev.device
  179. dev.lock.Unlock()
  180. if device == nil {
  181. return 0, ErrDeviceClosed
  182. }
  183. // Execute the read operation
  184. read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b))))
  185. if read == -1 {
  186. // If the read failed, verify if closed or other error
  187. dev.lock.Lock()
  188. device = dev.device
  189. dev.lock.Unlock()
  190. if device == nil {
  191. return 0, ErrDeviceClosed
  192. }
  193. // Device not closed, some other error occurred
  194. message := C.hid_error(device)
  195. if message == nil {
  196. return 0, errors.New("hidapi: unknown failure")
  197. }
  198. failure, _ := wcharTToString(message)
  199. return 0, errors.New("hidapi: " + failure)
  200. }
  201. return read, nil
  202. }