[4.0] [RFC] api: Add connected/disconnected events handling 72/113172/9
authorPaweł Szewczyk <p.szewczyk@samsung.com>
Mon, 27 Mar 2017 10:03:40 +0000 (12:03 +0200)
committerPaweł Szewczyk <p.szewczyk@samsung.com>
Mon, 10 Apr 2017 06:55:41 +0000 (08:55 +0200)
Change-Id: If8a58e8401f6913956a5a1d88d5719a75aed2d0b
Signed-off-by: Paweł Szewczyk <p.szewczyk@samsung.com>
CMakeLists.txt
doc/usb_host_doc.h
include/usb_host.h
include/usb_host_internal.h
packaging/capi-system-usbhost.spec
src/usb_host.c

index 41d1014a274d50374dc1fd9a97ce9b5cb7c6499d..624262b42da4c4d72e6b359acd80de59ce56ef39 100644 (file)
@@ -27,6 +27,7 @@ IF(BUILD_SHARED_LIBS)
                libusb-1.0
                dlog
                capi-system-info
+               glib-2.0
        )
 ENDIF()
 
index e8e602ffff3482a8f7f01f5508f9188de76d6e91..3dfd641113f1882336d051c7c5c4dbdcd165e5d5 100644 (file)
  *
  * More details on featuring your application can be found from <a href="https://developer.tizen.org/development/tools/native-tools/manifest-text-editor#feature"><b>Feature List</b>.</a>
  *
+ * @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__ */
index f8b761c5a15a35c5d75dab26097ac6585eec2141..669e97841add0d6961c77da343e1de50aeeb7891 100644 (file)
@@ -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
index 69fa7b80a3c5096e1b4794bd09ca62f0dcd60ada..d7d76c1f38d9d1a7395f7ddc4d8f971295132c6a 100644 (file)
 #include "common.h"
 #include "log.h"
 #include <libusb-1.0/libusb.h>
+#include <glib.h>
+#include <pthread.h>
 
 #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;
+};
index 59a9c6a3740a7fcdf99c41b789dface4fb89911d..921fed9158d066c0292eeb89dede033bf1c44019 100644 (file)
@@ -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)
index 764f8f182bbae42f2e9c6b65aa9f0a64294e7900..620664d9fc71300ef6dcd98793393e5120297b89 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <sys/queue.h>
 #include <system_info.h>
+#include <poll.h>
 
 #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;
+}