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; +}