17bdc064bSHans Petter Selasky /*- 2a5b24a2bSHans Petter Selasky * Copyright (c) 2016-2019 Hans Petter Selasky. All rights reserved. 37bdc064bSHans Petter Selasky * 47bdc064bSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 57bdc064bSHans Petter Selasky * modification, are permitted provided that the following conditions 67bdc064bSHans Petter Selasky * are met: 77bdc064bSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 87bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 97bdc064bSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 107bdc064bSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 117bdc064bSHans Petter Selasky * documentation and/or other materials provided with the distribution. 127bdc064bSHans Petter Selasky * 137bdc064bSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 147bdc064bSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 157bdc064bSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 167bdc064bSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 177bdc064bSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 187bdc064bSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 197bdc064bSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 207bdc064bSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 217bdc064bSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 227bdc064bSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 237bdc064bSHans Petter Selasky * SUCH DAMAGE. 247bdc064bSHans Petter Selasky */ 257bdc064bSHans Petter Selasky 269dc96d8bSBaptiste Daroussin #include <netlink/netlink_snl_generic.h> 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> 439dc96d8bSBaptiste Daroussin #include <sys/socket.h> 449dc96d8bSBaptiste Daroussin #include <sys/un.h> 459dc96d8bSBaptiste Daroussin #include <sys/module.h> 469dc96d8bSBaptiste Daroussin #include <sys/linker.h> 477bdc064bSHans Petter Selasky #endif 487bdc064bSHans Petter Selasky 497bdc064bSHans Petter Selasky #define libusb_device_handle libusb20_device 507bdc064bSHans Petter Selasky 517bdc064bSHans Petter Selasky #include "libusb20.h" 527bdc064bSHans Petter Selasky #include "libusb20_desc.h" 537bdc064bSHans Petter Selasky #include "libusb20_int.h" 547bdc064bSHans Petter Selasky #include "libusb.h" 557bdc064bSHans Petter Selasky #include "libusb10.h" 567bdc064bSHans Petter Selasky 579dc96d8bSBaptiste Daroussin #define DEVDPIPE "/var/run/devd.seqpacket.pipe" 589dc96d8bSBaptiste Daroussin #define DEVCTL_MAXBUF 1024 599dc96d8bSBaptiste Daroussin 609dc96d8bSBaptiste Daroussin typedef enum { 619dc96d8bSBaptiste Daroussin broken_event, 629dc96d8bSBaptiste Daroussin invalid_event, 639dc96d8bSBaptiste Daroussin valid_event, 649dc96d8bSBaptiste Daroussin } event_t; 659dc96d8bSBaptiste Daroussin 669dc96d8bSBaptiste Daroussin static bool 679dc96d8bSBaptiste Daroussin netlink_init(libusb_context *ctx) 689dc96d8bSBaptiste Daroussin { 69*a92ce195SBaptiste Daroussin uint32_t group; 709dc96d8bSBaptiste Daroussin 719dc96d8bSBaptiste Daroussin if (modfind("nlsysevent") < 0) 729dc96d8bSBaptiste Daroussin kldload("nlsysevent"); 739dc96d8bSBaptiste Daroussin if (modfind("nlsysevent") < 0) 749dc96d8bSBaptiste Daroussin return (false); 75*a92ce195SBaptiste Daroussin if (!snl_init(&ctx->ss, NETLINK_GENERIC) || (group = 76*a92ce195SBaptiste Daroussin snl_get_genl_mcast_group(&ctx->ss, "nlsysevent", "ACPI", NULL)) == 0) 779dc96d8bSBaptiste Daroussin return (false); 789dc96d8bSBaptiste Daroussin 79*a92ce195SBaptiste Daroussin if (setsockopt(ctx->ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, 80*a92ce195SBaptiste Daroussin sizeof(group)) == -1) 819dc96d8bSBaptiste Daroussin return (false); 829dc96d8bSBaptiste Daroussin 839dc96d8bSBaptiste Daroussin ctx->usb_event_mode = usb_event_netlink; 849dc96d8bSBaptiste Daroussin return (true); 859dc96d8bSBaptiste Daroussin } 869dc96d8bSBaptiste Daroussin 879dc96d8bSBaptiste Daroussin static bool 889dc96d8bSBaptiste Daroussin devd_init(libusb_context *ctx) 899dc96d8bSBaptiste Daroussin { 909dc96d8bSBaptiste Daroussin struct sockaddr_un devd_addr; 919dc96d8bSBaptiste Daroussin 929dc96d8bSBaptiste Daroussin bzero(&devd_addr, sizeof(devd_addr)); 939dc96d8bSBaptiste Daroussin if ((ctx->devd_pipe = socket(PF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK, 0)) < 0) 949dc96d8bSBaptiste Daroussin return (false); 959dc96d8bSBaptiste Daroussin 969dc96d8bSBaptiste Daroussin devd_addr.sun_family = PF_LOCAL; 979dc96d8bSBaptiste Daroussin strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 989dc96d8bSBaptiste Daroussin if (connect(ctx->devd_pipe, (struct sockaddr *)&devd_addr, 999dc96d8bSBaptiste Daroussin sizeof(devd_addr)) == -1) { 1009dc96d8bSBaptiste Daroussin close(ctx->devd_pipe); 1019dc96d8bSBaptiste Daroussin ctx->devd_pipe = -1; 1029dc96d8bSBaptiste Daroussin return (false); 1039dc96d8bSBaptiste Daroussin } 1049dc96d8bSBaptiste Daroussin 1059dc96d8bSBaptiste Daroussin ctx->usb_event_mode = usb_event_devd; 1069dc96d8bSBaptiste Daroussin return (true); 1079dc96d8bSBaptiste Daroussin } 1089dc96d8bSBaptiste Daroussin 1099dc96d8bSBaptiste Daroussin struct nlevent { 1109dc96d8bSBaptiste Daroussin const char *name; 1119dc96d8bSBaptiste Daroussin const char *subsystem; 1129dc96d8bSBaptiste Daroussin const char *type; 1139dc96d8bSBaptiste Daroussin const char *data; 1149dc96d8bSBaptiste Daroussin }; 1159dc96d8bSBaptiste Daroussin 1169dc96d8bSBaptiste Daroussin #define _OUT(_field) offsetof(struct nlevent, _field) 1179dc96d8bSBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = { 1189dc96d8bSBaptiste Daroussin { .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string }, 1199dc96d8bSBaptiste Daroussin { .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string }, 1209dc96d8bSBaptiste Daroussin { .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string }, 1219dc96d8bSBaptiste Daroussin { .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string }, 1229dc96d8bSBaptiste Daroussin }; 1239dc96d8bSBaptiste Daroussin #undef _OUT 1249dc96d8bSBaptiste Daroussin 1259dc96d8bSBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get); 1269dc96d8bSBaptiste Daroussin 1279dc96d8bSBaptiste Daroussin static event_t 1289dc96d8bSBaptiste Daroussin verify_event_validity(libusb_context *ctx) 1299dc96d8bSBaptiste Daroussin { 1309dc96d8bSBaptiste Daroussin if (ctx->usb_event_mode == usb_event_netlink) { 1319dc96d8bSBaptiste Daroussin struct nlmsghdr *hdr; 1329dc96d8bSBaptiste Daroussin struct nlevent ne; 1339dc96d8bSBaptiste Daroussin 1349dc96d8bSBaptiste Daroussin hdr = snl_read_message(&ctx->ss); 1359dc96d8bSBaptiste Daroussin if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) { 1369dc96d8bSBaptiste Daroussin memset(&ne, 0, sizeof(ne)); 1379dc96d8bSBaptiste Daroussin if (!snl_parse_nlmsg(&ctx->ss, hdr, &nlevent_get_parser, &ne)) 1389dc96d8bSBaptiste Daroussin return (broken_event); 1399dc96d8bSBaptiste Daroussin if (strcmp(ne.subsystem, "DEVICE") == 0) 1409dc96d8bSBaptiste Daroussin return (valid_event); 1419dc96d8bSBaptiste Daroussin return (invalid_event); 1429dc96d8bSBaptiste Daroussin } 1439dc96d8bSBaptiste Daroussin return (invalid_event); 1449dc96d8bSBaptiste Daroussin } else if (ctx->usb_event_mode == usb_event_devd) { 1459dc96d8bSBaptiste Daroussin char buf[DEVCTL_MAXBUF]; 1469dc96d8bSBaptiste Daroussin ssize_t len; 1479dc96d8bSBaptiste Daroussin 1489dc96d8bSBaptiste Daroussin len = read(ctx->devd_pipe, buf, sizeof(buf)); 1499dc96d8bSBaptiste Daroussin if (len == 0 || (len < 0 && errno != EWOULDBLOCK)) 1509dc96d8bSBaptiste Daroussin return (broken_event); 1519dc96d8bSBaptiste Daroussin if (len > 0 && strstr(buf, "system=USB") != NULL && 1529dc96d8bSBaptiste Daroussin strstr(buf, "subsystem=DEVICE") != NULL) 1539dc96d8bSBaptiste Daroussin return (valid_event); 1549dc96d8bSBaptiste Daroussin return (invalid_event); 1559dc96d8bSBaptiste Daroussin } 1569dc96d8bSBaptiste Daroussin return (broken_event); 1579dc96d8bSBaptiste Daroussin } 1589dc96d8bSBaptiste Daroussin 1597bdc064bSHans Petter Selasky static int 1607bdc064bSHans Petter Selasky libusb_hotplug_equal(libusb_device *_adev, libusb_device *_bdev) 1617bdc064bSHans Petter Selasky { 1627bdc064bSHans Petter Selasky struct libusb20_device *adev = _adev->os_priv; 1637bdc064bSHans Petter Selasky struct libusb20_device *bdev = _bdev->os_priv; 1647bdc064bSHans Petter Selasky 1657bdc064bSHans Petter Selasky if (adev->bus_number != bdev->bus_number) 1667bdc064bSHans Petter Selasky return (0); 1677bdc064bSHans Petter Selasky if (adev->device_address != bdev->device_address) 1687bdc064bSHans Petter Selasky return (0); 1697bdc064bSHans Petter Selasky if (memcmp(&adev->ddesc, &bdev->ddesc, sizeof(adev->ddesc))) 1707bdc064bSHans Petter Selasky return (0); 1717bdc064bSHans Petter Selasky if (memcmp(&adev->session_data, &bdev->session_data, sizeof(adev->session_data))) 1727bdc064bSHans Petter Selasky return (0); 1737bdc064bSHans Petter Selasky return (1); 1747bdc064bSHans Petter Selasky } 1757bdc064bSHans Petter Selasky 1767bdc064bSHans Petter Selasky static int 1777bdc064bSHans Petter Selasky libusb_hotplug_filter(libusb_context *ctx, libusb_hotplug_callback_handle pcbh, 1787bdc064bSHans Petter Selasky libusb_device *dev, libusb_hotplug_event event) 1797bdc064bSHans Petter Selasky { 1807bdc064bSHans Petter Selasky if (!(pcbh->events & event)) 1817bdc064bSHans Petter Selasky return (0); 1827bdc064bSHans Petter Selasky if (pcbh->vendor != LIBUSB_HOTPLUG_MATCH_ANY && 1837bdc064bSHans Petter Selasky pcbh->vendor != libusb20_dev_get_device_desc(dev->os_priv)->idVendor) 1847bdc064bSHans Petter Selasky return (0); 1857bdc064bSHans Petter Selasky if (pcbh->product != LIBUSB_HOTPLUG_MATCH_ANY && 1867bdc064bSHans Petter Selasky pcbh->product != libusb20_dev_get_device_desc(dev->os_priv)->idProduct) 1877bdc064bSHans Petter Selasky return (0); 1887bdc064bSHans Petter Selasky if (pcbh->devclass != LIBUSB_HOTPLUG_MATCH_ANY && 1897bdc064bSHans Petter Selasky pcbh->devclass != libusb20_dev_get_device_desc(dev->os_priv)->bDeviceClass) 1907bdc064bSHans Petter Selasky return (0); 1917bdc064bSHans Petter Selasky return (pcbh->fn(ctx, dev, event, pcbh->user_data)); 1927bdc064bSHans Petter Selasky } 1937bdc064bSHans Petter Selasky 194a41b0ec1SHans Petter Selasky static int 195a41b0ec1SHans Petter Selasky libusb_hotplug_enumerate(libusb_context *ctx, struct libusb_device_head *phead) 196a41b0ec1SHans Petter Selasky { 197a41b0ec1SHans Petter Selasky libusb_device **ppdev; 198a41b0ec1SHans Petter Selasky ssize_t count; 199a41b0ec1SHans Petter Selasky ssize_t x; 200a41b0ec1SHans Petter Selasky 201a41b0ec1SHans Petter Selasky count = libusb_get_device_list(ctx, &ppdev); 202a41b0ec1SHans Petter Selasky if (count < 0) 203a41b0ec1SHans Petter Selasky return (-1); 204a41b0ec1SHans Petter Selasky 205a41b0ec1SHans Petter Selasky for (x = 0; x != count; x++) 206a41b0ec1SHans Petter Selasky TAILQ_INSERT_TAIL(phead, ppdev[x], hotplug_entry); 207a41b0ec1SHans Petter Selasky 208a41b0ec1SHans Petter Selasky libusb_free_device_list(ppdev, 0); 209a41b0ec1SHans Petter Selasky return (0); 210a41b0ec1SHans Petter Selasky } 211a41b0ec1SHans Petter Selasky 2127bdc064bSHans Petter Selasky static void * 2137bdc064bSHans Petter Selasky libusb_hotplug_scan(void *arg) 2147bdc064bSHans Petter Selasky { 2159dc96d8bSBaptiste Daroussin struct pollfd pfd; 216a41b0ec1SHans Petter Selasky struct libusb_device_head hotplug_devs; 2177bdc064bSHans Petter Selasky libusb_hotplug_callback_handle acbh; 2187bdc064bSHans Petter Selasky libusb_hotplug_callback_handle bcbh; 2197bdc064bSHans Petter Selasky libusb_context *ctx = arg; 2207bdc064bSHans Petter Selasky libusb_device *temp; 2217bdc064bSHans Petter Selasky libusb_device *adev; 2227bdc064bSHans Petter Selasky libusb_device *bdev; 2239dc96d8bSBaptiste Daroussin int timeout = INFTIM; 2249dc96d8bSBaptiste Daroussin int nfds; 2257bdc064bSHans Petter Selasky 2269dc96d8bSBaptiste Daroussin memset(&pfd, 0, sizeof(pfd)); 2279dc96d8bSBaptiste Daroussin if (ctx->usb_event_mode == usb_event_devd) { 2289dc96d8bSBaptiste Daroussin pfd.fd = ctx->devd_pipe; 2299dc96d8bSBaptiste Daroussin pfd.events = POLLIN | POLLERR; 2309dc96d8bSBaptiste Daroussin nfds = 1; 2319dc96d8bSBaptiste Daroussin } else if (ctx->usb_event_mode == usb_event_netlink) { 2329dc96d8bSBaptiste Daroussin pfd.fd = ctx->ss.fd; 2339dc96d8bSBaptiste Daroussin pfd.events = POLLIN | POLLERR; 2349dc96d8bSBaptiste Daroussin nfds = 1; 2359dc96d8bSBaptiste Daroussin } else { 2369dc96d8bSBaptiste Daroussin nfds = 0; 2379dc96d8bSBaptiste Daroussin timeout = 4000; 2389dc96d8bSBaptiste Daroussin } 239ba5834b8SBaptiste Daroussin for (;;) { 2409dc96d8bSBaptiste Daroussin pfd.revents = 0; 2419dc96d8bSBaptiste Daroussin if (poll(&pfd, nfds, timeout) > 0) { 2429dc96d8bSBaptiste Daroussin switch (verify_event_validity(ctx)) { 2439dc96d8bSBaptiste Daroussin case invalid_event: 2449dc96d8bSBaptiste Daroussin continue; 2459dc96d8bSBaptiste Daroussin case valid_event: 2469dc96d8bSBaptiste Daroussin break; 2479dc96d8bSBaptiste Daroussin case broken_event: 2489dc96d8bSBaptiste Daroussin /* There are 2 cases for broken events: 2499dc96d8bSBaptiste Daroussin * - devd and netlink sockets are not available 2509dc96d8bSBaptiste Daroussin * anymore (devd restarted, nlsysevent unloaded) 2519dc96d8bSBaptiste Daroussin * - libusb_exit has been called as it sets NO_THREAD 2529dc96d8bSBaptiste Daroussin * this will result in exiting this loop and this thread 2539dc96d8bSBaptiste Daroussin * immediately 2549dc96d8bSBaptiste Daroussin */ 2559dc96d8bSBaptiste Daroussin nfds = 0; 2569dc96d8bSBaptiste Daroussin if (ctx->usb_event_mode == usb_event_devd) { 2579dc96d8bSBaptiste Daroussin if (ctx->devd_pipe != -1) 2589dc96d8bSBaptiste Daroussin close(ctx->devd_pipe); 2599dc96d8bSBaptiste Daroussin } else if (ctx->usb_event_mode == usb_event_netlink) { 2609dc96d8bSBaptiste Daroussin if (ctx->ss.fd != -1) 2619dc96d8bSBaptiste Daroussin close(ctx->ss.fd); 2629dc96d8bSBaptiste Daroussin } 2639dc96d8bSBaptiste Daroussin ctx->usb_event_mode = usb_event_scan; 2649dc96d8bSBaptiste Daroussin timeout = 4000; 2659dc96d8bSBaptiste Daroussin break; 2669dc96d8bSBaptiste Daroussin } 2679dc96d8bSBaptiste Daroussin } 2687bdc064bSHans Petter Selasky 2697bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 270ba5834b8SBaptiste Daroussin if (ctx->hotplug_handler == NO_THREAD) { 271ba5834b8SBaptiste Daroussin while ((adev = TAILQ_FIRST(&ctx->hotplug_devs)) != NULL) { 272ba5834b8SBaptiste Daroussin TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 273ba5834b8SBaptiste Daroussin libusb_unref_device(adev); 274ba5834b8SBaptiste Daroussin } 2759dc96d8bSBaptiste Daroussin if (ctx->usb_event_mode == usb_event_devd) 2769dc96d8bSBaptiste Daroussin close(ctx->devd_pipe); 2779dc96d8bSBaptiste Daroussin else if (ctx->usb_event_mode == usb_event_netlink) 2789dc96d8bSBaptiste Daroussin close(ctx->ss.fd); 279ba5834b8SBaptiste Daroussin HOTPLUG_UNLOCK(ctx); 280ba5834b8SBaptiste Daroussin break; 281ba5834b8SBaptiste Daroussin } 2827bdc064bSHans Petter Selasky 2837bdc064bSHans Petter Selasky TAILQ_INIT(&hotplug_devs); 2847bdc064bSHans Petter Selasky 285cca46c5eSHans Petter Selasky if (libusb_hotplug_enumerate(ctx, &hotplug_devs) < 0) { 286cca46c5eSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 2877bdc064bSHans Petter Selasky continue; 288cca46c5eSHans Petter Selasky } 2897bdc064bSHans Petter Selasky 2907bdc064bSHans Petter Selasky /* figure out which devices are gone */ 2917bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &ctx->hotplug_devs, hotplug_entry, temp) { 2927bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &hotplug_devs, hotplug_entry) { 2937bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 2947bdc064bSHans Petter Selasky break; 2957bdc064bSHans Petter Selasky } 2967bdc064bSHans Petter Selasky if (bdev == NULL) { 2977bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_devs, adev, hotplug_entry); 2987bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 2997bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 3007bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) == 0) 3017bdc064bSHans Petter Selasky continue; 3027bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 3037bdc064bSHans Petter Selasky free(acbh); 3047bdc064bSHans Petter Selasky } 3057bdc064bSHans Petter Selasky libusb_unref_device(adev); 3067bdc064bSHans Petter Selasky } 3077bdc064bSHans Petter Selasky } 3087bdc064bSHans Petter Selasky 3097bdc064bSHans Petter Selasky /* figure out which devices are new */ 3107bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(adev, &hotplug_devs, hotplug_entry, temp) { 3117bdc064bSHans Petter Selasky TAILQ_FOREACH(bdev, &ctx->hotplug_devs, hotplug_entry) { 3127bdc064bSHans Petter Selasky if (libusb_hotplug_equal(adev, bdev)) 3137bdc064bSHans Petter Selasky break; 3147bdc064bSHans Petter Selasky } 3157bdc064bSHans Petter Selasky if (bdev == NULL) { 3167bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 3177bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_devs, adev, hotplug_entry); 3187bdc064bSHans Petter Selasky TAILQ_FOREACH_SAFE(acbh, &ctx->hotplug_cbh, entry, bcbh) { 3197bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, acbh, adev, 3207bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 3217bdc064bSHans Petter Selasky continue; 3227bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, acbh, entry); 3237bdc064bSHans Petter Selasky free(acbh); 3247bdc064bSHans Petter Selasky } 3257bdc064bSHans Petter Selasky } 3267bdc064bSHans Petter Selasky } 3277bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 3287bdc064bSHans Petter Selasky 3297bdc064bSHans Petter Selasky /* unref remaining devices */ 3307bdc064bSHans Petter Selasky while ((adev = TAILQ_FIRST(&hotplug_devs)) != NULL) { 3317bdc064bSHans Petter Selasky TAILQ_REMOVE(&hotplug_devs, adev, hotplug_entry); 3327bdc064bSHans Petter Selasky libusb_unref_device(adev); 3337bdc064bSHans Petter Selasky } 3347bdc064bSHans Petter Selasky } 3357bdc064bSHans Petter Selasky return (NULL); 3367bdc064bSHans Petter Selasky } 3377bdc064bSHans Petter Selasky 3387bdc064bSHans Petter Selasky int libusb_hotplug_register_callback(libusb_context *ctx, 3397bdc064bSHans Petter Selasky libusb_hotplug_event events, libusb_hotplug_flag flags, 3407bdc064bSHans Petter Selasky int vendor_id, int product_id, int dev_class, 3417bdc064bSHans Petter Selasky libusb_hotplug_callback_fn cb_fn, void *user_data, 3427bdc064bSHans Petter Selasky libusb_hotplug_callback_handle *phandle) 3437bdc064bSHans Petter Selasky { 3447bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle; 3457bdc064bSHans Petter Selasky struct libusb_device *adev; 3467bdc064bSHans Petter Selasky 3477bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 3487bdc064bSHans Petter Selasky 3499dc96d8bSBaptiste Daroussin if (ctx->usb_event_mode == usb_event_none) { 3509dc96d8bSBaptiste Daroussin HOTPLUG_LOCK(ctx); 3519dc96d8bSBaptiste Daroussin if (!netlink_init(ctx) && !devd_init(ctx)) 3529dc96d8bSBaptiste Daroussin ctx->usb_event_mode = usb_event_scan; 3539dc96d8bSBaptiste Daroussin HOTPLUG_UNLOCK(ctx); 3549dc96d8bSBaptiste Daroussin } 3559dc96d8bSBaptiste Daroussin 3567bdc064bSHans Petter Selasky if (ctx == NULL || cb_fn == NULL || events == 0 || 3577bdc064bSHans Petter Selasky vendor_id < -1 || vendor_id > 0xffff || 3587bdc064bSHans Petter Selasky product_id < -1 || product_id > 0xffff || 3597bdc064bSHans Petter Selasky dev_class < -1 || dev_class > 0xff) 3607bdc064bSHans Petter Selasky return (LIBUSB_ERROR_INVALID_PARAM); 3617bdc064bSHans Petter Selasky 3627bdc064bSHans Petter Selasky handle = malloc(sizeof(*handle)); 3637bdc064bSHans Petter Selasky if (handle == NULL) 3647bdc064bSHans Petter Selasky return (LIBUSB_ERROR_NO_MEM); 3657bdc064bSHans Petter Selasky 3667bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 3677bdc064bSHans Petter Selasky if (ctx->hotplug_handler == NO_THREAD) { 368a5b24a2bSHans Petter Selasky libusb_hotplug_enumerate(ctx, &ctx->hotplug_devs); 369a5b24a2bSHans Petter Selasky 3707bdc064bSHans Petter Selasky if (pthread_create(&ctx->hotplug_handler, NULL, 3717bdc064bSHans Petter Selasky &libusb_hotplug_scan, ctx) != 0) 3727bdc064bSHans Petter Selasky ctx->hotplug_handler = NO_THREAD; 3737bdc064bSHans Petter Selasky } 3747bdc064bSHans Petter Selasky handle->events = events; 3757bdc064bSHans Petter Selasky handle->vendor = vendor_id; 3767bdc064bSHans Petter Selasky handle->product = product_id; 3777bdc064bSHans Petter Selasky handle->devclass = dev_class; 3787bdc064bSHans Petter Selasky handle->fn = cb_fn; 3797bdc064bSHans Petter Selasky handle->user_data = user_data; 3807bdc064bSHans Petter Selasky 3817bdc064bSHans Petter Selasky if (flags & LIBUSB_HOTPLUG_ENUMERATE) { 3827bdc064bSHans Petter Selasky TAILQ_FOREACH(adev, &ctx->hotplug_devs, hotplug_entry) { 3837bdc064bSHans Petter Selasky if (libusb_hotplug_filter(ctx, handle, adev, 3847bdc064bSHans Petter Selasky LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) == 0) 3857bdc064bSHans Petter Selasky continue; 3867bdc064bSHans Petter Selasky free(handle); 3877bdc064bSHans Petter Selasky handle = NULL; 3887bdc064bSHans Petter Selasky break; 3897bdc064bSHans Petter Selasky } 3907bdc064bSHans Petter Selasky } 3917bdc064bSHans Petter Selasky if (handle != NULL) 3927bdc064bSHans Petter Selasky TAILQ_INSERT_TAIL(&ctx->hotplug_cbh, handle, entry); 3937bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 3947bdc064bSHans Petter Selasky 3957bdc064bSHans Petter Selasky if (phandle != NULL) 3967bdc064bSHans Petter Selasky *phandle = handle; 3977bdc064bSHans Petter Selasky return (LIBUSB_SUCCESS); 3987bdc064bSHans Petter Selasky } 3997bdc064bSHans Petter Selasky 4007bdc064bSHans Petter Selasky void libusb_hotplug_deregister_callback(libusb_context *ctx, 4017bdc064bSHans Petter Selasky libusb_hotplug_callback_handle handle) 4027bdc064bSHans Petter Selasky { 4037bdc064bSHans Petter Selasky ctx = GET_CONTEXT(ctx); 4047bdc064bSHans Petter Selasky 4057bdc064bSHans Petter Selasky if (ctx == NULL || handle == NULL) 4067bdc064bSHans Petter Selasky return; 4077bdc064bSHans Petter Selasky 4087bdc064bSHans Petter Selasky HOTPLUG_LOCK(ctx); 4097bdc064bSHans Petter Selasky TAILQ_REMOVE(&ctx->hotplug_cbh, handle, entry); 4107bdc064bSHans Petter Selasky HOTPLUG_UNLOCK(ctx); 4117bdc064bSHans Petter Selasky 4127bdc064bSHans Petter Selasky free(handle); 4137bdc064bSHans Petter Selasky } 414