Skip to content
Merged
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
259 changes: 221 additions & 38 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,65 +263,188 @@ extern "C" {

/** @brief Callback handle.

Callbacks handles are generated by hid_hotplug_register_callback()
and can be used to deregister callbacks. Callback handles are unique
and it is safe to call hid_hotplug_deregister_callback() on
an already deregistered callback.
Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

Callback handles are generated by hid_hotplug_register_callback()
and can be used to deregister callbacks. Callback handles are
unique and are not reused while the library remains initialized,
so it is safe to call hid_hotplug_deregister_callback() on an
already deregistered callback: it fails with -1 and never
affects another callback.

A valid callback handle is always a positive value;
0 is never a valid handle and may be used as a sentinel.

@ingroup API
*/
typedef int hid_hotplug_callback_handle;

/**
Hotplug events
/** @brief Hotplug events.

Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

@ingroup API
*/
typedef enum {
/** A device has been plugged in and is ready to use */
/** A device has been plugged in and may be opened.
Opening can still fail, e.g. due to insufficient permissions
or if the device has already disconnected again. */
HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0),

/** A device has left and is no longer available.
It is the user's responsibility to call hid_close with a disconnected device.
If the application holds an open handle to the device, it is
still the application's responsibility to close it with
hid_close().
*/
HID_API_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1)
} hid_hotplug_event;

/**
Hotplug flags
/** @brief Hotplug flags.

Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

@ingroup API
*/
typedef enum {
/** Arm the callback and fire it for all matching currently attached devices. */
/** Arm the callback and fire it for all matching devices that are
already connected at registration time.

hid_hotplug_register_callback() takes a snapshot of the
matching connected devices, and the callback is invoked
asynchronously, on the same internal event context that
delivers live events (see #hid_hotplug_callback_fn), once
for each device in that snapshot. Devices whose arrival is
detected after the snapshot is taken are reported as regular
live events: each device connection is reported to the
callback exactly once - either by the initial pass or as a
live event, never both and never neither. (A device that
disconnects and reconnects is a new connection and is
reported again.)

The initial pass is delivered before any live events for
this callback. In particular, a callback registered with
this flag for both event types never observes a
#HID_API_HOTPLUG_EVENT_DEVICE_LEFT for a matching device
whose arrival it has not been told about first.

These synthetic "arrived" events are delivered only when
#HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED is present in the
requested events mask.

hid_hotplug_register_callback() returning does not imply
the initial pass has been delivered yet: the synthetic
events may fire before or after it returns. */
HID_API_HOTPLUG_ENUMERATE = (1 << 0)
} hid_hotplug_flag;

/** @brief Hotplug callback function type. When requesting hotplug event notifications,
you pass a pointer to a callback function of this type.

This callback may be called by an internal event thread and as such it is
recommended the callback do minimal processing before returning.

hidapi will call this function later, when a matching event had happened on
a matching device.
/** @brief Hotplug callback function type.

Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

Called by HIDAPI when a device matching the registration filter
is connected or disconnected. See #hid_hotplug_register_callback.

@par Execution context

The callback is only ever invoked on HIDAPI's internal event
context, never on an application thread (including the
application's main thread). This includes the synthetic
"arrived" events requested with #HID_API_HOTPLUG_ENUMERATE:
they are delivered asynchronously on that same context and are
never delivered from within the hid_hotplug_register_callback()
call itself. (When a hotplug callback itself registers a new
callback with #HID_API_HOTPLUG_ENUMERATE, the new registration's
synthetic events are delivered later, on this same event
context.)

Every callback invocation holds an internal hotplug mutex for
its duration: any other thread calling
hid_hotplug_register_callback() or
hid_hotplug_deregister_callback() will block until the callback
returns. The mutex is re-entrant: calling those functions from
within the callback itself (i.e. on the event context, which
already holds the mutex) does not block and cannot deadlock -
that is what makes them safe to call from inside the callback
(see below). Keep the callback short.

When multiple callbacks are registered, each event is delivered
to every matching callback sequentially, in the order the
callbacks were registered.

@par What the callback may call

The hotplug API itself is thread-safe (see "Thread safety" under
#hid_hotplug_register_callback) and the following calls are always
safe from within the callback:

- hid_hotplug_register_callback()
- hid_hotplug_deregister_callback() (including on its own handle)
- hid_error(dev) with a non-NULL device handle, provided no
other thread uses that same handle concurrently

Any other HIDAPI function follows HIDAPI's general thread-safety
rule (see the Multi-threading Notes in the project wiki): it is
the application's responsibility to serialize hid_init / hid_exit /
hid_enumerate / hid_open* / hid_close / hid_error(NULL) across all
threads, including the hotplug callback thread (hid_exit()
additionally must never be called from within the callback
itself - see below). If your application
already calls those functions only from one thread, calling them
from the hotplug callback adds a second thread and is therefore
UNSAFE unless the application adds synchronisation itself. The
recommended pattern is to copy the needed fields of @p device out
of the callback and handle open/close on your own thread.

Calling hid_exit() from within the callback has undefined behavior:
hid_exit() joins the hotplug thread, which would be joining itself.

@par The device parameter

The @p device pointer is owned by HIDAPI and is valid only for the
duration of the call. To keep any of its fields (path,
serial_number, manufacturer_string, etc.) beyond the callback,
copy them out; pointer fields must be deep-copied, as all strings
are owned by HIDAPI.

The @p device->next pointer is always NULL. Each callback
invocation describes exactly one device; compound or composite
devices that expose multiple interfaces produce multiple callback
invocations (typically delivered in quick succession).

For #HID_API_HOTPLUG_EVENT_DEVICE_LEFT events @p device points to
a copy captured when the device arrived (or was enumerated): all
fields, including the strings, are valid and describe the device
as it was while connected. A "left" event is delivered for any
matching device that disconnects while the callback is
registered, including devices that were already connected
before the registration (their arrival is reported to this
callback only if #HID_API_HOTPLUG_ENUMERATE was used). When the
callback has observed the device's arrival, the path field
matches the one reported then and may be used to correlate the
two events.

@par Return value

Return 0 to keep the callback registered. Return any non-zero
value to have HIDAPI deregister the callback; no further events
for this handle will be delivered, and the handle is freed as if
hid_hotplug_deregister_callback() had been called. This applies
to both live events and to the synthetic "arrived" events of
#HID_API_HOTPLUG_ENUMERATE: a non-zero return during the
initial pass stops the remainder of that pass and deregisters
the callback.

@ingroup API

@param callback_handle The handle of this callback.
@param device The hid_device_info of the device this event occurred on.
@param event Event that occurred: exactly one value (not a mask)
of \ref hid_hotplug_event.
@param user_data User data provided when this callback was registered
(may be NULL).

Note that when callbacks are called from hid_hotplug_register_callback()
because of the \ref HID_API_HOTPLUG_ENUMERATE flag, the callback return
value is ignored. In other words, you cannot cause a callback to be
deregistered by returning 1 when it is called from hid_hotplug_register_callback().

@ingroup API

@param callback_handle The hid_hotplug_callback_handle callback handle.
@param device The hid_device_info of device this event occurred on event that occurred.
@param event Event that occurred.
@param user_data User data provided when this callback was registered.
(Optionally NULL).

@returns bool
Whether this callback is finished processing events.
Returning non-zero value will cause this callback to be deregistered.
@returns
0 to stay registered; any non-zero value to be deregistered.
*/
typedef int (HID_API_CALL *hid_hotplug_callback_fn)(
hid_hotplug_callback_handle callback_handle,
Expand All @@ -331,10 +454,41 @@ extern "C" {

/** @brief Register a HID hotplug callback function.

Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

If @p vendor_id is set to 0 then any vendor matches.
If @p product_id is set to 0 then any product matches.
If @p vendor_id and @p product_id are both set to 0, then all HID devices will be notified.

If HIDAPI is not initialized yet, this function initializes it
implicitly (as if by hid_init()).

When #HID_API_HOTPLUG_ENUMERATE is set, the synthetic "arrived"
events are delivered asynchronously on HIDAPI's internal event
context (see #hid_hotplug_flag): they may fire before or after
this function returns, but never from within this call itself,
and never on an application thread.
@p callback_handle (when non-NULL) is written before any events
can be delivered, and the callback always receives its own
handle as a parameter, so the callback may deregister itself
even during that initial pass.

@par Thread safety

hid_hotplug_register_callback() and hid_hotplug_deregister_callback()
are thread-safe. They may be called from any thread, including
from within a hotplug callback. This is a deliberate exception
to HIDAPI's general "not thread-safe" rule (see the
Multi-threading Notes in the project wiki).

The first successful call to hid_hotplug_register_callback()
starts HIDAPI's internal hotplug machinery (on most platforms an
internal thread), which runs until either (a) the last callback
is deregistered, or (b) hid_exit() is called. hid_exit()
deregisters any callbacks that are still registered and
invalidates their handles. hid_exit() must not be called from
within a hotplug callback (see #hid_hotplug_callback_fn).

@ingroup API

@param vendor_id The Vendor ID (VID) of the types of device to notify about.
Expand All @@ -347,23 +501,52 @@ extern "C" {
See \ref hid_hotplug_callback_fn.
@param user_data The user data you wanted to provide to your callback function.
@param callback_handle Pointer to store the handle of the allocated callback
(Optionally NULL).
(Optionally NULL). On failure, *callback_handle (when
non-NULL) is set to 0.

@returns
This function returns 0 on success or -1 on error.
Call hid_error(NULL) to get the failure reason.
Registration fails if @p callback is NULL, if @p events
contains no valid #hid_hotplug_event bit, or if @p events
or @p flags contain unknown bits.

@note On backends without hotplug support (e.g. NetBSD)
this function always returns -1.
*/
int HID_API_EXPORT HID_API_CALL hid_hotplug_register_callback(unsigned short vendor_id, unsigned short product_id, int events, int flags, hid_hotplug_callback_fn callback, void *user_data, hid_hotplug_callback_handle *callback_handle);

/** @brief Deregister a callback from a HID hotplug.

This function is safe to call from within a hotplug callback.
Since version 0.16.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 16, 0)

Thread-safe. May be called from any thread, including from within
a hotplug callback (on its own handle or on another callback's
handle). Calling it on a handle that was already deregistered,
or on a handle that was never valid, is safe: it has no effect
and returns -1.

When called from any thread other than HIDAPI's internal event
context, this function does not return until an in-progress
invocation of the callback (if any) has completed; once it
returns, the callback will not be invoked again - including any
undelivered synthetic events of #HID_API_HOTPLUG_ENUMERATE -
and it is safe to release any resources the callback uses
(e.g. whatever @p user_data points to). When called from within
a hotplug callback, the deregistered callback will not be
invoked once the currently executing invocation returns.

See "Thread safety" on #hid_hotplug_register_callback for the
full thread-safety contract of the hotplug API.

@ingroup API

@param callback_handle The handle of the callback to deregister.

@returns
This function returns 0 on success or -1 on error.
This function returns 0 when the callback was found and
deregistered, or -1 on error (including when
@p callback_handle is not a registered handle).
*/
int HID_API_EXPORT HID_API_CALL hid_hotplug_deregister_callback(hid_hotplug_callback_handle callback_handle);

Expand Down
Loading