Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,50 @@ extern "C" {
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);

/** @brief Upper bound for hid_set_num_input_buffers().

Values passed above this limit are rejected by
hid_set_num_input_buffers(). Guards against
memory-exhaustion via unbounded input report queue growth.
*/
#define HID_API_MAX_NUM_INPUT_BUFFERS 1024
Comment thread
Youw marked this conversation as resolved.

/** @brief Set the number of input report buffers queued per device.

Some HID devices emit input reports in bursts at rates
that exceed the default internal queue capacity, causing
silent report drops on macOS and the libusb Linux backend.
This function allows callers to change how many input
report buffers are retained per device.

Call after hid_open() and before the first hid_read() to
avoid losing reports buffered at open time.

@note Per-backend behavior:
- **macOS (IOKit)** and **Linux libusb**: resizes the
userspace input-report queue. Default: 30 reports.
- **Windows**: forwards to HidD_SetNumInputBuffers(),
which resizes the kernel HID ring buffer. The kernel
accepts values in the range [2, 512]; requests outside
this range return -1. Default: 64 reports.
- **Linux hidraw** and **NetBSD uhid**: the call is
accepted (returns 0) and validated against
HID_API_MAX_NUM_INPUT_BUFFERS, but has no effect (no-op) —
these kernels manage the input report buffer internally
and expose no userspace resize.

@ingroup API
@param dev A device handle returned from hid_open().
@param num_buffers The desired number of input report buffers.
Must be in range [1, HID_API_MAX_NUM_INPUT_BUFFERS].

@returns
0 on success, -1 on failure (invalid parameters or
backend-specific error). Call hid_error(dev) for
details where supported.
*/
int HID_API_EXPORT HID_API_CALL hid_set_num_input_buffers(hid_device *dev, int num_buffers);
Comment thread
Youw marked this conversation as resolved.

/** @brief Send a Feature report to the device.

Feature reports are sent over the Control endpoint as a
Expand Down
10 changes: 10 additions & 0 deletions hidtest/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ int main(int argc, char* argv[])
(void)&hid_get_input_report;
#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
(void)&hid_send_output_report;
#endif
#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)
(void)&hid_set_num_input_buffers;
#endif
(void)&hid_get_feature_report;
(void)&hid_send_feature_report;
Expand Down Expand Up @@ -198,6 +201,13 @@ int main(int argc, char* argv[])
return 1;
}

#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)
res = hid_set_num_input_buffers(handle, 500);
if (res < 0) {
printf("Unable to set input buffers: %ls\n", hid_error(handle));
}
#endif

#if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
hid_winapi_set_write_timeout(handle, 5000);
#endif
Expand Down
17 changes: 16 additions & 1 deletion libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ struct hid_device_ {
/* Whether blocking reads are used */
int blocking; /* boolean */

/* Maximum number of input reports to queue before dropping oldest. */
int num_input_buffers;

/* Read thread objects */
hidapi_thread_state thread_state;
int shutdown_thread;
Expand Down Expand Up @@ -143,6 +146,7 @@ static hid_device *new_hid_device(void)
return NULL;

dev->blocking = 1;
dev->num_input_buffers = 30;

hidapi_thread_state_init(&dev->thread_state);

Expand Down Expand Up @@ -985,7 +989,7 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
/* Pop one off if we've reached 30 in the queue. This
way we don't grow forever if the user never reads
anything from the device. */
if (num_queued > 30) {
if (num_queued > dev->num_input_buffers) {
return_data(dev, NULL, 0);
}
Comment thread
Youw marked this conversation as resolved.
Outdated
}
Expand Down Expand Up @@ -1574,6 +1578,17 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev)
}



int HID_API_EXPORT hid_set_num_input_buffers(hid_device *dev, int num_buffers)
{
if (num_buffers <= 0 || num_buffers > HID_API_MAX_NUM_INPUT_BUFFERS)
return -1;
hidapi_thread_mutex_lock(&dev->thread_state);
dev->num_input_buffers = num_buffers;
hidapi_thread_mutex_unlock(&dev->thread_state);
return 0;
}

int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
{
dev->blocking = !nonblock;
Expand Down
11 changes: 11 additions & 0 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,17 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev)
return dev->last_read_error_str;
}


int HID_API_EXPORT hid_set_num_input_buffers(hid_device *dev, int num_buffers)
{
if (num_buffers <= 0 || num_buffers > HID_API_MAX_NUM_INPUT_BUFFERS) {
register_error_str(&dev->last_error_str, "num_buffers out of range");
Comment thread
Youw marked this conversation as resolved.
Outdated
return -1;
}
(void)num_buffers;
return 0;
}

int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
{
/* Do all non-blocking in userspace using poll(), since it looks
Expand Down
17 changes: 16 additions & 1 deletion mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ struct hid_device_ {
IOOptionBits open_options;
int blocking;
int disconnected;
int num_input_buffers;
CFStringRef run_loop_mode;
CFRunLoopRef run_loop;
CFRunLoopSourceRef source;
Expand Down Expand Up @@ -156,6 +157,7 @@ static hid_device *new_hid_device(void)
dev->open_options = device_open_options;
dev->blocking = 1;
dev->disconnected = 0;
dev->num_input_buffers = 30;
dev->run_loop_mode = NULL;
dev->run_loop = NULL;
dev->source = NULL;
Expand Down Expand Up @@ -908,7 +910,7 @@ static void hid_report_callback(void *context, IOReturn result, void *sender,
/* Pop one off if we've reached 30 in the queue. This
way we don't grow forever if the user never reads
anything from the device. */
if (num_queued > 30) {
if (num_queued > dev->num_input_buffers) {
return_data(dev, NULL, 0);
}
Comment thread
Youw marked this conversation as resolved.
Outdated
}
Expand Down Expand Up @@ -1347,6 +1349,19 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev)
return dev->last_read_error_str;
}


int HID_API_EXPORT hid_set_num_input_buffers(hid_device *dev, int num_buffers)
{
if (num_buffers <= 0 || num_buffers > HID_API_MAX_NUM_INPUT_BUFFERS) {
register_error_str(&dev->last_error_str, "num_buffers out of range");
return -1;
}
pthread_mutex_lock(&dev->mutex);
dev->num_input_buffers = num_buffers;
pthread_mutex_unlock(&dev->mutex);
return 0;
}

int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
{
/* All Nonblocking operation is handled by the library. */
Expand Down
11 changes: 11 additions & 0 deletions netbsd/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,17 @@ HID_API_EXPORT const wchar_t* HID_API_CALL hid_read_error(hid_device *dev)
return dev->last_read_error_str;
}


int HID_API_EXPORT HID_API_CALL hid_set_num_input_buffers(hid_device *dev, int num_buffers)
{
if (num_buffers <= 0 || num_buffers > HID_API_MAX_NUM_INPUT_BUFFERS) {
register_error_str(&dev->last_error_str, "num_buffers out of range");
Comment thread
Youw marked this conversation as resolved.
Outdated
return -1;
}
(void)num_buffers;
return 0;
}

int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
{
dev->blocking = !nonblock;
Expand Down
14 changes: 14 additions & 0 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,20 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev)
return dev->last_read_error_str;
}


int HID_API_EXPORT HID_API_CALL hid_set_num_input_buffers(hid_device *dev, int num_buffers)
{
if (num_buffers <= 0 || num_buffers > HID_API_MAX_NUM_INPUT_BUFFERS) {
register_string_error(dev, L"num_buffers out of range");
return -1;
}
if (!HidD_SetNumInputBuffers(dev->device_handle, (ULONG)num_buffers)) {
register_winapi_error(dev, L"HidD_SetNumInputBuffers");
return -1;
}
return 0;
}

int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
{
dev->blocking = !nonblock;
Expand Down
Loading