Blame libusb/os/poll_windows.c

Packit Service b0a153
/*
Packit Service b0a153
 * poll_windows: poll compatibility wrapper for Windows
Packit Service b0a153
 * Copyright © 2017 Chris Dickens <christopher.a.dickens@gmail.com>
Packit Service b0a153
 *
Packit Service b0a153
 * This library is free software; you can redistribute it and/or
Packit Service b0a153
 * modify it under the terms of the GNU Lesser General Public
Packit Service b0a153
 * License as published by the Free Software Foundation; either
Packit Service b0a153
 * version 2.1 of the License, or (at your option) any later version.
Packit Service b0a153
 *
Packit Service b0a153
 * This library is distributed in the hope that it will be useful,
Packit Service b0a153
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service b0a153
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service b0a153
 * Lesser General Public License for more details.
Packit Service b0a153
 *
Packit Service b0a153
 * You should have received a copy of the GNU Lesser General Public
Packit Service b0a153
 * License along with this library; if not, write to the Free Software
Packit Service b0a153
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Packit Service b0a153
 *
Packit Service b0a153
 */
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
 * poll() and pipe() Windows compatibility layer for libusb 1.0
Packit Service b0a153
 *
Packit Service b0a153
 * The way this layer works is by using OVERLAPPED with async I/O transfers, as
Packit Service b0a153
 * OVERLAPPED have an associated event which is flagged for I/O completion.
Packit Service b0a153
 *
Packit Service b0a153
 * For USB pollable async I/O, you would typically:
Packit Service b0a153
 * - obtain a Windows HANDLE to a file or device that has been opened in
Packit Service b0a153
 *   OVERLAPPED mode
Packit Service b0a153
 * - call usbi_create_fd with this handle to obtain a custom fd.
Packit Service b0a153
 * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
Packit Service b0a153
 *
Packit Service b0a153
 * The pipe pollable synchronous I/O works using the overlapped event associated
Packit Service b0a153
 * with a fake pipe. The read/write functions are only meant to be used in that
Packit Service b0a153
 * context.
Packit Service b0a153
 */
Packit Service b0a153
#include <config.h>
Packit Service b0a153
Packit Service b0a153
#include <assert.h>
Packit Service b0a153
#include <errno.h>
Packit Service b0a153
#include <stdlib.h>
Packit Service b0a153
Packit Service b0a153
#include "libusbi.h"
Packit Service b0a153
#include "windows_common.h"
Packit Service b0a153
Packit Service b0a153
// public fd data
Packit Service b0a153
const struct winfd INVALID_WINFD = { -1, NULL };
Packit Service b0a153
Packit Service b0a153
// private data
Packit Service b0a153
struct file_descriptor {
Packit Service b0a153
	enum fd_type { FD_TYPE_PIPE, FD_TYPE_TRANSFER } type;
Packit Service b0a153
	OVERLAPPED overlapped;
Packit Service b0a153
	int refcount;
Packit Service b0a153
};
Packit Service b0a153
Packit Service b0a153
static usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER;
Packit Service b0a153
Packit Service b0a153
static struct file_descriptor **fd_table;
Packit Service b0a153
static size_t fd_count;
Packit Service b0a153
static size_t fd_size;
Packit Service b0a153
#define INC_FDS_EACH 256
Packit Service b0a153
Packit Service b0a153
static void usbi_dec_fd_table()
Packit Service b0a153
{
Packit Service b0a153
	fd_count--;
Packit Service b0a153
	if (fd_count == 0) {
Packit Service b0a153
		free(fd_table);
Packit Service b0a153
		fd_size = 0;
Packit Service b0a153
		fd_table = NULL;
Packit Service b0a153
	}
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
static void smart_realloc_fd_table_space(int inc)
Packit Service b0a153
{
Packit Service b0a153
	if (fd_table == NULL || fd_count + inc > fd_size) {
Packit Service b0a153
		struct file_descriptor **p = (struct file_descriptor **)realloc(fd_table, (fd_size + INC_FDS_EACH) * sizeof(struct file_descriptor *));
Packit Service b0a153
		if (p != NULL) {
Packit Service b0a153
			memset(p + fd_size, 0, INC_FDS_EACH * sizeof(struct file_descriptor *));
Packit Service b0a153
			fd_size += INC_FDS_EACH;
Packit Service b0a153
			fd_table = p;
Packit Service b0a153
		}
Packit Service b0a153
	}
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
static struct file_descriptor *create_fd(enum fd_type type)
Packit Service b0a153
{
Packit Service b0a153
	struct file_descriptor *fd = calloc(1, sizeof(*fd));
Packit Service b0a153
	if (fd == NULL)
Packit Service b0a153
		return NULL;
Packit Service b0a153
	fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Packit Service b0a153
	if (fd->overlapped.hEvent == NULL) {
Packit Service b0a153
		free(fd);
Packit Service b0a153
		return NULL;
Packit Service b0a153
	}
Packit Service b0a153
	fd->type = type;
Packit Service b0a153
	fd->refcount = 1;
Packit Service b0a153
	return fd;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
static void free_fd(struct file_descriptor *fd)
Packit Service b0a153
{
Packit Service b0a153
	CloseHandle(fd->overlapped.hEvent);
Packit Service b0a153
	free(fd);
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
 * Create both an fd and an OVERLAPPED, so that it can be used with our
Packit Service b0a153
 * polling function
Packit Service b0a153
 * The handle MUST support overlapped transfers (usually requires CreateFile
Packit Service b0a153
 * with FILE_FLAG_OVERLAPPED)
Packit Service b0a153
 * Return a pollable file descriptor struct, or INVALID_WINFD on error
Packit Service b0a153
 *
Packit Service b0a153
 * Note that the fd returned by this function is a per-transfer fd, rather
Packit Service b0a153
 * than a per-session fd and cannot be used for anything else but our
Packit Service b0a153
 * custom functions.
Packit Service b0a153
 * if you plan to do R/W on the same handle, you MUST create 2 fds: one for
Packit Service b0a153
 * read and one for write. Using a single R/W fd is unsupported and will
Packit Service b0a153
 * produce unexpected results
Packit Service b0a153
 */
Packit Service b0a153
struct winfd usbi_create_fd(void)
Packit Service b0a153
{
Packit Service b0a153
	struct file_descriptor *fd;
Packit Service b0a153
	struct winfd wfd;
Packit Service b0a153
Packit Service b0a153
	fd = create_fd(FD_TYPE_TRANSFER);
Packit Service b0a153
	if (fd == NULL)
Packit Service b0a153
		return INVALID_WINFD;
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	smart_realloc_fd_table_space(1);
Packit Service b0a153
Packit Service b0a153
	for (wfd.fd = 0; wfd.fd < fd_size; wfd.fd++) {
Packit Service b0a153
		if (fd_table[wfd.fd] != NULL)
Packit Service b0a153
			continue;
Packit Service b0a153
		fd_table[wfd.fd] = fd;
Packit Service b0a153
		fd_count++;
Packit Service b0a153
		break;
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	if (wfd.fd == fd_size) {
Packit Service b0a153
		free_fd(fd);
Packit Service b0a153
		return INVALID_WINFD;
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	wfd.overlapped = &fd->overlapped;
Packit Service b0a153
Packit Service b0a153
	return wfd;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
void usbi_inc_fds_ref(struct pollfd *fds, unsigned int nfds)
Packit Service b0a153
{
Packit Service b0a153
	int n;
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	for (n = 0; n < nfds; ++n) {
Packit Service b0a153
		fd_table[fds[n].fd]->refcount++;
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
void usbi_dec_fds_ref(struct pollfd *fds, unsigned int nfds)
Packit Service b0a153
{
Packit Service b0a153
	int n;
Packit Service b0a153
	struct file_descriptor *fd;
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	for (n = 0; n < nfds; ++n) {
Packit Service b0a153
		fd = fd_table[fds[n].fd];
Packit Service b0a153
		fd->refcount--;
Packit Service b0a153
		//FD_TYPE_PIPE map fd to two _fd
Packit Service b0a153
		if (fd->refcount == 0 || (fd->refcount == 1 && fd->type == FD_TYPE_PIPE))
Packit Service b0a153
		{
Packit Service b0a153
			if (fd->type == FD_TYPE_PIPE) {
Packit Service b0a153
				// InternalHigh is our reference count
Packit Service b0a153
				fd->overlapped.InternalHigh--;
Packit Service b0a153
				if (fd->overlapped.InternalHigh == 0)
Packit Service b0a153
					free_fd(fd);
Packit Service b0a153
			}
Packit Service b0a153
			else {
Packit Service b0a153
				free_fd(fd);
Packit Service b0a153
			}
Packit Service b0a153
			fd_table[fds[n].fd] = NULL;
Packit Service b0a153
			usbi_dec_fd_table();
Packit Service b0a153
		}
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
Packit Service b0a153
static int check_pollfds(struct pollfd *fds, unsigned int nfds,
Packit Service b0a153
	HANDLE *wait_handles, DWORD *nb_wait_handles)
Packit Service b0a153
{
Packit Service b0a153
	struct file_descriptor *fd;
Packit Service b0a153
	unsigned int n;
Packit Service b0a153
	int nready = 0;
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	for (n = 0; n < nfds; ++n) {
Packit Service b0a153
		fds[n].revents = 0;
Packit Service b0a153
Packit Service b0a153
		// Keep it simple - only allow either POLLIN *or* POLLOUT
Packit Service b0a153
		assert((fds[n].events == POLLIN) || (fds[n].events == POLLOUT));
Packit Service b0a153
		if ((fds[n].events != POLLIN) && (fds[n].events != POLLOUT)) {
Packit Service b0a153
			fds[n].revents = POLLNVAL;
Packit Service b0a153
			nready++;
Packit Service b0a153
			continue;
Packit Service b0a153
		}
Packit Service b0a153
Packit Service b0a153
		if ((fds[n].fd >= 0) && (fds[n].fd < fd_size))
Packit Service b0a153
			fd = fd_table[fds[n].fd];
Packit Service b0a153
		else
Packit Service b0a153
			fd = NULL;
Packit Service b0a153
Packit Service b0a153
		assert(fd != NULL);
Packit Service b0a153
		if (fd == NULL) {
Packit Service b0a153
			fds[n].revents = POLLNVAL;
Packit Service b0a153
			nready++;
Packit Service b0a153
			continue;
Packit Service b0a153
		}
Packit Service b0a153
Packit Service b0a153
		if (HasOverlappedIoCompleted(&fd->overlapped)
Packit Service b0a153
				&& (WaitForSingleObject(fd->overlapped.hEvent, 0) == WAIT_OBJECT_0)) {
Packit Service b0a153
			fds[n].revents = fds[n].events;
Packit Service b0a153
			nready++;
Packit Service b0a153
		} else if (wait_handles != NULL) {
Packit Service b0a153
			if (*nb_wait_handles == MAXIMUM_WAIT_OBJECTS) {
Packit Service b0a153
				usbi_warn(NULL, "too many HANDLEs to wait on");
Packit Service b0a153
				continue;
Packit Service b0a153
			}
Packit Service b0a153
			wait_handles[*nb_wait_handles] = fd->overlapped.hEvent;
Packit Service b0a153
			(*nb_wait_handles)++;
Packit Service b0a153
		}
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	return nready;
Packit Service b0a153
}
Packit Service b0a153
/*
Packit Service b0a153
 * POSIX poll equivalent, using Windows OVERLAPPED
Packit Service b0a153
 * Currently, this function only accepts one of POLLIN or POLLOUT per fd
Packit Service b0a153
 * (but you can create multiple fds from the same handle for read and write)
Packit Service b0a153
 */
Packit Service b0a153
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout)
Packit Service b0a153
{
Packit Service b0a153
	HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
Packit Service b0a153
	DWORD nb_wait_handles = 0;
Packit Service b0a153
	DWORD ret;
Packit Service b0a153
	int nready;
Packit Service b0a153
Packit Service b0a153
	nready = check_pollfds(fds, nfds, wait_handles, &nb_wait_handles);
Packit Service b0a153
Packit Service b0a153
	// If nothing was triggered, wait on all fds that require it
Packit Service b0a153
	if ((nready == 0) && (nb_wait_handles != 0) && (timeout != 0)) {
Packit Service b0a153
		ret = WaitForMultipleObjects(nb_wait_handles, wait_handles,
Packit Service b0a153
			FALSE, (timeout < 0) ? INFINITE : (DWORD)timeout);
Packit Service b0a153
		if (ret < (WAIT_OBJECT_0 + nb_wait_handles)) {
Packit Service b0a153
			nready = check_pollfds(fds, nfds, NULL, NULL);
Packit Service b0a153
		} else if (ret != WAIT_TIMEOUT) {
Packit Service b0a153
			if (ret == WAIT_FAILED)
Packit Service b0a153
				usbi_err(NULL, "WaitForMultipleObjects failed: %u", (unsigned int)GetLastError());
Packit Service b0a153
			nready = -1;
Packit Service b0a153
		}
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	return nready;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
 * close a fake file descriptor
Packit Service b0a153
 */
Packit Service b0a153
int usbi_close(int _fd)
Packit Service b0a153
{
Packit Service b0a153
	struct file_descriptor *fd;
Packit Service b0a153
Packit Service b0a153
	if (_fd < 0 || _fd >= fd_size)
Packit Service b0a153
		goto err_badfd;
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	fd = fd_table[_fd];
Packit Service b0a153
	fd->refcount--;
Packit Service b0a153
	//FD_TYPE_PIPE map fd to two _fd
Packit Service b0a153
	if(fd->refcount==0 || (fd->refcount == 1 && fd->type == FD_TYPE_PIPE))
Packit Service b0a153
	{	fd_table[_fd] = NULL;
Packit Service b0a153
		usbi_dec_fd_table();
Packit Service b0a153
Packit Service b0a153
		if (fd->type == FD_TYPE_PIPE) {
Packit Service b0a153
			// InternalHigh is our reference count
Packit Service b0a153
			fd->overlapped.InternalHigh--;
Packit Service b0a153
			if (fd->overlapped.InternalHigh == 0)
Packit Service b0a153
				free_fd(fd);
Packit Service b0a153
		}
Packit Service b0a153
		else {
Packit Service b0a153
			free_fd(fd);
Packit Service b0a153
		}
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	if (fd == NULL)
Packit Service b0a153
		goto err_badfd;
Packit Service b0a153
Packit Service b0a153
	return 0;
Packit Service b0a153
Packit Service b0a153
err_badfd:
Packit Service b0a153
	errno = EBADF;
Packit Service b0a153
	return -1;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
* Create a fake pipe.
Packit Service b0a153
* As libusb only uses pipes for signaling, all we need from a pipe is an
Packit Service b0a153
* event. To that extent, we create a single wfd and overlapped as a means
Packit Service b0a153
* to access that event.
Packit Service b0a153
*/
Packit Service b0a153
int usbi_pipe(int filedes[2])
Packit Service b0a153
{
Packit Service b0a153
	struct file_descriptor *fd;
Packit Service b0a153
	int r_fd = -1, w_fd = -1;
Packit Service b0a153
	int i;
Packit Service b0a153
Packit Service b0a153
	fd = create_fd(FD_TYPE_PIPE);
Packit Service b0a153
	if (fd == NULL) {
Packit Service b0a153
		errno = ENOMEM;
Packit Service b0a153
		return -1;
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	// Use InternalHigh as a reference count
Packit Service b0a153
	fd->overlapped.Internal = STATUS_PENDING;
Packit Service b0a153
	fd->overlapped.InternalHigh = 2;
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	do {
Packit Service b0a153
		smart_realloc_fd_table_space(2);
Packit Service b0a153
Packit Service b0a153
		for (i = 0; i < fd_size; i++) {
Packit Service b0a153
			if (fd_table[i] != NULL)
Packit Service b0a153
				continue;
Packit Service b0a153
			if (r_fd == -1) {
Packit Service b0a153
				r_fd = i;
Packit Service b0a153
			} else if (w_fd == -1) {
Packit Service b0a153
				w_fd = i;
Packit Service b0a153
				break;
Packit Service b0a153
			}
Packit Service b0a153
		}
Packit Service b0a153
Packit Service b0a153
		if (i == fd_size)
Packit Service b0a153
			break;
Packit Service b0a153
Packit Service b0a153
		fd_table[r_fd] = fd;
Packit Service b0a153
		fd_table[w_fd] = fd;
Packit Service b0a153
Packit Service b0a153
		fd->refcount++; //this fd reference twice for r and w.
Packit Service b0a153
Packit Service b0a153
		fd_count += 2;
Packit Service b0a153
Packit Service b0a153
	} while (0);
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	if (i == fd_size) {
Packit Service b0a153
		free_fd(fd);
Packit Service b0a153
		errno = EMFILE;
Packit Service b0a153
		return -1;
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	filedes[0] = r_fd;
Packit Service b0a153
	filedes[1] = w_fd;
Packit Service b0a153
Packit Service b0a153
	return 0;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
 * synchronous write for fake "pipe" signaling
Packit Service b0a153
 */
Packit Service b0a153
ssize_t usbi_write(int fd, const void *buf, size_t count)
Packit Service b0a153
{
Packit Service b0a153
	int error = EBADF;
Packit Service b0a153
Packit Service b0a153
	UNUSED(buf);
Packit Service b0a153
Packit Service b0a153
	if (fd < 0 || fd >= fd_size)
Packit Service b0a153
		goto err_out;
Packit Service b0a153
Packit Service b0a153
	if (count != sizeof(unsigned char)) {
Packit Service b0a153
		usbi_err(NULL, "this function should only used for signaling");
Packit Service b0a153
		error = EINVAL;
Packit Service b0a153
		goto err_out;
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
Packit Service b0a153
		assert(fd_table[fd]->overlapped.Internal == STATUS_PENDING);
Packit Service b0a153
		assert(fd_table[fd]->overlapped.InternalHigh == 2);
Packit Service b0a153
		fd_table[fd]->overlapped.Internal = STATUS_WAIT_0;
Packit Service b0a153
		SetEvent(fd_table[fd]->overlapped.hEvent);
Packit Service b0a153
		error = 0;
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	if (error)
Packit Service b0a153
		goto err_out;
Packit Service b0a153
Packit Service b0a153
	return sizeof(unsigned char);
Packit Service b0a153
Packit Service b0a153
err_out:
Packit Service b0a153
	errno = error;
Packit Service b0a153
	return -1;
Packit Service b0a153
}
Packit Service b0a153
Packit Service b0a153
/*
Packit Service b0a153
 * synchronous read for fake "pipe" signaling
Packit Service b0a153
 */
Packit Service b0a153
ssize_t usbi_read(int fd, void *buf, size_t count)
Packit Service b0a153
{
Packit Service b0a153
	int error = EBADF;
Packit Service b0a153
Packit Service b0a153
	UNUSED(buf);
Packit Service b0a153
Packit Service b0a153
	if (fd < 0 || fd >= fd_size)
Packit Service b0a153
		goto err_out;
Packit Service b0a153
Packit Service b0a153
	if (count != sizeof(unsigned char)) {
Packit Service b0a153
		usbi_err(NULL, "this function should only used for signaling");
Packit Service b0a153
		error = EINVAL;
Packit Service b0a153
		goto err_out;
Packit Service b0a153
	}
Packit Service b0a153
Packit Service b0a153
	usbi_mutex_static_lock(&fd_table_lock);
Packit Service b0a153
	if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
Packit Service b0a153
		assert(fd_table[fd]->overlapped.Internal == STATUS_WAIT_0);
Packit Service b0a153
		assert(fd_table[fd]->overlapped.InternalHigh == 2);
Packit Service b0a153
		fd_table[fd]->overlapped.Internal = STATUS_PENDING;
Packit Service b0a153
		ResetEvent(fd_table[fd]->overlapped.hEvent);
Packit Service b0a153
		error = 0;
Packit Service b0a153
	}
Packit Service b0a153
	usbi_mutex_static_unlock(&fd_table_lock);
Packit Service b0a153
Packit Service b0a153
	if (error)
Packit Service b0a153
		goto err_out;
Packit Service b0a153
Packit Service b0a153
	return sizeof(unsigned char);
Packit Service b0a153
Packit Service b0a153
err_out:
Packit Service b0a153
	errno = error;
Packit Service b0a153
	return -1;
Packit Service b0a153
}