libusb10_hotplug.c revision 302080
191396Stmm/* $FreeBSD: head/lib/libusb/libusb10_hotplug.c 302080 2016-06-22 10:38:41Z hselasky $ */ 291396Stmm/*- 391396Stmm * Copyright (c) 2016 Hans Petter Selasky. All rights reserved. 491396Stmm * 591396Stmm * Redistribution and use in source and binary forms, with or without 691396Stmm * modification, are permitted provided that the following conditions 791396Stmm * are met: 891396Stmm * 1. Redistributions of source code must retain the above copyright 991396Stmm * notice, this list of conditions and the following disclaimer. 1091396Stmm * 2. Redistributions in binary form must reproduce the above copyright 1191396Stmm * notice, this list of conditions and the following disclaimer in the 1291396Stmm * documentation and/or other materials provided with the distribution. 1391396Stmm * 1491396Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1591396Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1691396Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1791396Stmm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1891396Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1991396Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2091396Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2191396Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2291396Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2391396Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2491396Stmm * SUCH DAMAGE. 2591396Stmm */ 2691396Stmm 2791396Stmm#ifdef LIBUSB_GLOBAL_INCLUDE_FILE 2891396Stmm#include LIBUSB_GLOBAL_INCLUDE_FILE 2991396Stmm#else 3091396Stmm#include <assert.h> 3191396Stmm#include <errno.h> 3291396Stmm#include <poll.h> 3391396Stmm#include <pthread.h> 3491396Stmm#include <stdio.h> 3591396Stmm#include <stdlib.h> 3691396Stmm#include <string.h> 3791396Stmm#include <unistd.h> 3891396Stmm#include <time.h> 3991396Stmm#include <sys/fcntl.h> 4091396Stmm#include <sys/ioctl.h> 4191396Stmm#include <sys/queue.h> 4291396Stmm#include <sys/endian.h> 4391396Stmm#endif 4491396Stmm 4591396Stmm#define libusb_device_handle libusb20_device 4691396Stmm 4791396Stmm#include "libusb20.h" 4891396Stmm#include "libusb20_desc.h" 4991396Stmm#include "libusb20_int.h" 5091396Stmm#include "libusb.h" 5191396Stmm#include "libusb10.h" 5291396Stmm 5391396Stmmstatic int 5491396Stmmlibusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) 5591396Stmm{ 5691396Stmm struct libusb20_device *adev = _adev->os_priv; 5791396Stmm struct libusb20_device *bdev = _bdev->os_priv; 5891396Stmm 5991396Stmm if (adev->bus_number != bdev->bus_number) 6091396Stmm return (0); 6191396Stmm if (adev->device_address != bdev->device_address) 6291396Stmm return (0); 6391396Stmm if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) 6491396Stmm return (0); 6591396Stmm if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) 6691396Stmm return (0); 6791396Stmm return (1); 6891396Stmm} 6991396Stmm 7091396Stmmstatic int 7191396Stmmlibusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, 7291396Stmm libusb_device *dev, libusb_hotplug_event event) 7391396Stmm{ 7491396Stmm if (!(pcbh->events & event)) 7591396Stmm return (0); 7691396Stmm if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && 7791396Stmm pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) 7891396Stmm return (0); 7991396Stmm if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && 8091396Stmm pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) 8191396Stmm return (0); 8291396Stmm if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && 8391396Stmm pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) 8491396Stmm return (0); 8591396Stmm return (pcbh->fn(ctx, dev, event, pcbh->user_data)); 8691396Stmm} 8791396Stmm 8891396Stmmstatic void * 8991396Stmmlibusb_hotplug_scan(void *arg) 9091396Stmm{ 9191396Stmm TAILQ_HEAD(, libusb_device) hotplug_devs; 9291396Stmm libusb_hotplug_callback_handle acbh; 9391396Stmm libusb_hotplug_callback_handle bcbh; 9491396Stmm libusb_context *ctx = arg; 9591396Stmm libusb_device **ppdev; 9691396Stmm libusb_device *temp; 9791396Stmm libusb_device *adev; 9891396Stmm libusb_device *bdev; 9991396Stmm unsigned do_loop = 1; 10091396Stmm ssize_t count; 10191396Stmm ssize_t x; 10291396Stmm 10391396Stmm while (do_loop) { 10491396Stmm usleep(4000000); 10591396Stmm 10691396Stmm HOTPLUG_LOCK(ctx); 10791396Stmm 10891396Stmm TAILQ_INIT(&hotplug_devs); 10991396Stmm 11091396Stmm if (ctx->hotplug_handler != NO_THREAD) { 11191396Stmm count = libusb_get_device_list(ctx, &ppdev); 11291396Stmm if (count < 0) 11391396Stmm continue; 11491396Stmm for (x = 0; x != count; x++) { 11591396Stmm TAILQ_INSERT_TAIL(&hotplug_devs, ppdev[x], 11691396Stmm hotplug_entry); 11791396Stmm } 11891396Stmm libusb_free_device_list(ppdev, 0); 11991396Stmm } else { 12091396Stmm do_loop = 0; 12191396Stmm } 12291396Stmm 12391396Stmm /* figure out which devices are gone */ 12491396Stmm TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 12591396Stmm TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 12691396Stmm if (libusb_hotplug_equal(adev, bdev)) 12791396Stmm break; 12891396Stmm } 12991396Stmm if (bdev == NULL) { 13091396Stmm TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 13191396Stmm TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 13291396Stmm if (libusb_hotplug_filter(ctx, acbh, adev, 13391396Stmm LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 13491396Stmm continue; 13591396Stmm TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 13691396Stmm free(acbh); 13791396Stmm } 13891396Stmm libusb_unref_device(adev); 13991396Stmm } 14091396Stmm } 14191396Stmm 14291396Stmm /* figure out which devices are new */ 14391396Stmm TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 14491396Stmm TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 14591396Stmm if (libusb_hotplug_equal(adev, bdev)) 14691396Stmm break; 14791396Stmm } 14891396Stmm if (bdev == NULL) { 14991396Stmm TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 15091396Stmm TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 15191396Stmm TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 15291396Stmm if (libusb_hotplug_filter(ctx, acbh, adev, 15391396Stmm LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 15491396Stmm continue; 15591396Stmm TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 15691396Stmm free(acbh); 15791396Stmm } 15891396Stmm } 15991396Stmm } 16091396Stmm HOTPLUG_UNLOCK(ctx); 16191396Stmm 16291396Stmm /* unref remaining devices */ 16391396Stmm while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 16491396Stmm TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 16591396Stmm libusb_unref_device(adev); 16691396Stmm } 16791396Stmm } 16891396Stmm return (NULL); 16991396Stmm} 17091396Stmm 17191396Stmmint libusb_hotplug_register_callback(libusb_context *ctx, 17291396Stmm libusb_hotplug_event events, libusb_hotplug_flag flags, 17391396Stmm int vendor_id, int product_id, int dev_class, 17491396Stmm libusb_hotplug_callback_fn cb_fn, void *user_data, 17591396Stmm libusb_hotplug_callback_handle *phandle) 17691396Stmm{ 17791396Stmm libusb_hotplug_callback_handle handle; 17891396Stmm struct libusb_device *adev; 17991396Stmm 18091396Stmm ctx = GET_CONTEXT(ctx); 18191396Stmm 18291396Stmm if (ctx == NULL || cb_fn == NULL || events == 0 || 18391396Stmm vendor_id < -1 || vendor_id > 0xffff || 18491396Stmm product_id < -1 || product_id > 0xffff || 18591396Stmm dev_class < -1 || dev_class > 0xff) 18691396Stmm return (LIBUSB_ERROR_INVALID_PARAM); 18791396Stmm 18891396Stmm handle = malloc(sizeof(*handle)); 18991396Stmm if (handle == NULL) 19091396Stmm return (LIBUSB_ERROR_NO_MEM); 19191396Stmm 19291396Stmm HOTPLUG_LOCK(ctx); 19391396Stmm if (ctx->hotplug_handler == NO_THREAD) { 19491396Stmm if (pthread_create(&ctx->hotplug_handler, NULL, 19591396Stmm &libusb_hotplug_scan, ctx) != 0) 19691396Stmm ctx->hotplug_handler = NO_THREAD; 19791396Stmm } 19891396Stmm handle->events = events; 19991396Stmm handle->vendor = vendor_id; 20091396Stmm handle->product = product_id; 20191396Stmm handle->devclass = dev_class; 20291396Stmm handle->fn = cb_fn; 20391396Stmm handle->user_data = user_data; 20491396Stmm 20591396Stmm if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 20691396Stmm TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 20791396Stmm if (libusb_hotplug_filter(ctx, handle, adev, 20891396Stmm LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 20991396Stmm continue; 21091396Stmm free(handle); 21191396Stmm handle = NULL; 21291396Stmm break; 21391396Stmm } 21491396Stmm } 21591396Stmm if (handle != NULL) 21691396Stmm TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 21791396Stmm HOTPLUG_UNLOCK(ctx); 21891396Stmm 21991396Stmm if (phandle != NULL) 22091396Stmm *phandle = handle; 22191396Stmm return (LIBUSB_SUCCESS); 22291396Stmm} 22391396Stmm 22491396Stmmvoid libusb_hotplug_deregister_callback(libusb_context *ctx, 22591396Stmm libusb_hotplug_callback_handle handle) 22691396Stmm{ 22791396Stmm ctx = GET_CONTEXT(ctx); 22891396Stmm 22991396Stmm if (ctx == NULL || handle == NULL) 23091396Stmm return; 23191396Stmm 23291396Stmm HOTPLUG_LOCK(ctx); 23391396Stmm TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 23491396Stmm HOTPLUG_UNLOCK(ctx); 23591396Stmm 23691396Stmm free(handle); 23791396Stmm} 23891396Stmm