aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/karalabe/hid/hid_enabled.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/karalabe/hid/hid_enabled.go')
-rw-r--r--vendor/github.com/karalabe/hid/hid_enabled.go228
1 files changed, 228 insertions, 0 deletions
diff --git a/vendor/github.com/karalabe/hid/hid_enabled.go b/vendor/github.com/karalabe/hid/hid_enabled.go
new file mode 100644
index 0000000..419273b
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/hid_enabled.go
@@ -0,0 +1,228 @@
+// hid - Gopher Interface Devices (USB HID)
+// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
+//
+// This file is released under the 3-clause BSD license. Note however that Linux
+// support depends on libusb, released under LGNU GPL 2.1 or later.
+
+// +build linux,cgo darwin,!ios,cgo windows,cgo
+
+package hid
+
+/*
+#cgo CFLAGS: -I./hidapi/hidapi
+
+#cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int
+#cgo linux,!android LDFLAGS: -lrt
+#cgo darwin CFLAGS: -DOS_DARWIN
+#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit
+#cgo windows CFLAGS: -DOS_WINDOWS
+#cgo windows LDFLAGS: -lsetupapi
+
+#ifdef OS_LINUX
+ #include <sys/poll.h>
+ #include "os/threads_posix.c"
+ #include "os/poll_posix.c"
+
+ #include "os/linux_usbfs.c"
+ #include "os/linux_netlink.c"
+
+ #include "core.c"
+ #include "descriptor.c"
+ #include "hotplug.c"
+ #include "io.c"
+ #include "strerror.c"
+ #include "sync.c"
+
+ #include "hidapi/libusb/hid.c"
+#elif OS_DARWIN
+ #include "hidapi/mac/hid.c"
+#elif OS_WINDOWS
+ #include "hidapi/windows/hid.c"
+#endif
+*/
+import "C"
+import (
+ "errors"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+// enumerateLock is a mutex serializing access to USB device enumeration needed
+// by the macOS USB HID system calls, which require 2 consecutive method calls
+// for enumeration, causing crashes if called concurrently.
+//
+// For more details, see:
+// https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching
+// > "subsequent calls will cause the hid manager to release previously enumerated devices"
+var enumerateLock sync.Mutex
+
+func init() {
+ // Initialize the HIDAPI library
+ C.hid_init()
+}
+
+// Supported returns whether this platform is supported by the HID library or not.
+// The goal of this method is to allow programatically handling platforms that do
+// not support USB HID and not having to fall back to build constraints.
+func Supported() bool {
+ return true
+}
+
+// Enumerate returns a list of all the HID devices attached to the system which
+// match the vendor and product id:
+// - If the vendor id is set to 0 then any vendor matches.
+// - If the product id is set to 0 then any product matches.
+// - If the vendor and product id are both 0, all HID devices are returned.
+func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
+ enumerateLock.Lock()
+ defer enumerateLock.Unlock()
+
+ // Gather all device infos and ensure they are freed before returning
+ head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID))
+ if head == nil {
+ return nil
+ }
+ defer C.hid_free_enumeration(head)
+
+ // Iterate the list and retrieve the device details
+ var infos []DeviceInfo
+ for ; head != nil; head = head.next {
+ info := DeviceInfo{
+ Path: C.GoString(head.path),
+ VendorID: uint16(head.vendor_id),
+ ProductID: uint16(head.product_id),
+ Release: uint16(head.release_number),
+ UsagePage: uint16(head.usage_page),
+ Usage: uint16(head.usage),
+ Interface: int(head.interface_number),
+ }
+ if head.serial_number != nil {
+ info.Serial, _ = wcharTToString(head.serial_number)
+ }
+ if head.product_string != nil {
+ info.Product, _ = wcharTToString(head.product_string)
+ }
+ if head.manufacturer_string != nil {
+ info.Manufacturer, _ = wcharTToString(head.manufacturer_string)
+ }
+ infos = append(infos, info)
+ }
+ return infos
+}
+
+// Open connects to an HID device by its path name.
+func (info DeviceInfo) Open() (*Device, error) {
+ path := C.CString(info.Path)
+ defer C.free(unsafe.Pointer(path))
+
+ device := C.hid_open_path(path)
+ if device == nil {
+ return nil, errors.New("hidapi: failed to open device")
+ }
+ return &Device{
+ DeviceInfo: info,
+ device: device,
+ }, nil
+}
+
+// Device is a live HID USB connected device handle.
+type Device struct {
+ DeviceInfo // Embed the infos for easier access
+
+ device *C.hid_device // Low level HID device to communicate through
+ lock sync.Mutex
+}
+
+// Close releases the HID USB device handle.
+func (dev *Device) Close() {
+ dev.lock.Lock()
+ defer dev.lock.Unlock()
+
+ if dev.device != nil {
+ C.hid_close(dev.device)
+ dev.device = nil
+ }
+}
+
+// Write sends an output report to a HID device.
+//
+// Write will send the data on the first OUT endpoint, if one exists. If it does
+// not, it will send the data through the Control Endpoint (Endpoint 0).
+func (dev *Device) Write(b []byte) (int, error) {
+ // Abort if nothing to write
+ if len(b) == 0 {
+ return 0, nil
+ }
+ // Abort if device closed in between
+ dev.lock.Lock()
+ device := dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Prepend a HID report ID on Windows, other OSes don't need it
+ var report []byte
+ if runtime.GOOS == "windows" {
+ report = append([]byte{0x00}, b...)
+ } else {
+ report = b
+ }
+ // Execute the write operation
+ written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report))))
+ if written == -1 {
+ // If the write failed, verify if closed or other error
+ dev.lock.Lock()
+ device = dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Device not closed, some other error occurred
+ message := C.hid_error(device)
+ if message == nil {
+ return 0, errors.New("hidapi: unknown failure")
+ }
+ failure, _ := wcharTToString(message)
+ return 0, errors.New("hidapi: " + failure)
+ }
+ return written, nil
+}
+
+// Read retrieves an input report from a HID device.
+func (dev *Device) Read(b []byte) (int, error) {
+ // Aborth if nothing to read
+ if len(b) == 0 {
+ return 0, nil
+ }
+ // Abort if device closed in between
+ dev.lock.Lock()
+ device := dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Execute the read operation
+ read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b))))
+ if read == -1 {
+ // If the read failed, verify if closed or other error
+ dev.lock.Lock()
+ device = dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Device not closed, some other error occurred
+ message := C.hid_error(device)
+ if message == nil {
+ return 0, errors.New("hidapi: unknown failure")
+ }
+ failure, _ := wcharTToString(message)
+ return 0, errors.New("hidapi: " + failure)
+ }
+ return read, nil
+}