aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/karalabe/hid/hid_enabled.go
blob: 3ceecce0d3e7ffe90d9fed2d632d69c554d9348c (plain) (tree)
1
2
3
4
5
6
7





                                                                                
                                               






                                                                                                          
                                 








































































































































































































                                                                                          
// 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 GNU 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"
)

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 {
    // 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
}