X-Git-Url: http://47.100.26.94:8080/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjni%2Flibusb%2Flibusb%2Fos%2Fpoll_windows.c;fp=app%2Fsrc%2Fmain%2Fjni%2Flibusb%2Flibusb%2Fos%2Fpoll_windows.c;h=2a75c5e7e6824c974c2b24c85f4fb88e340b0a3e;hb=831cc09829bc6e18d8d0d8bb78063e89ea565ce9;hp=0000000000000000000000000000000000000000;hpb=061580c83656bf358b01a6b78fd22ae9bd497728;p=rtmpclient.git diff --git a/app/src/main/jni/libusb/libusb/os/poll_windows.c b/app/src/main/jni/libusb/libusb/os/poll_windows.c new file mode 100644 index 0000000..2a75c5e --- /dev/null +++ b/app/src/main/jni/libusb/libusb/os/poll_windows.c @@ -0,0 +1,726 @@ +/* + * poll_windows: poll compatibility wrapper for Windows + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * poll() and pipe() Windows compatibility layer for libusb 1.0 + * + * The way this layer works is by using OVERLAPPED with async I/O transfers, as + * OVERLAPPED have an associated event which is flagged for I/O completion. + * + * For USB pollable async I/O, you would typically: + * - obtain a Windows HANDLE to a file or device that has been opened in + * OVERLAPPED mode + * - call usbi_create_fd with this handle to obtain a custom fd. + * Note that if you need simultaneous R/W access, you need to call create_fd + * twice, once in RW_READ and once in RW_WRITE mode to obtain 2 separate + * pollable fds + * - leave the core functions call the poll routine and flag POLLIN/POLLOUT + * + * The pipe pollable synchronous I/O works using the overlapped event associated + * with a fake pipe. The read/write functions are only meant to be used in that + * context. + */ +#include +#include +#include + +#include "libusbi.h" + +// Uncomment to debug the polling layer +//#define DEBUG_POLL_WINDOWS +#if defined(DEBUG_POLL_WINDOWS) +#define poll_dbg usbi_dbg +#else +// MSVC++ < 2005 cannot use a variadic argument and non MSVC +// compilers produce warnings if parenthesis are ommitted. +#if defined(_MSC_VER) && (_MSC_VER < 1400) +#define poll_dbg +#else +#define poll_dbg(...) +#endif +#endif + +#if defined(_PREFAST_) +#pragma warning(disable:28719) +#endif + +#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) + +// public fd data +const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE}; +struct winfd poll_fd[MAX_FDS]; +// internal fd data +struct { + CRITICAL_SECTION mutex; // lock for fds + // Additional variables for XP CancelIoEx partial emulation + HANDLE original_handle; + DWORD thread_id; +} _poll_fd[MAX_FDS]; + +// globals +BOOLEAN is_polling_set = FALSE; +LONG pipe_number = 0; +static volatile LONG compat_spinlock = 0; + +#if !defined(_WIN32_WCE) +// CancelIoEx, available on Vista and later only, provides the ability to cancel +// a single transfer (OVERLAPPED) when used. As it may not be part of any of the +// platform headers, we hook into the Kernel32 system DLL directly to seek it. +static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL; +#define Use_Duplicate_Handles (pCancelIoEx == NULL) + +static inline void setup_cancel_io(void) +{ + HMODULE hKernel32 = GetModuleHandleA("KERNEL32"); + if (hKernel32 != NULL) { + pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) + GetProcAddress(hKernel32, "CancelIoEx"); + } + usbi_dbg("Will use CancelIo%s for I/O cancellation", + Use_Duplicate_Handles?"":"Ex"); +} + +static inline BOOL cancel_io(int _index) +{ + if ((_index < 0) || (_index >= MAX_FDS)) { + return FALSE; + } + + if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) + || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { + return TRUE; + } + if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { + // Cancel outstanding transfer via the specific callback + (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); + return TRUE; + } + if (pCancelIoEx != NULL) { + return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped); + } + if (_poll_fd[_index].thread_id == GetCurrentThreadId()) { + return CancelIo(poll_fd[_index].handle); + } + usbi_warn(NULL, "Unable to cancel I/O that was started from another thread"); + return FALSE; +} +#else +#define Use_Duplicate_Handles FALSE + +static __inline void setup_cancel_io() +{ + // No setup needed on WinCE +} + +static __inline BOOL cancel_io(int _index) +{ + if ((_index < 0) || (_index >= MAX_FDS)) { + return FALSE; + } + if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) + || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { + return TRUE; + } + if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { + // Cancel outstanding transfer via the specific callback + (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); + } + return TRUE; +} +#endif + +// Init +void init_polling(void) +{ + int i; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (!is_polling_set) { + setup_cancel_io(); + for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(overlapped->hEvent == NULL) { + free (overlapped); + return NULL; + } + return overlapped; +} + +static void free_overlapped(OVERLAPPED *overlapped) +{ + if (overlapped == NULL) + return; + + if ( (overlapped->hEvent != 0) + && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { + CloseHandle(overlapped->hEvent); + } + free(overlapped); +} + +void exit_polling(void) +{ + int i; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (is_polling_set) { + is_polling_set = FALSE; + + for (i=0; iInternal = STATUS_PENDING; + overlapped->InternalHigh = 0; + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + + // Use index as the unique fd number + poll_fd[i].fd = i; + // Read end of the "pipe" + filedes[0] = poll_fd[i].fd; + // We can use the same handle for both ends + filedes[1] = filedes[0]; + + poll_fd[i].handle = DUMMY_HANDLE; + poll_fd[i].overlapped = overlapped; + // There's no polling on the write end, so we just use READ for our needs + poll_fd[i].rw = RW_READ; + _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; + LeaveCriticalSection(&_poll_fd[i].mutex); + return 0; + } + } + free_overlapped(overlapped); + return -1; +} + +/* + * Create both an fd and an OVERLAPPED from an open Windows handle, so that + * it can be used with our polling function + * The handle MUST support overlapped transfers (usually requires CreateFile + * with FILE_FLAG_OVERLAPPED) + * Return a pollable file descriptor struct, or INVALID_WINFD on error + * + * Note that the fd returned by this function is a per-transfer fd, rather + * than a per-session fd and cannot be used for anything else but our + * custom functions (the fd itself points to the NUL: device) + * if you plan to do R/W on the same handle, you MUST create 2 fds: one for + * read and one for write. Using a single R/W fd is unsupported and will + * produce unexpected results + */ +struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn) +{ + int i; + struct winfd wfd = INVALID_WINFD; + OVERLAPPED* overlapped = NULL; + + CHECK_INIT_POLLING; + + if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { + return INVALID_WINFD; + } + + wfd.itransfer = itransfer; + wfd.cancel_fn = cancel_fn; + + if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) { + usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported.\n" + "If you want to poll for R/W simultaneously, create multiple fds from the same handle."); + return INVALID_WINFD; + } + if (access_mode == RW_READ) { + wfd.rw = RW_READ; + } else { + wfd.rw = RW_WRITE; + } + + overlapped = create_overlapped(); + if(overlapped == NULL) { + return INVALID_WINFD; + } + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + // Use index as the unique fd number + wfd.fd = i; + // Attempt to emulate some of the CancelIoEx behaviour on platforms + // that don't have it + if (Use_Duplicate_Handles) { + _poll_fd[i].thread_id = GetCurrentThreadId(); + if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), + &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + usbi_dbg("could not duplicate handle for CancelIo - using original one"); + wfd.handle = handle; + // Make sure we won't close the original handle on fd deletion then + _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; + } else { + _poll_fd[i].original_handle = handle; + } + } else { + wfd.handle = handle; + } + wfd.overlapped = overlapped; + memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); + return wfd; + } + } + free_overlapped(overlapped); + return INVALID_WINFD; +} + +static void _free_index(int _index) +{ + // Cancel any async IO (Don't care about the validity of our handles for this) + cancel_io(_index); + // close the duplicate handle (if we have an actual duplicate) + if (Use_Duplicate_Handles) { + if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) { + CloseHandle(poll_fd[_index].handle); + } + _poll_fd[_index].original_handle = INVALID_HANDLE_VALUE; + _poll_fd[_index].thread_id = 0; + } + free_overlapped(poll_fd[_index].overlapped); + poll_fd[_index] = INVALID_WINFD; +} + +/* + * Release a pollable file descriptor. + * + * Note that the associated Windows handle is not closed by this call + */ +void usbi_free_fd(struct winfd *wfd) +{ + int _index; + + CHECK_INIT_POLLING; + + _index = _fd_to_index_and_lock(wfd->fd); + if (_index < 0) { + return; + } + _free_index(_index); + *wfd = INVALID_WINFD; + LeaveCriticalSection(&_poll_fd[_index].mutex); +} + +/* + * The functions below perform various conversions between fd, handle and OVERLAPPED + */ +struct winfd fd_to_winfd(int fd) +{ + int i; + struct winfd wfd; + + CHECK_INIT_POLLING; + + if (fd < 0) + return INVALID_WINFD; + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + usbi_warn(NULL, "invalid fd"); + triggered = -1; + goto poll_exit; + } + + // IN or OUT must match our fd direction + if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) { + fds[i].revents |= POLLNVAL | POLLERR; + errno = EBADF; + usbi_warn(NULL, "attempted POLLIN on fd without READ access"); + LeaveCriticalSection(&_poll_fd[_index].mutex); + triggered = -1; + goto poll_exit; + } + + if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) { + fds[i].revents |= POLLNVAL | POLLERR; + errno = EBADF; + usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access"); + LeaveCriticalSection(&_poll_fd[_index].mutex); + triggered = -1; + goto poll_exit; + } + + // The following macro only works if overlapped I/O was reported pending + if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped)) + || (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) { + poll_dbg(" completed"); + // checks above should ensure this works: + fds[i].revents = fds[i].events; + triggered++; + } else { + handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent; + handle_to_index[nb_handles_to_wait_on] = i; + nb_handles_to_wait_on++; + } + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + + // If nothing was triggered, wait on all fds that require it + if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) { + if (timeout < 0) { + poll_dbg("starting infinite wait for %d handles...", (int)nb_handles_to_wait_on); + } else { + poll_dbg("starting %d ms wait for %d handles...", timeout, (int)nb_handles_to_wait_on); + } + ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on, + FALSE, (timeout<0)?INFINITE:(DWORD)timeout); + object_index = ret-WAIT_OBJECT_0; + if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { + poll_dbg(" completed after wait"); + i = handle_to_index[object_index]; + _index = _fd_to_index_and_lock(fds[i].fd); + fds[i].revents = fds[i].events; + triggered++; + if (_index >= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + } else if (ret == WAIT_TIMEOUT) { + poll_dbg(" timed out"); + triggered = 0; // 0 = timeout + } else { + errno = EIO; + triggered = -1; // error + } + } + +poll_exit: + if (handles_to_wait_on != NULL) { + free(handles_to_wait_on); + } + if (handle_to_index != NULL) { + free(handle_to_index); + } + return triggered; +} + +/* + * close a fake pipe fd + */ +int usbi_close(int fd) +{ + int _index; + int r = -1; + + CHECK_INIT_POLLING; + + _index = _fd_to_index_and_lock(fd); + + if (_index < 0) { + errno = EBADF; + } else { + free_overlapped(poll_fd[_index].overlapped); + poll_fd[_index] = INVALID_WINFD; + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + return r; +} + +/* + * synchronous write for fake "pipe" signaling + */ +ssize_t usbi_write(int fd, const void *buf, size_t count) +{ + int _index; + UNUSED(buf); + + CHECK_INIT_POLLING; + + if (count != sizeof(unsigned char)) { + usbi_err(NULL, "this function should only used for signaling"); + return -1; + } + + _index = _fd_to_index_and_lock(fd); + + if ( (_index < 0) || (poll_fd[_index].overlapped == NULL) ) { + errno = EBADF; + if (_index >= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + return -1; + } + + poll_dbg("set pipe event (fd = %d, thread = %08X)", _index, GetCurrentThreadId()); + SetEvent(poll_fd[_index].overlapped->hEvent); + poll_fd[_index].overlapped->Internal = STATUS_WAIT_0; + // If two threads write on the pipe at the same time, we need to + // process two separate reads => use the overlapped as a counter + poll_fd[_index].overlapped->InternalHigh++; + + LeaveCriticalSection(&_poll_fd[_index].mutex); + return sizeof(unsigned char); +} + +/* + * synchronous read for fake "pipe" signaling + */ +ssize_t usbi_read(int fd, void *buf, size_t count) +{ + int _index; + ssize_t r = -1; + UNUSED(buf); + + CHECK_INIT_POLLING; + + if (count != sizeof(unsigned char)) { + usbi_err(NULL, "this function should only used for signaling"); + return -1; + } + + _index = _fd_to_index_and_lock(fd); + + if (_index < 0) { + errno = EBADF; + return -1; + } + + if (WaitForSingleObject(poll_fd[_index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) { + usbi_warn(NULL, "waiting for event failed: %d", (int)GetLastError()); + errno = EIO; + goto out; + } + + poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, GetCurrentThreadId()); + poll_fd[_index].overlapped->InternalHigh--; + // Don't reset unless we don't have any more events to process + if (poll_fd[_index].overlapped->InternalHigh <= 0) { + ResetEvent(poll_fd[_index].overlapped->hEvent); + poll_fd[_index].overlapped->Internal = STATUS_PENDING; + } + + r = sizeof(unsigned char); + +out: + LeaveCriticalSection(&_poll_fd[_index].mutex); + return r; +}