accounts/usbwallet: support webusb for Trezor wallets
This commit is contained in:
		
							parent
							
								
									7a22da98b9
								
							
						
					
					
						commit
						4799b5abd4
					
				| @ -89,6 +89,12 @@ func NewTrezorHub() (*Hub, error) { | ||||
| 	return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver) | ||||
| } | ||||
| 
 | ||||
| // NewWebUSBTrezorHub creates a new hardware wallet manager for Trezor devices with
 | ||||
| // firmware version > 1.8.0
 | ||||
| func NewWebUSBTrezorHub() (*Hub, error) { | ||||
| 	return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor 1 WebUSB */}, 0, 0, newTrezorDriver) | ||||
| } | ||||
| 
 | ||||
| // newHub creates a new hardware wallet manager for generic USB devices.
 | ||||
| func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) { | ||||
| 	if !hid.Supported() { | ||||
| @ -148,9 +154,19 @@ func (hub *Hub) refreshWallets() { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	for _, info := range hid.Enumerate(hub.vendorID, 0) { | ||||
| 	infos, err := hid.Enumerate(hub.vendorID, 0) | ||||
| 	if err != nil { | ||||
| 		if runtime.GOOS == "linux" { | ||||
| 			// See rationale before the enumeration why this is needed and only on Linux.
 | ||||
| 			hub.commsLock.Unlock() | ||||
| 		} | ||||
| 		log.Error("error enumerating USB enumeration: ", "code", err) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, info := range infos { | ||||
| 		for _, id := range hub.productIDs { | ||||
| 			if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) { | ||||
| 			_, pid, endpoint, _ /* FIXME usageID */ := info.IDs() | ||||
| 			if pid == id && ( /* FIXME usageID == hub.usageID || */ endpoint == hub.endpointID) { | ||||
| 				devices = append(devices, info) | ||||
| 				break | ||||
| 			} | ||||
| @ -169,7 +185,7 @@ func (hub *Hub) refreshWallets() { | ||||
| 	) | ||||
| 
 | ||||
| 	for _, device := range devices { | ||||
| 		url := accounts.URL{Scheme: hub.scheme, Path: device.Path} | ||||
| 		url := accounts.URL{Scheme: hub.scheme, Path: device.GetPath()} | ||||
| 
 | ||||
| 		// Drop wallets in front of the next device or those that failed for some reason
 | ||||
| 		for len(hub.wallets) > 0 { | ||||
|  | ||||
| @ -36,6 +36,8 @@ import ( | ||||
| 	"github.com/golang/protobuf/proto" | ||||
| ) | ||||
| 
 | ||||
| var ErrInvalidDeviceType = errors.New("trezor: invalid device type") | ||||
| 
 | ||||
| // ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
 | ||||
| // this case, the calling application should display a pinpad and send back the
 | ||||
| // encoded passphrase.
 | ||||
|  | ||||
| @ -78,7 +78,7 @@ type wallet struct { | ||||
| 	url    *accounts.URL // Textual URL uniquely identifying this wallet
 | ||||
| 
 | ||||
| 	info   hid.DeviceInfo // Known USB device infos about the wallet
 | ||||
| 	device *hid.Device    // USB device advertising itself as a hardware wallet
 | ||||
| 	device hid.Device     // USB device advertising itself as a hardware wallet
 | ||||
| 
 | ||||
| 	accounts []accounts.Account                         // List of derive accounts pinned on the hardware wallet
 | ||||
| 	paths    map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
 | ||||
|  | ||||
| @ -507,6 +507,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { | ||||
| 			} else { | ||||
| 				backends = append(backends, trezorhub) | ||||
| 			} | ||||
| 			// Start a USB hub for Trezor hardware wallets (WebUSB version)
 | ||||
| 			if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil { | ||||
| 				log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) | ||||
| 			} else { | ||||
| 				backends = append(backends, trezorhub) | ||||
| 			} | ||||
| 		} | ||||
| 		if len(conf.SmartCardDaemonPath) > 0 { | ||||
| 			// Start a smart card hub
 | ||||
|  | ||||
| @ -151,6 +151,13 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts. | ||||
| 			backends = append(backends, trezorhub) | ||||
| 			log.Debug("Trezor support enabled") | ||||
| 		} | ||||
| 		// Start a USB hub for Trezor hardware wallets (WebUSB version)
 | ||||
| 		if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil { | ||||
| 			log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) | ||||
| 		} else { | ||||
| 			backends = append(backends, trezorhub) | ||||
| 			log.Debug("Trezor support enabled") | ||||
| 		} | ||||
| 	} | ||||
| 	// Clef doesn't allow insecure http account unlock.
 | ||||
| 	return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) | ||||
|  | ||||
							
								
								
									
										54
									
								
								vendor/github.com/karalabe/hid/generic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/karalabe/hid/generic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| // hid - Gopher Interface Devices (USB HID)
 | ||||
| // Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved.
 | ||||
| 
 | ||||
| package hid | ||||
| 
 | ||||
| import ( | ||||
| 	"C" | ||||
| ) | ||||
| 
 | ||||
| type GenericEndpointDirection uint8 | ||||
| 
 | ||||
| // List of endpoint direction types
 | ||||
| const ( | ||||
| 	GenericEndpointDirectionOut = 0x00 | ||||
| 	GenericEndpointDirectionIn  = 0x80 | ||||
| ) | ||||
| 
 | ||||
| // List of endpoint attributes
 | ||||
| const ( | ||||
| 	GenericEndpointAttributeInterrupt = 3 | ||||
| ) | ||||
| 
 | ||||
| // GenericEndpoint represents a USB endpoint
 | ||||
| type GenericEndpoint struct { | ||||
| 	Address    uint8 | ||||
| 	Direction  GenericEndpointDirection | ||||
| 	Attributes uint8 | ||||
| } | ||||
| 
 | ||||
| type GenericDeviceInfo struct { | ||||
| 	Path      string // Platform-specific device path
 | ||||
| 	VendorID  uint16 // Device Vendor ID
 | ||||
| 	ProductID uint16 // Device Product ID
 | ||||
| 
 | ||||
| 	device *GenericDevice | ||||
| 
 | ||||
| 	Interface int | ||||
| 
 | ||||
| 	Endpoints []GenericEndpoint | ||||
| } | ||||
| 
 | ||||
| func (gdi *GenericDeviceInfo) Type() DeviceType { | ||||
| 	return DeviceTypeGeneric | ||||
| } | ||||
| 
 | ||||
| // Platform-specific device path
 | ||||
| func (gdi *GenericDeviceInfo) GetPath() string { | ||||
| 	return gdi.Path | ||||
| } | ||||
| 
 | ||||
| // IDs returns the vendor and product IDs for the device
 | ||||
| func (gdi *GenericDeviceInfo) IDs() (uint16, uint16, int, uint16) { | ||||
| 	return gdi.VendorID, gdi.ProductID, gdi.Interface, 0 | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/github.com/karalabe/hid/hid.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/karalabe/hid/hid.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -17,8 +17,8 @@ var ErrDeviceClosed = errors.New("hid: device closed") | ||||
| // operating system is not supported by the library.
 | ||||
| var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") | ||||
| 
 | ||||
| // DeviceInfo is a hidapi info structure.
 | ||||
| type DeviceInfo struct { | ||||
| // HidDeviceInfo is a hidapi info structure.
 | ||||
| type HidDeviceInfo struct { | ||||
| 	Path         string // Platform-specific device path
 | ||||
| 	VendorID     uint16 // Device Vendor ID
 | ||||
| 	ProductID    uint16 // Device Product ID
 | ||||
| @ -35,3 +35,18 @@ type DeviceInfo struct { | ||||
| 	// only if the device contains more than one interface.
 | ||||
| 	Interface int | ||||
| } | ||||
| 
 | ||||
| // GetPath returns the system-dependent path to the device
 | ||||
| func (hdi *HidDeviceInfo) GetPath() string { | ||||
| 	return hdi.Path | ||||
| } | ||||
| 
 | ||||
| // IDs returns the vendor and product id of the device
 | ||||
| func (hdi *HidDeviceInfo) IDs() (uint16, uint16, int, uint16) { | ||||
| 	return hdi.VendorID, hdi.ProductID, hdi.Interface, hdi.UsagePage | ||||
| } | ||||
| 
 | ||||
| // Type returns the type of the device (HID or generic)
 | ||||
| func (hdi *HidDeviceInfo) Type() DeviceType { | ||||
| 	return DeviceTypeHID | ||||
| } | ||||
|  | ||||
							
								
								
									
										41
									
								
								vendor/github.com/karalabe/hid/hid_disabled.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/karalabe/hid/hid_disabled.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -4,7 +4,7 @@ | ||||
| // This file is released under the 3-clause BSD license. Note however that Linux
 | ||||
| // support depends on libusb, released under GNU LGPL 2.1 or later.
 | ||||
| 
 | ||||
| // +build !linux,!darwin,!windows ios !cgo
 | ||||
| // +build !freebsd,!linux,!darwin,!windows ios !cgo
 | ||||
| 
 | ||||
| package hid | ||||
| 
 | ||||
| @ -22,30 +22,55 @@ func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Device is a live HID USB connected device handle. On platforms that this file
 | ||||
| // HidDevice is a live HID USB connected device handle. On platforms that this file
 | ||||
| // implements the type lacks the actual HID device and all methods are noop.
 | ||||
| type Device struct { | ||||
| 	DeviceInfo // Embed the infos for easier access
 | ||||
| type HidDevice struct { | ||||
| 	HidDeviceInfo // Embed the infos for easier access
 | ||||
| } | ||||
| 
 | ||||
| // Open connects to an HID device by its path name. On platforms that this file
 | ||||
| // implements the method just returns an error.
 | ||||
| func (info DeviceInfo) Open() (*Device, error) { | ||||
| func (info HidDeviceInfo) Open() (*Device, error) { | ||||
| 	return nil, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // Close releases the HID USB device handle. On platforms that this file implements
 | ||||
| // the method is just a noop.
 | ||||
| func (dev *Device) Close() error { return nil } | ||||
| func (dev *HidDevice) Close() error { return ErrUnsupportedPlatform } | ||||
| 
 | ||||
| // Write sends an output report to a HID device. On platforms that this file
 | ||||
| // implements the method just returns an error.
 | ||||
| func (dev *Device) Write(b []byte) (int, error) { | ||||
| func (dev *HidDevice) Write(b []byte) (int, error) { | ||||
| 	return 0, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // Read retrieves an input report from a HID device. On platforms that this file
 | ||||
| // implements the method just returns an error.
 | ||||
| func (dev *Device) Read(b []byte) (int, error) { | ||||
| func (dev *HidDevice) Read(b []byte) (int, error) { | ||||
| 	return 0, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // Open tries to open the USB device represented by the current DeviceInfo
 | ||||
| func (gdi *GenericDeviceInfo) Open() (Device, error) { | ||||
| 	return nil, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // GenericDevice represents a generic USB device
 | ||||
| type GenericDevice struct { | ||||
| 	*GenericDeviceInfo // Embed the infos for easier access
 | ||||
| } | ||||
| 
 | ||||
| // Write implements io.ReaderWriter
 | ||||
| func (gd *GenericDevice) Write(b []byte) (int, error) { | ||||
| 	return 0, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // Read implements io.ReaderWriter
 | ||||
| func (gd *GenericDevice) Read(b []byte) (int, error) { | ||||
| 	return 0, ErrUnsupportedPlatform | ||||
| } | ||||
| 
 | ||||
| // Close a previously opened generic USB device
 | ||||
| func (gd *GenericDevice) Close() error { | ||||
| 	return ErrUnsupportedPlatform | ||||
| } | ||||
|  | ||||
							
								
								
									
										263
									
								
								vendor/github.com/karalabe/hid/hid_enabled.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										263
									
								
								vendor/github.com/karalabe/hid/hid_enabled.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -4,7 +4,7 @@ | ||||
| // 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
 | ||||
| // +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
 | ||||
| 
 | ||||
| package hid | ||||
| 
 | ||||
| @ -13,13 +13,15 @@ package hid | ||||
| 
 | ||||
| #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 darwin CFLAGS: -DOS_DARWIN -I./libusb/libusb | ||||
| #cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lusb-1.0.0 | ||||
| #cgo windows CFLAGS: -DOS_WINDOWS | ||||
| #cgo windows LDFLAGS: -lsetupapi | ||||
| #cgo freebsd CFLAGS: -DOS_FREEBSD | ||||
| #cgo freebsd LDFLAGS: -lusb | ||||
| 
 | ||||
| #ifdef OS_LINUX | ||||
| 	#include <sys/poll.h> | ||||
| 	#include <poll.h> | ||||
| 	#include "os/threads_posix.c" | ||||
| 	#include "os/poll_posix.c" | ||||
| 
 | ||||
| @ -35,15 +37,50 @@ package hid | ||||
| 
 | ||||
| 	#include "hidapi/libusb/hid.c" | ||||
| #elif OS_DARWIN | ||||
| 	#include <libusb.h> | ||||
| 	#include "hidapi/mac/hid.c" | ||||
| #elif OS_WINDOWS | ||||
| 	#include "hidapi/windows/hid.c" | ||||
| #elif OS_FREEBSD | ||||
|     #include <stdlib.h> | ||||
| 	#include <libusb.h> | ||||
| 	#include "hidapi/libusb/hid.c" | ||||
| #endif | ||||
| 
 | ||||
| #if defined(OS_LINUX) || defined(OS_WINDOWS) | ||||
| 	void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count) | ||||
| 	{ | ||||
| 		int i; | ||||
| 		struct libusb_device *current = *list; | ||||
| 		for (i=0; i<count; i++) | ||||
| 		{ | ||||
| 			 data[i] = current; | ||||
| 			 current = list_entry(current->list.next, struct libusb_device, list); | ||||
| 		} | ||||
| 	} | ||||
| #elif defined(OS_DARWIN) || defined(OS_FREEBSD) | ||||
| 	void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count) | ||||
| 	{ | ||||
| 		int i; | ||||
| 		// No memcopy because the struct size isn't available for a sizeof()
 | ||||
| 		for (i=0; i<count; i++) | ||||
| 		{ | ||||
| 			data[i] = list[i]; | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| const char *usb_strerror(int err) | ||||
| { | ||||
| 	return libusb_strerror(err); | ||||
| } | ||||
| */ | ||||
| import "C" | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
| @ -65,26 +102,132 @@ func Supported() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // genericEnumerate performs generic USB device enumeration
 | ||||
| func genericEnumerate(vendorID uint16, productID uint16) ([]DeviceInfo, error) { | ||||
| 	var infos []DeviceInfo | ||||
| 	var ctx *C.struct_libusb_context | ||||
| 	errCode := int(C.libusb_init((**C.struct_libusb_context)(&ctx))) | ||||
| 	if errCode < 0 { | ||||
| 		return nil, fmt.Errorf("Error while initializing libusb: %d", errCode) | ||||
| 	} | ||||
| 
 | ||||
| 	var deviceListPtr **C.struct_libusb_device | ||||
| 	count := C.libusb_get_device_list(ctx, (***C.struct_libusb_device)(&deviceListPtr)) | ||||
| 	if count < 0 { | ||||
| 		return nil, fmt.Errorf("Error code listing devices: %d", count) | ||||
| 	} | ||||
| 	defer C.libusb_free_device_list(deviceListPtr, C.int(count)) | ||||
| 
 | ||||
| 	deviceList := make([]*C.struct_libusb_device, count) | ||||
| 	dlhdr := (*reflect.SliceHeader)(unsafe.Pointer(&deviceList)) | ||||
| 	C.copy_device_list_to_slice((**C.struct_libusb_device)(unsafe.Pointer(dlhdr.Data)), deviceListPtr, C.int(count)) | ||||
| 
 | ||||
| 	for devnum, dev := range deviceList { | ||||
| 		var desc C.struct_libusb_device_descriptor | ||||
| 		errCode := int(C.libusb_get_device_descriptor(dev, &desc)) | ||||
| 		if errCode < 0 { | ||||
| 			return nil, fmt.Errorf("Error getting device descriptor for generic device %d: %d", devnum, errCode) | ||||
| 		} | ||||
| 
 | ||||
| 		// Start by checking the vendor id and the product id if necessary
 | ||||
| 		if uint16(desc.idVendor) != vendorID || !(productID == 0 || uint16(desc.idProduct) == productID) { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Skip HID devices, they will be handled later
 | ||||
| 		switch desc.bDeviceClass { | ||||
| 		case 0: | ||||
| 			/* Device class is specified at interface level */ | ||||
| 			for cfgnum := 0; cfgnum < int(desc.bNumConfigurations); cfgnum++ { | ||||
| 				var cfgdesc *C.struct_libusb_config_descriptor | ||||
| 				errCode = int(C.libusb_get_config_descriptor(dev, C.uint8_t(cfgnum), &cfgdesc)) | ||||
| 				if errCode != 0 { | ||||
| 					return nil, fmt.Errorf("Error getting device configuration #%d for generic device %d: %d", cfgnum, devnum, errCode) | ||||
| 				} | ||||
| 
 | ||||
| 				var ifs []C.struct_libusb_interface | ||||
| 				ifshdr := (*reflect.SliceHeader)(unsafe.Pointer(&ifs)) | ||||
| 				ifshdr.Cap = int(cfgdesc.bNumInterfaces) | ||||
| 				ifshdr.Len = int(cfgdesc.bNumInterfaces) | ||||
| 				ifshdr.Data = uintptr(unsafe.Pointer(cfgdesc._interface)) | ||||
| 
 | ||||
| 				for ifnum, ifc := range ifs { | ||||
| 					var ifdescs []C.struct_libusb_interface_descriptor | ||||
| 					ifdshdr := (*reflect.SliceHeader)(unsafe.Pointer(&ifdescs)) | ||||
| 					ifdshdr.Cap = int(ifc.num_altsetting) | ||||
| 					ifdshdr.Len = int(ifc.num_altsetting) | ||||
| 					ifdshdr.Data = uintptr(unsafe.Pointer(ifc.altsetting)) | ||||
| 
 | ||||
| 					for _, alt := range ifdescs { | ||||
| 						if alt.bInterfaceClass != 3 { | ||||
| 							// Device isn't a HID interface, add them to the device list.
 | ||||
| 
 | ||||
| 							var endps []C.struct_libusb_endpoint_descriptor | ||||
| 							endpshdr := (*reflect.SliceHeader)(unsafe.Pointer(&endps)) | ||||
| 							endpshdr.Cap = int(alt.bNumEndpoints) | ||||
| 							endpshdr.Len = int(alt.bNumEndpoints) | ||||
| 							endpshdr.Data = uintptr(unsafe.Pointer(alt.endpoint)) | ||||
| 
 | ||||
| 							endpoints := make([]GenericEndpoint, alt.bNumEndpoints) | ||||
| 
 | ||||
| 							for ne, endpoint := range endps { | ||||
| 								endpoints[ne] = GenericEndpoint{ | ||||
| 									Direction:  GenericEndpointDirection(endpoint.bEndpointAddress) & GenericEndpointDirectionIn, | ||||
| 									Address:    uint8(endpoint.bEndpointAddress), | ||||
| 									Attributes: uint8(endpoint.bmAttributes), | ||||
| 								} | ||||
| 							} | ||||
| 
 | ||||
| 							info := &GenericDeviceInfo{ | ||||
| 								Path:      fmt.Sprintf("%x:%x:%d", vendorID, uint16(desc.idProduct), uint8(C.libusb_get_port_number(dev))), | ||||
| 								VendorID:  uint16(desc.idVendor), | ||||
| 								ProductID: uint16(desc.idProduct), | ||||
| 								device: &GenericDevice{ | ||||
| 									device: dev, | ||||
| 								}, | ||||
| 								Endpoints: endpoints, | ||||
| 								Interface: ifnum, | ||||
| 							} | ||||
| 							info.device.GenericDeviceInfo = info | ||||
| 							infos = append(infos, info) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		case 3: | ||||
| 			// Device class is HID, skip it
 | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return infos, nil | ||||
| } | ||||
| 
 | ||||
| // 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 { | ||||
| func Enumerate(vendorID uint16, productID uint16) ([]DeviceInfo, error) { | ||||
| 	enumerateLock.Lock() | ||||
| 	defer enumerateLock.Unlock() | ||||
| 
 | ||||
| 	infos, err := genericEnumerate(vendorID, productID) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// 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 | ||||
| 		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{ | ||||
| 		info := &HidDeviceInfo{ | ||||
| 			Path:      C.GoString(head.path), | ||||
| 			VendorID:  uint16(head.vendor_id), | ||||
| 			ProductID: uint16(head.product_id), | ||||
| @ -104,11 +247,11 @@ func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { | ||||
| 		} | ||||
| 		infos = append(infos, info) | ||||
| 	} | ||||
| 	return infos | ||||
| 	return infos, nil | ||||
| } | ||||
| 
 | ||||
| // Open connects to an HID device by its path name.
 | ||||
| func (info DeviceInfo) Open() (*Device, error) { | ||||
| func (info *HidDeviceInfo) Open() (Device, error) { | ||||
| 	enumerateLock.Lock() | ||||
| 	defer enumerateLock.Unlock() | ||||
| 
 | ||||
| @ -119,14 +262,14 @@ func (info DeviceInfo) Open() (*Device, error) { | ||||
| 	if device == nil { | ||||
| 		return nil, errors.New("hidapi: failed to open device") | ||||
| 	} | ||||
| 	return &Device{ | ||||
| 	return &HidDevice{ | ||||
| 		DeviceInfo: info, | ||||
| 		device:     device, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Device is a live HID USB connected device handle.
 | ||||
| type Device struct { | ||||
| // 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
 | ||||
| @ -134,7 +277,7 @@ type Device struct { | ||||
| } | ||||
| 
 | ||||
| // Close releases the HID USB device handle.
 | ||||
| func (dev *Device) Close() error { | ||||
| func (dev *HidDevice) Close() error { | ||||
| 	dev.lock.Lock() | ||||
| 	defer dev.lock.Unlock() | ||||
| 
 | ||||
| @ -149,7 +292,7 @@ func (dev *Device) Close() error { | ||||
| //
 | ||||
| // 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) { | ||||
| func (dev *HidDevice) Write(b []byte) (int, error) { | ||||
| 	// Abort if nothing to write
 | ||||
| 	if len(b) == 0 { | ||||
| 		return 0, nil | ||||
| @ -192,7 +335,7 @@ func (dev *Device) Write(b []byte) (int, error) { | ||||
| } | ||||
| 
 | ||||
| // Read retrieves an input report from a HID device.
 | ||||
| func (dev *Device) Read(b []byte) (int, error) { | ||||
| func (dev *HidDevice) Read(b []byte) (int, error) { | ||||
| 	// Aborth if nothing to read
 | ||||
| 	if len(b) == 0 { | ||||
| 		return 0, nil | ||||
| @ -226,3 +369,91 @@ func (dev *Device) Read(b []byte) (int, error) { | ||||
| 	} | ||||
| 	return read, nil | ||||
| } | ||||
| 
 | ||||
| // Type identify the device as a HID device
 | ||||
| func (dev *HidDevice) Type() DeviceType { | ||||
| 	return dev.DeviceInfo.Type() | ||||
| } | ||||
| 
 | ||||
| // Open tries to open the USB device represented by the current DeviceInfo
 | ||||
| func (gdi *GenericDeviceInfo) Open() (Device, error) { | ||||
| 	var handle *C.struct_libusb_device_handle | ||||
| 	errCode := int(C.libusb_open(gdi.device.device, (**C.struct_libusb_device_handle)(&handle))) | ||||
| 	if errCode < 0 { | ||||
| 		return nil, fmt.Errorf("Error opening generic USB device %v, code %d", gdi.device.handle, errCode) | ||||
| 	} | ||||
| 
 | ||||
| 	gdi.device.handle = handle | ||||
| 	// QUESTION: ai-je deja initialie le GDI ?
 | ||||
| 	// 	GenericDeviceInfo: gdi,
 | ||||
| 	// 	handle:            handle,
 | ||||
| 	// }
 | ||||
| 
 | ||||
| 	for _, endpoint := range gdi.Endpoints { | ||||
| 		switch { | ||||
| 		case endpoint.Direction == GenericEndpointDirectionOut && endpoint.Attributes == GenericEndpointAttributeInterrupt: | ||||
| 			gdi.device.WEndpoint = endpoint.Address | ||||
| 		case endpoint.Direction == GenericEndpointDirectionIn && endpoint.Attributes == GenericEndpointAttributeInterrupt: | ||||
| 			gdi.device.REndpoint = endpoint.Address | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if gdi.device.REndpoint == 0 || gdi.device.WEndpoint == 0 { | ||||
| 		return nil, fmt.Errorf("Missing endpoint in device %#x:%#x:%d", gdi.VendorID, gdi.ProductID, gdi.Interface) | ||||
| 	} | ||||
| 
 | ||||
| 	return gdi.device, nil | ||||
| } | ||||
| 
 | ||||
| // GenericDevice represents a generic USB device
 | ||||
| type GenericDevice struct { | ||||
| 	*GenericDeviceInfo // Embed the infos for easier access
 | ||||
| 
 | ||||
| 	REndpoint uint8 | ||||
| 	WEndpoint uint8 | ||||
| 
 | ||||
| 	device *C.struct_libusb_device | ||||
| 	handle *C.struct_libusb_device_handle | ||||
| 	lock   sync.Mutex | ||||
| } | ||||
| 
 | ||||
| // Write implements io.ReaderWriter
 | ||||
| func (gd *GenericDevice) Write(b []byte) (int, error) { | ||||
| 	gd.lock.Lock() | ||||
| 	defer gd.lock.Unlock() | ||||
| 
 | ||||
| 	out, err := interruptTransfer(gd.handle, gd.WEndpoint, b) | ||||
| 	return len(out), err | ||||
| } | ||||
| 
 | ||||
| // Read implements io.ReaderWriter
 | ||||
| func (gd *GenericDevice) Read(b []byte) (int, error) { | ||||
| 	gd.lock.Lock() | ||||
| 	defer gd.lock.Unlock() | ||||
| 
 | ||||
| 	out, err := interruptTransfer(gd.handle, gd.REndpoint, b) | ||||
| 	return len(out), err | ||||
| } | ||||
| 
 | ||||
| // Close a previously opened generic USB device
 | ||||
| func (gd *GenericDevice) Close() error { | ||||
| 	gd.lock.Lock() | ||||
| 	defer gd.lock.Unlock() | ||||
| 
 | ||||
| 	if gd.handle != nil { | ||||
| 		C.libusb_close(gd.handle) | ||||
| 		gd.handle = nil | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // interruptTransfer is a helpler function for libusb's interrupt transfer function
 | ||||
| func interruptTransfer(handle *C.struct_libusb_device_handle, endpoint uint8, data []byte) ([]byte, error) { | ||||
| 	var transferred C.int | ||||
| 	errCode := int(C.libusb_interrupt_transfer(handle, (C.uchar)(endpoint), (*C.uchar)(&data[0]), (C.int)(len(data)), &transferred, (C.uint)(0))) | ||||
| 	if errCode != 0 { | ||||
| 		return nil, fmt.Errorf("Interrupt transfer error: %s", C.GoString(C.usb_strerror(C.int(errCode)))) | ||||
| 	} | ||||
| 	return data[:int(transferred)], nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										53
									
								
								vendor/github.com/karalabe/hid/usb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vendor/github.com/karalabe/hid/usb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| // hid - Gopher Interface Devices (USB HID)
 | ||||
| // Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved.
 | ||||
| //
 | ||||
| // This file is released under the 3-clause BSD license. Note however that Linux
 | ||||
| // support depends on libusb, released under GNU LGPL 2.1 or later.
 | ||||
| 
 | ||||
| // Package usb provide interfaces for generic USB devices.
 | ||||
| package hid | ||||
| 
 | ||||
| // DeviceType represents the type of a USB device (generic or HID)
 | ||||
| type DeviceType int | ||||
| 
 | ||||
| // List of supported device types
 | ||||
| const ( | ||||
| 	DeviceTypeGeneric DeviceType = 0 | ||||
| 	DeviceTypeHID     DeviceType = 1 | ||||
| ) | ||||
| 
 | ||||
| // 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 {
 | ||||
| // }
 | ||||
| 
 | ||||
| // DeviceInfo is a generic libusb info interface
 | ||||
| type DeviceInfo interface { | ||||
| 	// Type returns the type of the device (generic or HID)
 | ||||
| 	Type() DeviceType | ||||
| 
 | ||||
| 	// Platform-specific device path
 | ||||
| 	GetPath() string | ||||
| 
 | ||||
| 	// IDs returns the vendor and product IDs for the device,
 | ||||
| 	// as well as the endpoint id and the usage page.
 | ||||
| 	IDs() (uint16, uint16, int, uint16) | ||||
| 
 | ||||
| 	// Open tries to open the USB device represented by the current DeviceInfo
 | ||||
| 	Open() (Device, error) | ||||
| } | ||||
| 
 | ||||
| // Device is a generic libusb device interface
 | ||||
| type Device interface { | ||||
| 	Close() error | ||||
| 
 | ||||
| 	Write(b []byte) (int, error) | ||||
| 
 | ||||
| 	Read(b []byte) (int, error) | ||||
| 
 | ||||
| 	// Type returns the type of the device (generic or HID)
 | ||||
| 	Type() DeviceType | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/github.com/karalabe/hid/wchar.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/karalabe/hid/wchar.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -7,7 +7,7 @@ | ||||
| // https://github.com/orofarne/gowchar/blob/master/LICENSE
 | ||||
| 
 | ||||
| // +build !ios
 | ||||
| // +build linux darwin windows
 | ||||
| // +build freebsd linux darwin windows
 | ||||
| 
 | ||||
| package hid | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							| @ -267,10 +267,10 @@ | ||||
| 			"revisionTime": "2017-04-30T22:20:11Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "6XsjAARQFvlW6dS15al0ibTFPOQ=", | ||||
| 			"checksumSHA1": "p6UjFsx/1ACWAhsdEOWrXAHptGY=", | ||||
| 			"path": "github.com/karalabe/hid", | ||||
| 			"revision": "d815e0c1a2e2082a287a2806bc90bc8fc7b276a9", | ||||
| 			"revisionTime": "2018-11-28T19:21:57Z", | ||||
| 			"revision": "e40407cce1c217644c09da5415bbfb07d330ea5e", | ||||
| 			"revisionTime": "2019-05-28T15:16:06Z", | ||||
| 			"tree": true | ||||
| 		}, | ||||
| 		{ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user