libusb10_hotplug.c revision 349669
1302080Shselasky/* $FreeBSD: stable/10/lib/libusb/libusb10_hotplug.c 349669 2019-07-03 18:26:07Z 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) { 126349669Shselasky if (libusb_hotplug_enumerate(ctx, &hotplug_devs) < 0) 127302080Shselasky continue; 128302080Shselasky } else { 129302080Shselasky do_loop = 0; 130302080Shselasky } 131302080Shselasky 132302080Shselasky /* figure out which devices are gone */ 133302080Shselasky TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 134302080Shselasky TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 135302080Shselasky if (libusb_hotplug_equal(adev, bdev)) 136302080Shselasky break; 137302080Shselasky } 138302080Shselasky if (bdev == NULL) { 139302080Shselasky TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 140302080Shselasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 141302080Shselasky if (libusb_hotplug_filter(ctx, acbh, adev, 142302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 143302080Shselasky continue; 144302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 145302080Shselasky free(acbh); 146302080Shselasky } 147302080Shselasky libusb_unref_device(adev); 148302080Shselasky } 149302080Shselasky } 150302080Shselasky 151302080Shselasky /* figure out which devices are new */ 152302080Shselasky TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 153302080Shselasky TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 154302080Shselasky if (libusb_hotplug_equal(adev, bdev)) 155302080Shselasky break; 156302080Shselasky } 157302080Shselasky if (bdev == NULL) { 158302080Shselasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 159302080Shselasky TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 160302080Shselasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 161302080Shselasky if (libusb_hotplug_filter(ctx, acbh, adev, 162302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 163302080Shselasky continue; 164302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 165302080Shselasky free(acbh); 166302080Shselasky } 167302080Shselasky } 168302080Shselasky } 169302080Shselasky HOTPLUG_UNLOCK(ctx); 170302080Shselasky 171302080Shselasky /* unref remaining devices */ 172302080Shselasky while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 173302080Shselasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 174302080Shselasky libusb_unref_device(adev); 175302080Shselasky } 176302080Shselasky } 177302080Shselasky return (NULL); 178302080Shselasky} 179302080Shselasky 180302080Shselaskyint libusb_hotplug_register_callback(libusb_context *ctx, 181302080Shselasky libusb_hotplug_event events, libusb_hotplug_flag flags, 182302080Shselasky int vendor_id, int product_id, int dev_class, 183302080Shselasky libusb_hotplug_callback_fn cb_fn, void *user_data, 184302080Shselasky libusb_hotplug_callback_handle *phandle) 185302080Shselasky{ 186302080Shselasky libusb_hotplug_callback_handle handle; 187302080Shselasky struct libusb_device *adev; 188302080Shselasky 189302080Shselasky ctx = GET_CONTEXT(ctx); 190302080Shselasky 191302080Shselasky if (ctx == NULL || cb_fn == NULL || events == 0 || 192302080Shselasky vendor_id < -1 || vendor_id > 0xffff || 193302080Shselasky product_id < -1 || product_id > 0xffff || 194302080Shselasky dev_class < -1 || dev_class > 0xff) 195302080Shselasky return (LIBUSB_ERROR_INVALID_PARAM); 196302080Shselasky 197302080Shselasky handle = malloc(sizeof(*handle)); 198302080Shselasky if (handle == NULL) 199302080Shselasky return (LIBUSB_ERROR_NO_MEM); 200302080Shselasky 201302080Shselasky HOTPLUG_LOCK(ctx); 202302080Shselasky if (ctx->hotplug_handler == NO_THREAD) { 203349669Shselasky libusb_hotplug_enumerate(ctx, &ctx->hotplug_devs); 204349669Shselasky 205302080Shselasky if (pthread_create(&ctx->hotplug_handler, NULL, 206302080Shselasky &libusb_hotplug_scan, ctx) != 0) 207302080Shselasky ctx->hotplug_handler = NO_THREAD; 208302080Shselasky } 209302080Shselasky handle->events = events; 210302080Shselasky handle->vendor = vendor_id; 211302080Shselasky handle->product = product_id; 212302080Shselasky handle->devclass = dev_class; 213302080Shselasky handle->fn = cb_fn; 214302080Shselasky handle->user_data = user_data; 215302080Shselasky 216302080Shselasky if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 217302080Shselasky TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 218302080Shselasky if (libusb_hotplug_filter(ctx, handle, adev, 219302080Shselasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 220302080Shselasky continue; 221302080Shselasky free(handle); 222302080Shselasky handle = NULL; 223302080Shselasky break; 224302080Shselasky } 225302080Shselasky } 226302080Shselasky if (handle != NULL) 227302080Shselasky TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 228302080Shselasky HOTPLUG_UNLOCK(ctx); 229302080Shselasky 230302080Shselasky if (phandle != NULL) 231302080Shselasky *phandle = handle; 232302080Shselasky return (LIBUSB_SUCCESS); 233302080Shselasky} 234302080Shselasky 235302080Shselaskyvoid libusb_hotplug_deregister_callback(libusb_context *ctx, 236302080Shselasky libusb_hotplug_callback_handle handle) 237302080Shselasky{ 238302080Shselasky ctx = GET_CONTEXT(ctx); 239302080Shselasky 240302080Shselasky if (ctx == NULL || handle == NULL) 241302080Shselasky return; 242302080Shselasky 243302080Shselasky HOTPLUG_LOCK(ctx); 244302080Shselasky TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 245302080Shselasky HOTPLUG_UNLOCK(ctx); 246302080Shselasky 247302080Shselasky free(handle); 248302080Shselasky} 249