1*7bdc064bSHans Petter Selasky /* $FreeBSD$ */ 2*7bdc064bSHans Petter Selasky /*- 3*7bdc064bSHans Petter Selasky * Copyright (c) 2016 Hans Petter Selasky. All rights reserved. 4*7bdc064bSHans Petter Selasky * 5*7bdc064bSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 6*7bdc064bSHans Petter Selasky * modification, are permitted provided that the following conditions 7*7bdc064bSHans Petter Selasky * are met: 8*7bdc064bSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 9*7bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 10*7bdc064bSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 11*7bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 12*7bdc064bSHans Petter Selasky * documentation and/or other materials provided with the distribution. 13*7bdc064bSHans Petter Selasky * 14*7bdc064bSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*7bdc064bSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*7bdc064bSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*7bdc064bSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*7bdc064bSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*7bdc064bSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*7bdc064bSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*7bdc064bSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*7bdc064bSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*7bdc064bSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*7bdc064bSHans Petter Selasky * SUCH DAMAGE. 25*7bdc064bSHans Petter Selasky */ 26*7bdc064bSHans Petter Selasky 27*7bdc064bSHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE 28*7bdc064bSHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE 29*7bdc064bSHans Petter Selasky #else 30*7bdc064bSHans Petter Selasky #include <assert.h> 31*7bdc064bSHans Petter Selasky #include <errno.h> 32*7bdc064bSHans Petter Selasky #include <poll.h> 33*7bdc064bSHans Petter Selasky #include <pthread.h> 34*7bdc064bSHans Petter Selasky #include <stdio.h> 35*7bdc064bSHans Petter Selasky #include <stdlib.h> 36*7bdc064bSHans Petter Selasky #include <string.h> 37*7bdc064bSHans Petter Selasky #include <unistd.h> 38*7bdc064bSHans Petter Selasky #include <time.h> 39*7bdc064bSHans Petter Selasky #include <sys/fcntl.h> 40*7bdc064bSHans Petter Selasky #include <sys/ioctl.h> 41*7bdc064bSHans Petter Selasky #include <sys/queue.h> 42*7bdc064bSHans Petter Selasky #include <sys/endian.h> 43*7bdc064bSHans Petter Selasky #endif 44*7bdc064bSHans Petter Selasky 45*7bdc064bSHans Petter Selasky #define libusb_device_handle libusb20_device 46*7bdc064bSHans Petter Selasky 47*7bdc064bSHans Petter Selasky #include "libusb20.h" 48*7bdc064bSHans Petter Selasky #include "libusb20_desc.h" 49*7bdc064bSHans Petter Selasky #include "libusb20_int.h" 50*7bdc064bSHans Petter Selasky #include "libusb.h" 51*7bdc064bSHans Petter Selasky #include "libusb10.h" 52*7bdc064bSHans Petter Selasky 53*7bdc064bSHans Petter Selasky static int 54*7bdc064bSHans Petter Selasky libusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) 55*7bdc064bSHans Petter Selasky { 56*7bdc064bSHans Petter Selasky struct libusb20_device *adev = _adev->os_priv; 57*7bdc064bSHans Petter Selasky struct libusb20_device *bdev = _bdev->os_priv; 58*7bdc064bSHans Petter Selasky 59*7bdc064bSHans Petter Selasky if (adev->bus_number != bdev->bus_number) 60*7bdc064bSHans Petter Selasky return (0); 61*7bdc064bSHans Petter Selasky if (adev->device_address != bdev->device_address) 62*7bdc064bSHans Petter Selasky return (0); 63*7bdc064bSHans Petter Selasky if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) 64*7bdc064bSHans Petter Selasky return (0); 65*7bdc064bSHans Petter Selasky if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) 66*7bdc064bSHans Petter Selasky return (0); 67*7bdc064bSHans Petter Selasky return (1); 68*7bdc064bSHans Petter Selasky } 69*7bdc064bSHans Petter Selasky 70*7bdc064bSHans Petter Selasky static int 71*7bdc064bSHans Petter Selasky libusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, 72*7bdc064bSHans Petter Selasky libusb_device *dev, libusb_hotplug_event event) 73*7bdc064bSHans Petter Selasky { 74*7bdc064bSHans Petter Selasky if (!(pcbh->events & event)) 75*7bdc064bSHans Petter Selasky return (0); 76*7bdc064bSHans Petter Selasky if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && 77*7bdc064bSHans Petter Selasky pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) 78*7bdc064bSHans Petter Selasky return (0); 79*7bdc064bSHans Petter Selasky if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && 80*7bdc064bSHans Petter Selasky pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) 81*7bdc064bSHans Petter Selasky return (0); 82*7bdc064bSHans Petter Selasky if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && 83*7bdc064bSHans Petter Selasky pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) 84*7bdc064bSHans Petter Selasky return (0); 85*7bdc064bSHans Petter Selasky return (pcbh->fn(ctx, dev, event, pcbh->user_data)); 86*7bdc064bSHans Petter Selasky } 87*7bdc064bSHans Petter Selasky 88*7bdc064bSHans Petter Selasky static void * 89*7bdc064bSHans Petter Selasky libusb_hotplug_scan(void *arg) 90*7bdc064bSHans Petter Selasky { 91*7bdc064bSHans Petter Selasky TAILQ_HEAD(, libusb_device) hotplug_devs; 92*7bdc064bSHans Petter Selasky libusb_hotplug_callback_handle acbh; 93*7bdc064bSHans Petter Selasky libusb_hotplug_callback_handle bcbh; 94*7bdc064bSHans Petter Selasky libusb_context *ctx = arg; 95*7bdc064bSHans Petter Selasky libusb_device **ppdev; 96*7bdc064bSHans Petter Selasky libusb_device *temp; 97*7bdc064bSHans Petter Selasky libusb_device *adev; 98*7bdc064bSHans Petter Selasky libusb_device *bdev; 99*7bdc064bSHans Petter Selasky unsigned do_loop = 1; 100*7bdc064bSHans Petter Selasky ssize_t count; 101*7bdc064bSHans Petter Selasky ssize_t x; 102*7bdc064bSHans Petter Selasky 103*7bdc064bSHans Petter Selasky while (do_loop) { 104*7bdc064bSHans Petter Selasky usleep(4000000); 105*7bdc064bSHans Petter Selasky 106*7bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 107*7bdc064bSHans Petter Selasky 108*7bdc064bSHans Petter Selasky TAILQ_INIT(&hotplug_devs); 109*7bdc064bSHans Petter Selasky 110*7bdc064bSHans Petter Selasky if (ctx->hotplug_handler != NO_THREAD) { 111*7bdc064bSHans Petter Selasky count = libusb_get_device_list(ctx, &ppdev); 112*7bdc064bSHans Petter Selasky if (count < 0) 113*7bdc064bSHans Petter Selasky continue; 114*7bdc064bSHans Petter Selasky for (x = 0; x != count; x++) { 115*7bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&hotplug_devs, ppdev[x], 116*7bdc064bSHans Petter Selasky hotplug_entry); 117*7bdc064bSHans Petter Selasky } 118*7bdc064bSHans Petter Selasky libusb_free_device_list(ppdev, 0); 119*7bdc064bSHans Petter Selasky } else { 120*7bdc064bSHans Petter Selasky do_loop = 0; 121*7bdc064bSHans Petter Selasky } 122*7bdc064bSHans Petter Selasky 123*7bdc064bSHans Petter Selasky /* figure out which devices are gone */ 124*7bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 125*7bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 126*7bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 127*7bdc064bSHans Petter Selasky break; 128*7bdc064bSHans Petter Selasky } 129*7bdc064bSHans Petter Selasky if (bdev == NULL) { 130*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 131*7bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 132*7bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 133*7bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 134*7bdc064bSHans Petter Selasky continue; 135*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 136*7bdc064bSHans Petter Selasky free(acbh); 137*7bdc064bSHans Petter Selasky } 138*7bdc064bSHans Petter Selasky libusb_unref_device(adev); 139*7bdc064bSHans Petter Selasky } 140*7bdc064bSHans Petter Selasky } 141*7bdc064bSHans Petter Selasky 142*7bdc064bSHans Petter Selasky /* figure out which devices are new */ 143*7bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 144*7bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 145*7bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 146*7bdc064bSHans Petter Selasky break; 147*7bdc064bSHans Petter Selasky } 148*7bdc064bSHans Petter Selasky if (bdev == NULL) { 149*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 150*7bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 151*7bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 152*7bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 153*7bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 154*7bdc064bSHans Petter Selasky continue; 155*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 156*7bdc064bSHans Petter Selasky free(acbh); 157*7bdc064bSHans Petter Selasky } 158*7bdc064bSHans Petter Selasky } 159*7bdc064bSHans Petter Selasky } 160*7bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 161*7bdc064bSHans Petter Selasky 162*7bdc064bSHans Petter Selasky /* unref remaining devices */ 163*7bdc064bSHans Petter Selasky while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 164*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 165*7bdc064bSHans Petter Selasky libusb_unref_device(adev); 166*7bdc064bSHans Petter Selasky } 167*7bdc064bSHans Petter Selasky } 168*7bdc064bSHans Petter Selasky return (NULL); 169*7bdc064bSHans Petter Selasky } 170*7bdc064bSHans Petter Selasky 171*7bdc064bSHans Petter Selasky int libusb_hotplug_register_callback(libusb_context *ctx, 172*7bdc064bSHans Petter Selasky libusb_hotplug_event events, libusb_hotplug_flag flags, 173*7bdc064bSHans Petter Selasky int vendor_id, int product_id, int dev_class, 174*7bdc064bSHans Petter Selasky libusb_hotplug_callback_fn cb_fn, void *user_data, 175*7bdc064bSHans Petter Selasky libusb_hotplug_callback_handle *phandle) 176*7bdc064bSHans Petter Selasky { 177*7bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle; 178*7bdc064bSHans Petter Selasky struct libusb_device *adev; 179*7bdc064bSHans Petter Selasky 180*7bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 181*7bdc064bSHans Petter Selasky 182*7bdc064bSHans Petter Selasky if (ctx == NULL || cb_fn == NULL || events == 0 || 183*7bdc064bSHans Petter Selasky vendor_id < -1 || vendor_id > 0xffff || 184*7bdc064bSHans Petter Selasky product_id < -1 || product_id > 0xffff || 185*7bdc064bSHans Petter Selasky dev_class < -1 || dev_class > 0xff) 186*7bdc064bSHans Petter Selasky return (LIBUSB_ERROR_INVALID_PARAM); 187*7bdc064bSHans Petter Selasky 188*7bdc064bSHans Petter Selasky handle = malloc(sizeof(*handle)); 189*7bdc064bSHans Petter Selasky if (handle == NULL) 190*7bdc064bSHans Petter Selasky return (LIBUSB_ERROR_NO_MEM); 191*7bdc064bSHans Petter Selasky 192*7bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 193*7bdc064bSHans Petter Selasky if (ctx->hotplug_handler == NO_THREAD) { 194*7bdc064bSHans Petter Selasky if (pthread_create(&ctx->hotplug_handler, NULL, 195*7bdc064bSHans Petter Selasky &libusb_hotplug_scan, ctx) != 0) 196*7bdc064bSHans Petter Selasky ctx->hotplug_handler = NO_THREAD; 197*7bdc064bSHans Petter Selasky } 198*7bdc064bSHans Petter Selasky handle->events = events; 199*7bdc064bSHans Petter Selasky handle->vendor = vendor_id; 200*7bdc064bSHans Petter Selasky handle->product = product_id; 201*7bdc064bSHans Petter Selasky handle->devclass = dev_class; 202*7bdc064bSHans Petter Selasky handle->fn = cb_fn; 203*7bdc064bSHans Petter Selasky handle->user_data = user_data; 204*7bdc064bSHans Petter Selasky 205*7bdc064bSHans Petter Selasky if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 206*7bdc064bSHans Petter Selasky TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 207*7bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, handle, adev, 208*7bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 209*7bdc064bSHans Petter Selasky continue; 210*7bdc064bSHans Petter Selasky free(handle); 211*7bdc064bSHans Petter Selasky handle = NULL; 212*7bdc064bSHans Petter Selasky break; 213*7bdc064bSHans Petter Selasky } 214*7bdc064bSHans Petter Selasky } 215*7bdc064bSHans Petter Selasky if (handle != NULL) 216*7bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 217*7bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 218*7bdc064bSHans Petter Selasky 219*7bdc064bSHans Petter Selasky if (phandle != NULL) 220*7bdc064bSHans Petter Selasky *phandle = handle; 221*7bdc064bSHans Petter Selasky return (LIBUSB_SUCCESS); 222*7bdc064bSHans Petter Selasky } 223*7bdc064bSHans Petter Selasky 224*7bdc064bSHans Petter Selasky void libusb_hotplug_deregister_callback(libusb_context *ctx, 225*7bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle) 226*7bdc064bSHans Petter Selasky { 227*7bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 228*7bdc064bSHans Petter Selasky 229*7bdc064bSHans Petter Selasky if (ctx == NULL || handle == NULL) 230*7bdc064bSHans Petter Selasky return; 231*7bdc064bSHans Petter Selasky 232*7bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 233*7bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 234*7bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 235*7bdc064bSHans Petter Selasky 236*7bdc064bSHans Petter Selasky free(handle); 237*7bdc064bSHans Petter Selasky } 238