// usb - Self contained USB and HID library for Go // Copyright 2017 The library Authors // // This library is free software: you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) any // later version. // // The library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along // with the library. If not, see . // +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo package usb /* #include #include "./hidapi/hidapi/hidapi.h" */ import "C" import ( "errors" "runtime" "sync" "unsafe" ) // enumerateHid 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 enumerateHid(vendorID uint16, productID uint16) ([]DeviceInfo, error) { // 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, 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, nil } // openHid connects to an HID device by its path name. func openHid(info DeviceInfo) (*hidDevice, 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 &hidDevice{ DeviceInfo: info, device: device, }, nil } // hidDevice is a live HID USB connected device handle. type hidDevice 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 *hidDevice) Close() error { dev.lock.Lock() defer dev.lock.Unlock() if dev.device != nil { C.hid_close(dev.device) dev.device = nil } return 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 *hidDevice) 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 *hidDevice) 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 }