Skip to content

Commit

Permalink
Import libusb/hidapi 0.14.0 sources
Browse files Browse the repository at this point in the history
  • Loading branch information
sstallion committed Jun 6, 2023
1 parent c844030 commit 4588142
Show file tree
Hide file tree
Showing 15 changed files with 1,780 additions and 127 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added missing WinAPI bindings (see `hid_windows.go` for details)

### Changed

- Imported 0.14.0 sources from [libusb/hidapi](https://github.com/libusb/hidapi).

## [0.13.3] - 2023-02-27

### Added
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ module github.com/sstallion/go-hid

go 1.17

require golang.org/x/sys v0.8.0

require github.com/sstallion/go-tools v1.0.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/sstallion/go-tools v1.0.0 h1:c1KRklANE9QtFnWlZmEOu70DOLyRizzsPKOV60DUCyk=
github.com/sstallion/go-tools v1.0.0/go.mod h1:y3Rklut4T6cPLmNkaU0obckQpnVSSvAZlB2N87qgUtg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
13 changes: 13 additions & 0 deletions hid.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,19 @@ func (d *Device) GetIndexedStr(index int) (string, error) {
return wcstogo(wcs), nil
}

// GetReportDescriptor receives a report descriptor with len(p) bytes from the
// Device. It returns the number of bytes read and an error, if any.
func (d *Device) GetReportDescriptor(p []byte) (int, error) {
data := (*C.uchar)(&p[0])
length := C.size_t(len(p))

res := C.hid_get_report_descriptor(d.handle, data, length)
if res == -1 {
return int(res), wrapErr(d.Error())
}
return int(res), nil
}

// Error returns the last error that occurred on the Device. If no error
// occurred, nil is returned.
func (d *Device) Error() error {
Expand Down
155 changes: 131 additions & 24 deletions hid_darwin.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <IOKit/usb/USBSpec.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach_error.h>
#include <stdbool.h>
#include <wchar.h>
#include <locale.h>
#include <pthread.h>
Expand Down Expand Up @@ -302,7 +303,7 @@ static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
{
CFTypeRef ref;
int32_t value;
int32_t value = 0;

ref = IOHIDDeviceGetProperty(device, key);
if (ref) {
Expand All @@ -314,6 +315,36 @@ static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
return 0;
}

static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t *out_val)
{
bool result = false;
CFTypeRef ref;

ref = IOHIDDeviceGetProperty(device, key);
if (ref) {
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
result = CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, out_val);
}
}
return result;
}

static bool try_get_ioregistry_int_property(io_service_t service, CFStringRef property, int32_t *out_val)
{
bool result = false;
CFTypeRef ref = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, 0);

if (ref) {
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
result = CFNumberGetValue(ref, kCFNumberSInt32Type, out_val);
}

CFRelease(ref);
}

return result;
}

static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
{
return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
Expand Down Expand Up @@ -417,12 +448,12 @@ static int init_hid_manager(void)
return -1;
}

HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
{
return &api_version;
}

HID_API_EXPORT const char* HID_API_CALL hid_version_str()
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
{
return HID_API_VERSION_STR;
}
Expand Down Expand Up @@ -466,6 +497,46 @@ static void process_pending_events(void) {
} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
}

static int read_usb_interface_from_hid_service_parent(io_service_t hid_service)
{
int32_t result = -1;
bool success = false;
io_registry_entry_t current = IO_OBJECT_NULL;
kern_return_t res;
int parent_number = 0;

res = IORegistryEntryGetParentEntry(hid_service, kIOServicePlane, &current);
while (KERN_SUCCESS == res
/* Only search up to 3 parent entries.
* With the default driver - the parent-of-interest supposed to be the first one,
* but lets assume some custom drivers or so, with deeper tree. */
&& parent_number < 3) {
io_registry_entry_t parent = IO_OBJECT_NULL;
int32_t interface_number = -1;
parent_number++;

success = try_get_ioregistry_int_property(current, CFSTR(kUSBInterfaceNumber), &interface_number);
if (success) {
result = interface_number;
break;
}

res = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parent);
if (parent) {
IOObjectRelease(current);
current = parent;
}

}

if (current) {
IOObjectRelease(current);
current = IO_OBJECT_NULL;
}

return result;
}

static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
{
unsigned short dev_vid;
Expand All @@ -475,7 +546,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
CFTypeRef transport_prop;

struct hid_device_info *cur_dev;
io_object_t iokit_dev;
io_service_t hid_service;
kern_return_t res;
uint64_t entry_id = 0;

Expand All @@ -499,9 +570,9 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,

/* Fill in the path (as a unique ID of the service entry) */
cur_dev->path = NULL;
iokit_dev = IOHIDDeviceGetService(dev);
if (iokit_dev != MACH_PORT_NULL) {
res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id);
hid_service = IOHIDDeviceGetService(dev);
if (hid_service != MACH_PORT_NULL) {
res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id);
}
else {
res = KERN_INVALID_ARGUMENT;
Expand All @@ -510,10 +581,11 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
if (res == KERN_SUCCESS) {
/* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */
cur_dev->path = calloc(1, 32);
9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */
const size_t path_len = 32;
cur_dev->path = calloc(1, path_len);
if (cur_dev->path != NULL) {
sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id);
snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id);
}
}

Expand All @@ -539,24 +611,32 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
/* Release Number */
cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));

/* Interface Number */
/* We can only retrieve the interface number for USB HID devices.
* IOKit always seems to return 0 when querying a standard USB device
* for its interface. */
int is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass;
if (is_usb_hid) {
/* Get the interface number */
cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber));
} else {
cur_dev->interface_number = -1;
}
/* Interface Number.
* We can only retrieve the interface number for USB HID devices.
* See below */
cur_dev->interface_number = -1;

/* Bus Type */
transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));

if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
int32_t interface_number = -1;
cur_dev->bus_type = HID_API_BUS_USB;

/* A IOHIDDeviceRef used to have this simple property,
* until macOS 13.3 - we will try to use it. */
if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) {
cur_dev->interface_number = interface_number;
} else {
/* Otherwise fallback to io_service_t property.
* (of one of the parent services). */
cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service);

/* If the above doesn't work -
* no (known) fallback exists at this point. */
}

/* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
} else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
Expand Down Expand Up @@ -876,7 +956,7 @@ static void *read_thread(void *param)
while (!dev->shutdown_thread && !dev->disconnected) {
code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
/* Return if the device has been disconnected */
if (code == kCFRunLoopRunFinished) {
if (code == kCFRunLoopRunFinished || code == kCFRunLoopRunStopped) {
dev->disconnected = 1;
break;
}
Expand Down Expand Up @@ -946,7 +1026,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)

/* Set up the HID Manager if it hasn't been done */
if (hid_init() < 0) {
goto return_error;
return NULL;
}
/* register_global_error: global error is set/reset by hid_init */

Expand Down Expand Up @@ -985,7 +1065,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)

/* Create the Run Loop Mode for this device.
printing the reference seems to work. */
sprintf(str, "HIDAPI_%p", (void*) dev->device_handle);
snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle);
dev->run_loop_mode =
CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);

Expand Down Expand Up @@ -1430,6 +1510,33 @@ int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev)
return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
}

int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey));
if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) {
CFDataRef report_descriptor = (CFDataRef) ref;
const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor);
CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor);
size_t copy_len = (size_t) descriptor_buf_len;

if (descriptor_buf == NULL || descriptor_buf_len < 0) {
register_device_error(dev, "Zero buffer/length");
return -1;
}

if (buf_size < copy_len) {
copy_len = buf_size;
}

memcpy(buf, descriptor_buf, copy_len);
return copy_len;
}
else {
register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property");
return -1;
}
}

HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
if (dev) {
Expand Down
14 changes: 11 additions & 3 deletions hid_libusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ static char *make_path(libusb_device *dev, int config_number, int interface_numb
return strdup(str);
}

HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
{
return &api_version;
}

HID_API_EXPORT const char* HID_API_CALL hid_version_str()
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
{
return HID_API_VERSION_STR;
}
Expand Down Expand Up @@ -835,8 +835,10 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
cur_dev = tmp;
}

if (res >= 0)
if (res >= 0) {
libusb_close(handle);
handle = NULL;
}
}
} /* altsettings */
} /* interfaces */
Expand Down Expand Up @@ -1633,6 +1635,12 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index
}


int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
return hid_get_report_descriptor_libusb(dev->device_handle, dev->interface, dev->report_descriptor_size, buf, buf_size);
}


HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
(void)dev;
Expand Down
Loading

0 comments on commit 4588142

Please sign in to comment.