1302080Shselasky/* $FreeBSD: stable/10/lib/libusb/libusb10_hotplug.c 357439 2020-02-03 11:05:59Z hselasky $ */ 2302080Shselasky/*- 3349669Shselasky * Copyright (c) 2016-2019 Hans Petter Selasky. All rights reserved. 4302080Shselasky * 5302080Shselasky * Redistribution and use in source and binary forms, with or without 6302080Shselasky * modification, are permitted provided that the following conditions 7302080Shselasky * are met: 8302080Shselasky * 1. Redistributions of source code must retain the above copyright 9302080Shselasky * notice, this list of conditions and the following disclaimer. 10302080Shselasky * 2. Redistributions in binary form must reproduce the above copyright 11302080Shselasky * notice, this list of conditions and the following disclaimer in the 12302080Shselasky * documentation and/or other materials provided with the distribution. 13302080Shselasky * 14302080Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15302080Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16302080Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17302080Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18302080Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19302080Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20302080Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21302080Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22302080Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23302080Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24302080Shselasky * SUCH DAMAGE. 25302080Shselasky */ 26302080Shselasky 27302080Shselasky#ifdef LIBUSB_GLOBAL_INCLUDE_FILE 28302080Shselasky#include LIBUSB_GLOBAL_INCLUDE_FILE 29302080Shselasky#else 30302080Shselasky#include <assert.h> 31302080Shselasky#include <errno.h> 32302080Shselasky#include <poll.h> 33302080Shselasky#include <pthread.h> 34302080Shselasky#include <stdio.h> 35302080Shselasky#include <stdlib.h> 36302080Shselasky#include <string.h> 37302080Shselasky#include <unistd.h> 38302080Shselasky#include <time.h> 39302080Shselasky#include <sys/fcntl.h> 40302080Shselasky#include <sys/ioctl.h> 41302080Shselasky#include <sys/queue.h> 42302080Shselasky#include <sys/endian.h> 43302080Shselasky#endif 44302080Shselasky 45302080Shselasky#define libusb_device_handle libusb20_device 46302080Shselasky 47302080Shselasky#include "libusb20.h" 48302080Shselasky#include "libusb20_desc.h" 49302080Shselasky#include "libusb20_int.h" 50302080Shselasky#include "libusb.h" 51302080Shselasky#include "libusb10.h" 52302080Shselasky 53302080Shselaskystatic int 54302080Shselaskylibusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) 55302080Shselasky{ 56302080Shselasky struct libusb20_device *adev = _adev->os_priv; 57302080Shselasky struct libusb20_device *bdev = _bdev->os_priv; 58302080Shselasky 59302080Shselasky if (adev->bus_number != bdev->bus_number) 60302080Shselasky return (0); 61302080Shselasky if (adev->device_address != bdev->device_address) 62302080Shselasky return (0); 63302080Shselasky if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) 64302080Shselasky return (0); 65302080Shselasky if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) 66302080Shselasky return (0); 67302080Shselasky return (1); 68302080Shselasky} 69302080Shselasky 70302080Shselaskystatic int 71302080Shselaskylibusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, 72302080Shselasky libusb_device *dev, libusb_hotplug_event event) 73302080Shselasky{ 74302080Shselasky if (!(pcbh->events & event)) 75302080Shselasky return (0); 76302080Shselasky if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && 77302080Shselasky pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) 78302080Shselasky return (0); 79302080Shselasky if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && 80302080Shselasky pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) 81302080Shselasky return (0); 82302080Shselasky if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && 83302080Shselasky pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) 84302080Shselasky return (0); 85302080Shselasky return (pcbh->fn(ctx, dev, event, pcbh->user_data)); 86302080Shselasky} 87302080Shselasky 88349669Shselaskystatic int 89349669Shselaskylibusb_hotplug_enumerate(libusb_context *ctx, struct libusb_device_head *phead) 90349669Shselasky{ 91349669Shselasky libusb_device **ppdev; 92349669Shselasky ssize_t count; 93349669Shselasky ssize_t x; 94349669Shselasky 95349669Shselasky count = libusb_get_device_list(ctx, &ppdev); 96349669Shselasky if (count < 0) 97349669Shselasky return (-1); 98349669Shselasky 99349669Shselasky for (x = 0; x != count; x++) 100349669Shselasky TAILQ_INSERT_TAIL(phead, ppdev[x], hotplug_entry); 101349669Shselasky 102349669Shselasky libusb_free_device_list(ppdev, 0); 103349669Shselasky return (0); 104349669Shselasky} 105349669Shselasky 106302080Shselaskystatic void * 107302080Shselaskylibusb_hotplug_scan(void *arg) 108302080Shselasky{ 109349669Shselasky struct libusb_device_head hotplug_devs; 110302080Shselasky libusb_hotplug_callback_handle acbh; 111302080Shselasky libusb_hotplug_callback_handle bcbh; 112302080Shselasky libusb_context *ctx = arg; 113302080Shselasky libusb_device *temp; 114302080Shselasky libusb_device *adev; 115302080Shselasky libusb_device *bdev; 116302080Shselasky unsigned do_loop = 1; 117302080Shselasky 118302080Shselasky while (do_loop) { 119302080Shselasky usleep(4000000); 120302080Shselasky 121302080Shselasky HOTPLUG_LOCK(ctx); 122302080Shselasky 123302080Shselasky TAILQ_INIT(&hotplug_devs); 124302080Shselasky 125302080Shselasky if (ctx->hotplug_handler != NO_THREAD) { 126357439Shselasky if (libusb_hotplug_enumerate(ctx, &hotplug_devs) < 0) { 127357439Shselasky HOTPLUG_UNLOCK(ctx); 128302080Shselasky continue; 129357439Shselasky } 130302080Shselasky } else { 131302080Shselasky do_loop = 0; 132302080Shselasky } 133302080Shselasky 134302080Shselasky /* figure out which devices are gone */ 135302080Shselasky TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 136302080Shselasky TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 137302080Shselasky if (libusb_hotplug_equal(adev, bdev)) 138302080Shselasky break; 139302080Shselasky } 140302080Shselasky if (bdev == NULL) { 141302080Shselasky TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 142302080Shselasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 143302080Shselasky if (libusb_hotplug_filter(ctx, acbh, adev, 144302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 145302080Shselasky continue; 146302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 147302080Shselasky free(acbh); 148302080Shselasky } 149302080Shselasky libusb_unref_device(adev); 150302080Shselasky } 151302080Shselasky } 152302080Shselasky 153302080Shselasky /* figure out which devices are new */ 154302080Shselasky TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 155302080Shselasky TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 156302080Shselasky if (libusb_hotplug_equal(adev, bdev)) 157302080Shselasky break; 158302080Shselasky } 159302080Shselasky if (bdev == NULL) { 160302080Shselasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 161302080Shselasky TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 162302080Shselasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 163302080Shselasky if (libusb_hotplug_filter(ctx, acbh, adev, 164302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 165302080Shselasky continue; 166302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 167302080Shselasky free(acbh); 168302080Shselasky } 169302080Shselasky } 170302080Shselasky } 171302080Shselasky HOTPLUG_UNLOCK(ctx); 172302080Shselasky 173302080Shselasky /* unref remaining devices */ 174302080Shselasky while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 175302080Shselasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 176302080Shselasky libusb_unref_device(adev); 177302080Shselasky } 178302080Shselasky } 179302080Shselasky return (NULL); 180302080Shselasky} 181302080Shselasky 182302080Shselaskyint libusb_hotplug_register_callback(libusb_context *ctx, 183302080Shselasky libusb_hotplug_event events, libusb_hotplug_flag flags, 184302080Shselasky int vendor_id, int product_id, int dev_class, 185302080Shselasky libusb_hotplug_callback_fn cb_fn, void *user_data, 186302080Shselasky libusb_hotplug_callback_handle *phandle) 187302080Shselasky{ 188302080Shselasky libusb_hotplug_callback_handle handle; 189302080Shselasky struct libusb_device *adev; 190302080Shselasky 191302080Shselasky ctx = GET_CONTEXT(ctx); 192302080Shselasky 193302080Shselasky if (ctx == NULL || cb_fn == NULL || events == 0 || 194302080Shselasky vendor_id < -1 || vendor_id > 0xffff || 195302080Shselasky product_id < -1 || product_id > 0xffff || 196302080Shselasky dev_class < -1 || dev_class > 0xff) 197302080Shselasky return (LIBUSB_ERROR_INVALID_PARAM); 198302080Shselasky 199302080Shselasky handle = malloc(sizeof(*handle)); 200302080Shselasky if (handle == NULL) 201302080Shselasky return (LIBUSB_ERROR_NO_MEM); 202302080Shselasky 203302080Shselasky HOTPLUG_LOCK(ctx); 204302080Shselasky if (ctx->hotplug_handler == NO_THREAD) { 205349669Shselasky libusb_hotplug_enumerate(ctx, &ctx->hotplug_devs); 206349669Shselasky 207302080Shselasky if (pthread_create(&ctx->hotplug_handler, NULL, 208302080Shselasky &libusb_hotplug_scan, ctx) != 0) 209302080Shselasky ctx->hotplug_handler = NO_THREAD; 210302080Shselasky } 211302080Shselasky handle->events = events; 212302080Shselasky handle->vendor = vendor_id; 213302080Shselasky handle->product = product_id; 214302080Shselasky handle->devclass = dev_class; 215302080Shselasky handle->fn = cb_fn; 216302080Shselasky handle->user_data = user_data; 217302080Shselasky 218302080Shselasky if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 219302080Shselasky TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 220302080Shselasky if (libusb_hotplug_filter(ctx, handle, adev, 221302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 222302080Shselasky continue; 223302080Shselasky free(handle); 224302080Shselasky handle = NULL; 225302080Shselasky break; 226302080Shselasky } 227302080Shselasky } 228302080Shselasky if (handle != NULL) 229302080Shselasky TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 230302080Shselasky HOTPLUG_UNLOCK(ctx); 231302080Shselasky 232302080Shselasky if (phandle != NULL) 233302080Shselasky *phandle = handle; 234302080Shselasky return (LIBUSB_SUCCESS); 235302080Shselasky} 236302080Shselasky 237302080Shselaskyvoid libusb_hotplug_deregister_callback(libusb_context *ctx, 238302080Shselasky libusb_hotplug_callback_handle handle) 239302080Shselasky{ 240302080Shselasky ctx = GET_CONTEXT(ctx); 241302080Shselasky 242302080Shselasky if (ctx == NULL || handle == NULL) 243302080Shselasky return; 244302080Shselasky 245302080Shselasky HOTPLUG_LOCK(ctx); 246302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 247302080Shselasky HOTPLUG_UNLOCK(ctx); 248302080Shselasky 249302080Shselasky free(handle); 250302080Shselasky} 251