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