17bdc064bSHans Petter Selasky /*- 2a5b24a2bSHans Petter Selasky * Copyright (c) 2016-2019 Hans Petter Selasky. All rights reserved. 37bdc064bSHans Petter Selasky * 47bdc064bSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 57bdc064bSHans Petter Selasky * modification, are permitted provided that the following conditions 67bdc064bSHans Petter Selasky * are met: 77bdc064bSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 87bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 97bdc064bSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 107bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 117bdc064bSHans Petter Selasky * documentation and/or other materials provided with the distribution. 127bdc064bSHans Petter Selasky * 137bdc064bSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 147bdc064bSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 157bdc064bSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 167bdc064bSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 177bdc064bSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 187bdc064bSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 197bdc064bSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 207bdc064bSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 217bdc064bSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 227bdc064bSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 237bdc064bSHans Petter Selasky * SUCH DAMAGE. 247bdc064bSHans Petter Selasky */ 257bdc064bSHans Petter Selasky 267bdc064bSHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE 277bdc064bSHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE 287bdc064bSHans Petter Selasky #else 297bdc064bSHans Petter Selasky #include <assert.h> 307bdc064bSHans Petter Selasky #include <errno.h> 317bdc064bSHans Petter Selasky #include <poll.h> 327bdc064bSHans Petter Selasky #include <pthread.h> 337bdc064bSHans Petter Selasky #include <stdio.h> 347bdc064bSHans Petter Selasky #include <stdlib.h> 357bdc064bSHans Petter Selasky #include <string.h> 367bdc064bSHans Petter Selasky #include <unistd.h> 377bdc064bSHans Petter Selasky #include <time.h> 387bdc064bSHans Petter Selasky #include <sys/fcntl.h> 397bdc064bSHans Petter Selasky #include <sys/ioctl.h> 407bdc064bSHans Petter Selasky #include <sys/queue.h> 417bdc064bSHans Petter Selasky #include <sys/endian.h> 427bdc064bSHans Petter Selasky #endif 437bdc064bSHans Petter Selasky 447bdc064bSHans Petter Selasky #define libusb_device_handle libusb20_device 457bdc064bSHans Petter Selasky 467bdc064bSHans Petter Selasky #include "libusb20.h" 477bdc064bSHans Petter Selasky #include "libusb20_desc.h" 487bdc064bSHans Petter Selasky #include "libusb20_int.h" 497bdc064bSHans Petter Selasky #include "libusb.h" 507bdc064bSHans Petter Selasky #include "libusb10.h" 517bdc064bSHans Petter Selasky 527bdc064bSHans Petter Selasky static int 537bdc064bSHans Petter Selasky libusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) 547bdc064bSHans Petter Selasky { 557bdc064bSHans Petter Selasky struct libusb20_device *adev = _adev->os_priv; 567bdc064bSHans Petter Selasky struct libusb20_device *bdev = _bdev->os_priv; 577bdc064bSHans Petter Selasky 587bdc064bSHans Petter Selasky if (adev->bus_number != bdev->bus_number) 597bdc064bSHans Petter Selasky return (0); 607bdc064bSHans Petter Selasky if (adev->device_address != bdev->device_address) 617bdc064bSHans Petter Selasky return (0); 627bdc064bSHans Petter Selasky if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) 637bdc064bSHans Petter Selasky return (0); 647bdc064bSHans Petter Selasky if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) 657bdc064bSHans Petter Selasky return (0); 667bdc064bSHans Petter Selasky return (1); 677bdc064bSHans Petter Selasky } 687bdc064bSHans Petter Selasky 697bdc064bSHans Petter Selasky static int 707bdc064bSHans Petter Selasky libusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, 717bdc064bSHans Petter Selasky libusb_device *dev, libusb_hotplug_event event) 727bdc064bSHans Petter Selasky { 737bdc064bSHans Petter Selasky if (!(pcbh->events & event)) 747bdc064bSHans Petter Selasky return (0); 757bdc064bSHans Petter Selasky if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && 767bdc064bSHans Petter Selasky pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) 777bdc064bSHans Petter Selasky return (0); 787bdc064bSHans Petter Selasky if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && 797bdc064bSHans Petter Selasky pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) 807bdc064bSHans Petter Selasky return (0); 817bdc064bSHans Petter Selasky if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && 827bdc064bSHans Petter Selasky pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) 837bdc064bSHans Petter Selasky return (0); 847bdc064bSHans Petter Selasky return (pcbh->fn(ctx, dev, event, pcbh->user_data)); 857bdc064bSHans Petter Selasky } 867bdc064bSHans Petter Selasky 87a41b0ec1SHans Petter Selasky static int 88a41b0ec1SHans Petter Selasky libusb_hotplug_enumerate(libusb_context *ctx, struct libusb_device_head *phead) 89a41b0ec1SHans Petter Selasky { 90a41b0ec1SHans Petter Selasky libusb_device **ppdev; 91a41b0ec1SHans Petter Selasky ssize_t count; 92a41b0ec1SHans Petter Selasky ssize_t x; 93a41b0ec1SHans Petter Selasky 94a41b0ec1SHans Petter Selasky count = libusb_get_device_list(ctx, &ppdev); 95a41b0ec1SHans Petter Selasky if (count < 0) 96a41b0ec1SHans Petter Selasky return (-1); 97a41b0ec1SHans Petter Selasky 98a41b0ec1SHans Petter Selasky for (x = 0; x != count; x++) 99a41b0ec1SHans Petter Selasky TAILQ_INSERT_TAIL(phead, ppdev[x], hotplug_entry); 100a41b0ec1SHans Petter Selasky 101a41b0ec1SHans Petter Selasky libusb_free_device_list(ppdev, 0); 102a41b0ec1SHans Petter Selasky return (0); 103a41b0ec1SHans Petter Selasky } 104a41b0ec1SHans Petter Selasky 1057bdc064bSHans Petter Selasky static void * 1067bdc064bSHans Petter Selasky libusb_hotplug_scan(void *arg) 1077bdc064bSHans Petter Selasky { 108a41b0ec1SHans Petter Selasky struct libusb_device_head hotplug_devs; 1097bdc064bSHans Petter Selasky libusb_hotplug_callback_handle acbh; 1107bdc064bSHans Petter Selasky libusb_hotplug_callback_handle bcbh; 1117bdc064bSHans Petter Selasky libusb_context *ctx = arg; 1127bdc064bSHans Petter Selasky libusb_device *temp; 1137bdc064bSHans Petter Selasky libusb_device *adev; 1147bdc064bSHans Petter Selasky libusb_device *bdev; 1157bdc064bSHans Petter Selasky 116*ba5834b8SBaptiste Daroussin for (;;) { 1177bdc064bSHans Petter Selasky usleep(4000000); 1187bdc064bSHans Petter Selasky 1197bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 120*ba5834b8SBaptiste Daroussin if (ctx->hotplug_handler == NO_THREAD) { 121*ba5834b8SBaptiste Daroussin while ((adev = TAILQ_FIRST(&ctx->hotplug_devs)) != NULL) { 122*ba5834b8SBaptiste Daroussin TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 123*ba5834b8SBaptiste Daroussin libusb_unref_device(adev); 124*ba5834b8SBaptiste Daroussin } 125*ba5834b8SBaptiste Daroussin HOTPLUG_UNLOCK(ctx); 126*ba5834b8SBaptiste Daroussin break; 127*ba5834b8SBaptiste Daroussin } 1287bdc064bSHans Petter Selasky 1297bdc064bSHans Petter Selasky TAILQ_INIT(&hotplug_devs); 1307bdc064bSHans Petter Selasky 131cca46c5eSHans Petter Selasky if (libusb_hotplug_enumerate(ctx, &hotplug_devs) < 0) { 132cca46c5eSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 1337bdc064bSHans Petter Selasky continue; 134cca46c5eSHans Petter Selasky } 1357bdc064bSHans Petter Selasky 1367bdc064bSHans Petter Selasky /* figure out which devices are gone */ 1377bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 1387bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 1397bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 1407bdc064bSHans Petter Selasky break; 1417bdc064bSHans Petter Selasky } 1427bdc064bSHans Petter Selasky if (bdev == NULL) { 1437bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 1447bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 1457bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 1467bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 1477bdc064bSHans Petter Selasky continue; 1487bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 1497bdc064bSHans Petter Selasky free(acbh); 1507bdc064bSHans Petter Selasky } 1517bdc064bSHans Petter Selasky libusb_unref_device(adev); 1527bdc064bSHans Petter Selasky } 1537bdc064bSHans Petter Selasky } 1547bdc064bSHans Petter Selasky 1557bdc064bSHans Petter Selasky /* figure out which devices are new */ 1567bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 1577bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 1587bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 1597bdc064bSHans Petter Selasky break; 1607bdc064bSHans Petter Selasky } 1617bdc064bSHans Petter Selasky if (bdev == NULL) { 1627bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 1637bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 1647bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 1657bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 1667bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 1677bdc064bSHans Petter Selasky continue; 1687bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 1697bdc064bSHans Petter Selasky free(acbh); 1707bdc064bSHans Petter Selasky } 1717bdc064bSHans Petter Selasky } 1727bdc064bSHans Petter Selasky } 1737bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 1747bdc064bSHans Petter Selasky 1757bdc064bSHans Petter Selasky /* unref remaining devices */ 1767bdc064bSHans Petter Selasky while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 1777bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 1787bdc064bSHans Petter Selasky libusb_unref_device(adev); 1797bdc064bSHans Petter Selasky } 1807bdc064bSHans Petter Selasky } 1817bdc064bSHans Petter Selasky return (NULL); 1827bdc064bSHans Petter Selasky } 1837bdc064bSHans Petter Selasky 1847bdc064bSHans Petter Selasky int libusb_hotplug_register_callback(libusb_context *ctx, 1857bdc064bSHans Petter Selasky libusb_hotplug_event events, libusb_hotplug_flag flags, 1867bdc064bSHans Petter Selasky int vendor_id, int product_id, int dev_class, 1877bdc064bSHans Petter Selasky libusb_hotplug_callback_fn cb_fn, void *user_data, 1887bdc064bSHans Petter Selasky libusb_hotplug_callback_handle *phandle) 1897bdc064bSHans Petter Selasky { 1907bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle; 1917bdc064bSHans Petter Selasky struct libusb_device *adev; 1927bdc064bSHans Petter Selasky 1937bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 1947bdc064bSHans Petter Selasky 1957bdc064bSHans Petter Selasky if (ctx == NULL || cb_fn == NULL || events == 0 || 1967bdc064bSHans Petter Selasky vendor_id < -1 || vendor_id > 0xffff || 1977bdc064bSHans Petter Selasky product_id < -1 || product_id > 0xffff || 1987bdc064bSHans Petter Selasky dev_class < -1 || dev_class > 0xff) 1997bdc064bSHans Petter Selasky return (LIBUSB_ERROR_INVALID_PARAM); 2007bdc064bSHans Petter Selasky 2017bdc064bSHans Petter Selasky handle = malloc(sizeof(*handle)); 2027bdc064bSHans Petter Selasky if (handle == NULL) 2037bdc064bSHans Petter Selasky return (LIBUSB_ERROR_NO_MEM); 2047bdc064bSHans Petter Selasky 2057bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 2067bdc064bSHans Petter Selasky if (ctx->hotplug_handler == NO_THREAD) { 207a5b24a2bSHans Petter Selasky libusb_hotplug_enumerate(ctx, &ctx->hotplug_devs); 208a5b24a2bSHans Petter Selasky 2097bdc064bSHans Petter Selasky if (pthread_create(&ctx->hotplug_handler, NULL, 2107bdc064bSHans Petter Selasky &libusb_hotplug_scan, ctx) != 0) 2117bdc064bSHans Petter Selasky ctx->hotplug_handler = NO_THREAD; 2127bdc064bSHans Petter Selasky } 2137bdc064bSHans Petter Selasky handle->events = events; 2147bdc064bSHans Petter Selasky handle->vendor = vendor_id; 2157bdc064bSHans Petter Selasky handle->product = product_id; 2167bdc064bSHans Petter Selasky handle->devclass = dev_class; 2177bdc064bSHans Petter Selasky handle->fn = cb_fn; 2187bdc064bSHans Petter Selasky handle->user_data = user_data; 2197bdc064bSHans Petter Selasky 2207bdc064bSHans Petter Selasky if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 2217bdc064bSHans Petter Selasky TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 2227bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, handle, adev, 2237bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 2247bdc064bSHans Petter Selasky continue; 2257bdc064bSHans Petter Selasky free(handle); 2267bdc064bSHans Petter Selasky handle = NULL; 2277bdc064bSHans Petter Selasky break; 2287bdc064bSHans Petter Selasky } 2297bdc064bSHans Petter Selasky } 2307bdc064bSHans Petter Selasky if (handle != NULL) 2317bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 2327bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 2337bdc064bSHans Petter Selasky 2347bdc064bSHans Petter Selasky if (phandle != NULL) 2357bdc064bSHans Petter Selasky *phandle = handle; 2367bdc064bSHans Petter Selasky return (LIBUSB_SUCCESS); 2377bdc064bSHans Petter Selasky } 2387bdc064bSHans Petter Selasky 2397bdc064bSHans Petter Selasky void libusb_hotplug_deregister_callback(libusb_context *ctx, 2407bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle) 2417bdc064bSHans Petter Selasky { 2427bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 2437bdc064bSHans Petter Selasky 2447bdc064bSHans Petter Selasky if (ctx == NULL || handle == NULL) 2457bdc064bSHans Petter Selasky return; 2467bdc064bSHans Petter Selasky 2477bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 2487bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 2497bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 2507bdc064bSHans Petter Selasky 2517bdc064bSHans Petter Selasky free(handle); 2527bdc064bSHans Petter Selasky } 253