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