diff --git a/hidapi/hidapi.h b/hidapi/hidapi.h index d7e89aabe..4c50d1503 100644 --- a/hidapi/hidapi.h +++ b/hidapi/hidapi.h @@ -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, @@ -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. @@ -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);