From: Paweł Szewczyk
Date: Mon, 27 Mar 2017 10:03:40 +0000 (+0200)
Subject: [4.0] [RFC] api: Add connected/disconnected events handling
X-Git-Tag: submit/tizen/20170412.154456^2
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F72%2F113172%2F9;p=platform%2Fcore%2Fapi%2Fusb-host.git
[4.0] [RFC] api: Add connected/disconnected events handling
Change-Id: If8a58e8401f6913956a5a1d88d5719a75aed2d0b
Signed-off-by: Paweł Szewczyk
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 41d1014..624262b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@ IF(BUILD_SHARED_LIBS)
libusb-1.0
dlog
capi-system-info
+ glib-2.0
)
ENDIF()
diff --git a/doc/usb_host_doc.h b/doc/usb_host_doc.h
index e8e602f..3dfd641 100644
--- a/doc/usb_host_doc.h
+++ b/doc/usb_host_doc.h
@@ -226,6 +226,13 @@
*
* More details on featuring your application can be found from Feature List.
*
+ * @defgroup CAPI_USB_HOST_HOTPLUG_MODULE Hotplug event notification
+ * @brief This API is used to register callbacks to be called when devices are connected or disconnected.
+ * @ingroup CAPI_USB_HOST_MODULE
+ * @section CAPI_USB_HOST_HOTPLUG_MODULE_OVERVIEW Overview
+ * Functions described here can be used to register callbacks to be called on events such as
+ * device connection or disconnection.
+ *
*/
#endif /* __TIZEN_USB_HOST_DOC_H__ */
diff --git a/include/usb_host.h b/include/usb_host.h
index f8b761c..669e978 100644
--- a/include/usb_host.h
+++ b/include/usb_host.h
@@ -93,6 +93,17 @@ typedef enum {
USB_HOST_ERROR_UNKNOWN = TIZEN_ERROR_UNKNOWN, /**< Other error */
} usb_host_error_e;
+/**
+ * @ingroup CAPI_USB_HOST_HOTPLUG_MODULE
+ * @brief Enumeration of hotplug event types.
+ * @since_tizen 4.0
+ */
+typedef enum {
+ USB_HOST_HOTPLUG_EVENT_ATTACH, /**< Device was connected */
+ USB_HOST_HOTPLUG_EVENT_DETACH, /**< Device was disconnected */
+ USB_HOST_HOTPLUG_EVENT_ANY, /**< Any event */
+} usb_host_hotplug_event_e;
+
/**
* @ingroup CAPI_USB_HOST_MODULE
* @brief Context handle to USB host.
@@ -150,6 +161,25 @@ typedef struct usb_host_interface_s *usb_host_interface_h;
*/
typedef struct usb_host_endpoint_s *usb_host_endpoint_h;
+/**
+ * @ingroup CAPI_USB_HOST_HOTPLUG_MODULE
+ * @brief USB hotplug callback handle.
+ * @details This handle is used for managing registered hotplug callbacks.
+ * @since_tizen 4.0
+ */
+typedef struct usb_host_hotplug_s *usb_host_hotplug_h;
+
+/**
+ * @ingroup CAPI_USB_HOST_HOTPLUG_MODULE
+ * @brief Device connected/disconnected event handler.
+ * @details The device handle should be unreffed with usb_host_unref_device()
+ * when no longer needed.
+ * @since_tizen 4.0
+ * @param[in] dev Device which was connected/disconnected
+ * @param[in] user_data User data pointer passed on callback registration
+ */
+typedef void (*usb_host_hotplug_cb)(usb_host_device_h dev, void *user_data);
+
/* Library initialization and cleanup */
/**
@@ -967,6 +997,35 @@ int usb_host_control_transfer(usb_host_device_h dev,
int usb_host_transfer(usb_host_endpoint_h ep, unsigned char *data, int length,
int *transferred, unsigned int timeout);
+/**
+ * @ingroup CAPI_USB_HOST_HOTPLUG_MODULE
+ * @brief Registers a callback function to be invoked when a device is connected or disconnected.
+ * @since_tizen 4.0
+ * @param[in] ctx Context handle
+ * @param[in] cb The callback function to be registered
+ * @param[in] event Event that will trigger registered callback
+ * @param[in] user_data The user data to be passed to the callback function
+ * @param[out] handle Handle of the registered callback
+ * @return 0 on success, negative error code on error
+ * @retval #USB_HOST_ERROR_NONE Successful
+ * @retval #USB_HOST_ERROR_NOT_SUPPORTED Not supported
+ * @retval #USB_HOST_ERROR_INVALID_PARAMETER Invalid parameter was passed
+ * @retval #USB_HOST_ERROR_OUT_OF_MEMORY Out of memory
+ */
+int usb_host_set_hotplug_cb(usb_host_context_h ctx, usb_host_hotplug_cb cb, usb_host_hotplug_event_e event, void *user_data, usb_host_hotplug_h *handle);
+
+/**
+ * @ingroup CAPI_USB_HOST_HOTPLUG_MODULE
+ * @brief Unregisters the hotplug callback function.
+ * @since_tizen 4.0
+ * @param[in] handle Handle of the callback to be unregistered
+ * @return 0 on success, negative error code on error
+ * @retval #USB_HOST_ERROR_NONE Successful
+ * @retval #USB_HOST_ERROR_INVALID_PARAMETER Invalid parameter was passed
+ * @retval #USB_HOST_ERROR_NOT_SUPPORTED Not supported
+ */
+int usb_host_unset_hotplug_cb(usb_host_hotplug_h handle);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/usb_host_internal.h b/include/usb_host_internal.h
index 69fa7b8..d7d76c1 100644
--- a/include/usb_host_internal.h
+++ b/include/usb_host_internal.h
@@ -20,11 +20,15 @@
#include "common.h"
#include "log.h"
#include
+#include
+#include
#define MAX_NMB_OF_CONFIGS 255
struct usb_host_context_s {
libusb_context *lusb_ctx;
+ pthread_mutex_t channel_list_lock;
+ GList *gio_channels;
};
struct usb_host_device_s {
@@ -126,3 +130,10 @@ struct usb_host_config_s {
struct usb_host_device_s *dev;
};
+
+struct usb_host_hotplug_s {
+ struct usb_host_context_s *ctx;
+ usb_host_hotplug_cb cb;
+ libusb_hotplug_callback_handle lusb_handle;
+ void *user_data;
+};
diff --git a/packaging/capi-system-usbhost.spec b/packaging/capi-system-usbhost.spec
index 59a9c6a..921fed9 100644
--- a/packaging/capi-system-usbhost.spec
+++ b/packaging/capi-system-usbhost.spec
@@ -10,6 +10,7 @@ Source1001: %{name}.manifest
BuildRequires: pkg-config
BuildRequires: cmake
BuildRequires: libattr-devel
+BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(dlog)
BuildRequires: pkgconfig(capi-system-info)
diff --git a/src/usb_host.c b/src/usb_host.c
index 764f8f1..620664d 100644
--- a/src/usb_host.c
+++ b/src/usb_host.c
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include "usb_host.h"
#include "uref.h"
@@ -366,11 +367,84 @@ out:
//LCOV_EXCL_STOP
}
+static gboolean usb_host_poll_cb(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ usb_host_context_h ctx = data;
+ _I("usb_host_poll_cb");
+
+ if (libusb_handle_events(ctx->lusb_ctx) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static int add_fd_watch(int fd, short events, void *user_data)
+{
+ GIOChannel *ch;
+ usb_host_context_h ctx = user_data;
+ GIOCondition condition = 0;
+ guint fd_handler;
+
+ if (events & POLLIN)
+ condition |= G_IO_IN;
+ if (events & POLLOUT)
+ condition |= G_IO_OUT;
+
+ ch = g_io_channel_unix_new(fd);
+ if (!ch) {
+ _E("Failed to create gio channel"); //LCOV_EXCL_LINE
+ return USB_HOST_ERROR_OUT_OF_MEMORY;
+ }
+
+ fd_handler = g_io_add_watch(ch, condition, usb_host_poll_cb, ctx);
+
+ pthread_mutex_lock(&ctx->channel_list_lock);
+ _I("added fd %d to poll", fd);
+ ctx->gio_channels = g_list_append(ctx->gio_channels, ch);
+ pthread_mutex_unlock(&ctx->channel_list_lock);
+
+ return USB_HOST_ERROR_NONE;
+}
+
+static void fd_added_cb(int fd, short events, void *user_data)
+{
+ add_fd_watch(fd, events, user_data);
+}
+
+static void fd_removed_cb(int fd, void *user_data)
+{
+ GIOChannel *ch;
+ usb_host_context_h ctx = user_data;
+ GList *l, *next;
+ int rfd;
+
+ pthread_mutex_lock(&ctx->channel_list_lock);
+ l = ctx->gio_channels;
+ while (l != NULL) {
+ ch = l->data;
+ next = l->next;
+ rfd = g_io_channel_unix_get_fd(ch);
+ if (fd == rfd) {
+ g_io_channel_unref(ch);
+ ctx->gio_channels = g_list_delete_link(ctx->gio_channels, l);
+ }
+
+ l = next;
+ }
+ pthread_mutex_unlock(&ctx->channel_list_lock);
+
+ _I("removed fd %d", fd);
+}
+
int usb_host_create(usb_host_context_h *ctx)
{
int ret = USB_HOST_ERROR_OUT_OF_MEMORY;
int count = 10;
+ int i;
struct usb_host_context_s *_ctx;
+ const struct libusb_pollfd **pollfds = NULL;
+ GList *l, *next;
+ GIOChannel *ch;
if (!usb_host_feature_enabled())
return USB_HOST_ERROR_NOT_SUPPORTED;
@@ -386,6 +460,8 @@ int usb_host_create(usb_host_context_h *ctx)
goto out; //LCOV_EXCL_LINE System Error
}
+ _ctx->gio_channels = NULL;
+
do {
ret = libusb_init(&(_ctx->lusb_ctx));
count--;
@@ -393,17 +469,48 @@ int usb_host_create(usb_host_context_h *ctx)
if (ret < 0) {
_E("Failed to init libusb (%d)", ret); //LCOV_EXCL_LINE
+ ret = translate_error(ret);
goto free_ctx; //LCOV_EXCL_LINE
}
+ ret = pthread_mutex_init(&_ctx->channel_list_lock, NULL);
+ if (ret != 0) {
+ _E("Failed to init mutex (%d)", ret); //LCOV_EXCL_LINE
+ goto free_ctx;
+ }
+
+ libusb_set_pollfd_notifiers(_ctx->lusb_ctx, fd_added_cb, fd_removed_cb, _ctx);
+ pollfds = libusb_get_pollfds(_ctx->lusb_ctx);
+ if (!pollfds) {
+ _E("Failed to get file descriptors for polling"); //LCOV_EXCL_LINE
+ ret = USB_HOST_ERROR_OUT_OF_MEMORY;
+ goto free_ctx;
+ }
+
+ for (i = 0; pollfds[i]; ++i) {
+ ret = add_fd_watch(pollfds[i]->fd, pollfds[i]->events, _ctx);
+ if (ret < 0)
+ goto free_ctx;
+ }
+
*ctx = _ctx;
+ free(pollfds);
return USB_HOST_ERROR_NONE;
//LCOV_EXCL_START System Error
free_ctx:
+ free(pollfds);
+ l = _ctx->gio_channels;
+ while (l != NULL) {
+ ch = l->data;
+ next = l->next;
+ g_io_channel_unref(ch);
+ l = next;
+ }
+
+ g_list_free(_ctx->gio_channels);
free(_ctx);
- ret = translate_error(ret);
out:
return ret;
//LCOV_EXCL_STOP
@@ -411,6 +518,9 @@ out:
int usb_host_destroy(usb_host_context_h context)
{
+ GIOChannel *ch;
+ GList *l, *next;
+
if (!usb_host_feature_enabled())
return USB_HOST_ERROR_NOT_SUPPORTED;
@@ -419,6 +529,16 @@ int usb_host_destroy(usb_host_context_h context)
return USB_HOST_ERROR_INVALID_PARAMETER;
}
+ l = context->gio_channels;
+ while (l != NULL) {
+ ch = l->data;
+ next = l->next;
+ g_io_channel_unref(ch);
+ l = next;
+ }
+
+ g_list_free(context->gio_channels);
+ pthread_mutex_destroy(&context->channel_list_lock);
libusb_exit(context->lusb_ctx);
free(context);
@@ -1581,3 +1701,87 @@ int usb_host_transfer(usb_host_endpoint_h ep, unsigned char *data, int length,
return USB_HOST_ERROR_NOT_SUPPORTED;
}
}
+
+static int lusb_hotplug_cb(libusb_context *ctx, libusb_device *lusb_device, libusb_hotplug_event event, void *user_data)
+{
+ struct usb_host_hotplug_s *data = user_data;
+ usb_host_device_h dev;
+
+ dev = alloc_device(lusb_device);
+ if (!dev) {
+ _E("Could not allocate device");
+ return 1;
+ }
+
+ data->cb(dev, data->user_data);
+
+ return 0;
+}
+
+int usb_host_set_hotplug_cb(usb_host_context_h ctx, usb_host_hotplug_cb cb, usb_host_hotplug_event_e event, void *user_data, usb_host_hotplug_h *handle)
+{
+ struct usb_host_hotplug_s *data;
+ libusb_hotplug_callback_handle lusb_handle;
+ int ret;
+ libusb_hotplug_event lusb_events = 0;
+
+ if (!usb_host_feature_enabled())
+ return USB_HOST_ERROR_NOT_SUPPORTED;
+
+ if (!cb)
+ return USB_HOST_ERROR_INVALID_PARAMETER;
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ _E("Failed to alloc memory");
+ return USB_HOST_ERROR_OUT_OF_MEMORY;
+ }
+
+ data->ctx = ctx;
+ data->cb = cb;
+ data->user_data = user_data;
+
+ switch (event) {
+ case USB_HOST_HOTPLUG_EVENT_ATTACH:
+ lusb_events = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED;
+ break;
+ case USB_HOST_HOTPLUG_EVENT_DETACH:
+ lusb_events = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
+ break;
+ case USB_HOST_HOTPLUG_EVENT_ANY:
+ lusb_events = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
+ break;
+ }
+
+ ret = libusb_hotplug_register_callback(ctx->lusb_ctx, lusb_events,
+ 0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY, lusb_hotplug_cb, data, &lusb_handle);
+
+ if (ret != LIBUSB_SUCCESS) {
+ ret = translate_error(ret);
+ goto out;
+ }
+
+ data->lusb_handle = lusb_handle;
+ *handle = data;
+
+ return USB_HOST_ERROR_NONE;
+
+out:
+ free(data);
+ return ret;
+}
+
+int usb_host_unset_hotplug_cb(usb_host_hotplug_h handle)
+{
+ if (!usb_host_feature_enabled())
+ return USB_HOST_ERROR_NOT_SUPPORTED;
+
+ if (!handle)
+ return USB_HOST_ERROR_INVALID_PARAMETER;
+
+ libusb_hotplug_deregister_callback(handle->ctx->lusb_ctx, handle->lusb_handle);
+ free(handle);
+
+ return USB_HOST_ERROR_NONE;
+}