Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,44 @@ extern "C" {
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);

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

Values passed above this limit are rejected by
hid_set_input_report_buffer_size(). Guards against
memory-exhaustion via unbounded input report queue growth.
*/
#define HID_API_MAX_INPUT_REPORT_BUFFER_SIZE 1024

/** @brief Set the size of the input report buffer/queue.

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 resize the per-device
input report buffer.

Defaults per backend:
- macOS: 30 reports
- Linux libusb: 30 reports
- Windows: 64 reports (via HidD_SetNumInputBuffers)
- Linux hidraw: kernel-managed, no userspace queue
- NetBSD: kernel-managed, no userspace queue

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

@ingroup API
@param dev A device handle returned from hid_open().
@param buffer_size The desired buffer size in reports.
Must be in range [1, HID_API_MAX_INPUT_REPORT_BUFFER_SIZE].

@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_input_report_buffer_size(hid_device *dev, int buffer_size);

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

Feature reports are sent over the Control endpoint as a
Expand Down
21 changes: 20 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 input_report_buffer_size;

/* 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->input_report_buffer_size = 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->input_report_buffer_size) {
return_data(dev, NULL, 0);
}
}
Expand Down Expand Up @@ -1574,6 +1578,21 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev)
}



int HID_API_EXPORT hid_set_input_report_buffer_size(hid_device *dev, int buffer_size)
{
/* Note: libusb backend currently has no error reporting infrastructure
(hid_error returns a fixed string). This function returns -1 on
invalid arguments but cannot provide a descriptive error message
until the backend gains error registration. */
if (buffer_size <= 0 || buffer_size > HID_API_MAX_INPUT_REPORT_BUFFER_SIZE)
return -1;
hidapi_thread_mutex_lock(&dev->thread_state);
dev->input_report_buffer_size = buffer_size;
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
15 changes: 15 additions & 0 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,21 @@ 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_input_report_buffer_size(hid_device *dev, int buffer_size)
{
if (buffer_size <= 0 || buffer_size > HID_API_MAX_INPUT_REPORT_BUFFER_SIZE) {
register_error_str(&dev->last_error_str, "buffer_size out of range");
return -1;
}
/* No-op on Linux hidraw and BSD backends: the kernel manages input
report buffering and there is no userspace queue to resize. The
call is accepted (returns 0) to preserve a consistent cross-platform
API so callers do not need per-backend conditional code. */
(void)buffer_size;
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 input_report_buffer_size;
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->input_report_buffer_size = 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->input_report_buffer_size) {
return_data(dev, NULL, 0);
}
}
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_input_report_buffer_size(hid_device *dev, int buffer_size)
{
if (buffer_size <= 0 || buffer_size > HID_API_MAX_INPUT_REPORT_BUFFER_SIZE) {
register_error_str(&dev->last_error_str, "buffer_size out of range");
return -1;
}
pthread_mutex_lock(&dev->mutex);
dev->input_report_buffer_size = buffer_size;
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
15 changes: 15 additions & 0 deletions netbsd/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,21 @@ 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_input_report_buffer_size(hid_device *dev, int buffer_size)
{
if (buffer_size <= 0 || buffer_size > HID_API_MAX_INPUT_REPORT_BUFFER_SIZE) {
register_error_str(&dev->last_error_str, "buffer_size out of range");
return -1;
}
/* No-op on Linux hidraw and BSD backends: the kernel manages input
report buffering and there is no userspace queue to resize. The
call is accepted (returns 0) to preserve a consistent cross-platform
API so callers do not need per-backend conditional code. */
(void)buffer_size;
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_input_report_buffer_size(hid_device *dev, int buffer_size)
{
if (buffer_size <= 0 || buffer_size > HID_API_MAX_INPUT_REPORT_BUFFER_SIZE) {
register_string_error(dev, L"buffer_size out of range");
return -1;
}
if (!HidD_SetNumInputBuffers(dev->device_handle, (ULONG)buffer_size)) {
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