/* * poll_windows: poll compatibility wrapper for Windows * Copyright (C) 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 _O_RDONLY and once in _O_WRONLY 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 #include #include // Uncomment to debug the polling layer //#define DEBUG_POLL_WINDOWS #if defined(DEBUG_POLL_WINDOWS) #define poll_dbg usbi_dbg #else // MSVC6 cannot use a variadic argument and non MSVC // compilers produce warnings if parenthesis are ommitted. #if defined(_MSC_VER) #define poll_dbg #else #define poll_dbg(...) #endif #endif #if defined(_PREFAST_) #pragma warning(disable:28719) #endif #if defined(__CYGWIN__) // cygwin produces a warning unless these prototypes are defined extern int _open(char* name, int flags); extern int _close(int fd); extern int _snprintf(char *buffer, size_t count, const char *format, ...); #define NUL_DEVICE "/dev/null" #else #define NUL_DEVICE "NUL" #endif #define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) // Workaround for MinGW-w64 multilib bug #if defined(_MSC_VER) || defined(_WIN64) #define INIT_INTERLOCKEDEXCHANGE #define pInterlockedExchange InterlockedExchange #else static LONG (WINAPI *pInterlockedExchange)(LONG volatile *, LONG) = NULL; #define INIT_INTERLOCKEDEXCHANGE if (pInterlockedExchange == NULL) { \ pInterlockedExchange = (LONG (WINAPI *)(LONG volatile *, LONG)) \ GetProcAddress(GetModuleHandle("KERNEL32"), "InterlockedExchange"); \ if (pInterlockedExchange == NULL) { \ usbi_err(NULL, "InterlockedExchange is unavailable"); \ return; \ } \ } #endif // public fd data const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, 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; #if defined(DYNAMIC_FDS) HANDLE fd_update = INVALID_HANDLE_VALUE; // event to notify poll of fd update HANDLE new_fd[MAX_FDS]; // overlapped event handles for fds created since last poll unsigned nb_new_fds = 0; // nb new fds created since last poll usbi_mutex_t new_fd_mutex; // mutex required for the above #endif LONG pipe_number = 0; static volatile LONG compat_spinlock = 0; // 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 CancelIoEx_Available (pCancelIoEx != NULL) __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 (CancelIoEx_Available) { 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; } // Init void init_polling(void) { int i; INIT_INTERLOCKEDEXCHANGE; while (pInterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { SleepEx(0, TRUE); } if (!is_polling_set) { pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) GetProcAddress(GetModuleHandleA("KERNEL32"), "CancelIoEx"); usbi_dbg("Will use CancelIo%s for I/O cancellation", CancelIoEx_Available?"Ex":""); for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if(overlapped->hEvent == NULL) { free (overlapped); return NULL; } return overlapped; } void free_overlapped(OVERLAPPED *overlapped) { if (overlapped == NULL) return; if ( (overlapped->hEvent != 0) && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { CloseHandle(overlapped->hEvent); } free(overlapped); } void reset_overlapped(OVERLAPPED *overlapped) { HANDLE event_handle; if (overlapped == NULL) return; event_handle = overlapped->hEvent; if (event_handle != NULL) { ResetEvent(event_handle); } memset(overlapped, 0, sizeof(OVERLAPPED)); overlapped->hEvent = event_handle; } void exit_polling(void) { int i; INIT_INTERLOCKEDEXCHANGE; while (pInterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { SleepEx(0, TRUE); } if (is_polling_set) { is_polling_set = FALSE; for (i=0; i 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { _close(poll_fd[i].fd); } free_overlapped(poll_fd[i].overlapped); if (!CancelIoEx_Available) { // Close duplicate handle if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) { CloseHandle(poll_fd[i].handle); } } poll_fd[i] = INVALID_WINFD; #if defined(DYNAMIC_FDS) usbi_mutex_destroy(&new_fd_mutex); CloseHandle(fd_update); fd_update = INVALID_HANDLE_VALUE; #endif LeaveCriticalSection(&_poll_fd[i].mutex); DeleteCriticalSection(&_poll_fd[i].mutex); } } compat_spinlock = 0; } /* * Create a fake pipe. * As libusb only uses pipes for signaling, all we need from a pipe is an * event. To that extent, we create a single wfd and overlapped as a means * to access that event. */ int usbi_pipe(int filedes[2]) { int i; OVERLAPPED* overlapped; CHECK_INIT_POLLING; overlapped = calloc(1, sizeof(OVERLAPPED)); if (overlapped == NULL) { return -1; } // The overlapped must have status pending for signaling to work in poll overlapped->Internal = STATUS_PENDING; overlapped->InternalHigh = 0; // Read end of the "pipe" filedes[0] = _open(NUL_DEVICE, _O_WRONLY); if (filedes[0] < 0) { usbi_err(NULL, "could not create pipe: errno %d", errno); goto out1; } // We can use the same handle for both ends filedes[1] = filedes[0]; poll_dbg("pipe filedes = %d", filedes[0]); // Note: manual reset must be true (second param) as the reset occurs in read overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if(!overlapped->hEvent) { goto out2; } for (i=0; i= 0) { LeaveCriticalSection(&_poll_fd[i].mutex); continue; } poll_fd[i].fd = 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; } } CloseHandle(overlapped->hEvent); out2: _close(filedes[0]); out1: free(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) { int i, fd; struct winfd wfd = INVALID_WINFD; OVERLAPPED* overlapped = NULL; CHECK_INIT_POLLING; if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { return INVALID_WINFD; } if ((access_mode != _O_RDONLY) && (access_mode != _O_WRONLY)) { usbi_warn(NULL, "only one of _O_RDONLY or _O_WRONLY 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 == _O_RDONLY) { wfd.rw = RW_READ; } else { wfd.rw = RW_WRITE; } // Ensure that we get a non system conflicting unique fd, using // the same fd attribution system as the pipe ends fd = _open(NUL_DEVICE, _O_WRONLY); if (fd < 0) { return INVALID_WINFD; } overlapped = create_overlapped(); if(overlapped == NULL) { _close(fd); return INVALID_WINFD; } for (i=0; i= 0) { LeaveCriticalSection(&_poll_fd[i].mutex); continue; } wfd.fd = fd; // Attempt to emulate some of the CancelIoEx behaviour on platforms // that don't have it if (!CancelIoEx_Available) { _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); #if defined(DYNAMIC_FDS) usbi_mutex_lock(&new_fd_mutex); new_fd[nb_new_fds++] = overlapped->hEvent; usbi_mutex_unlock(&new_fd_mutex); // Notify poll that fds have been updated SetEvent(fd_update); #endif return wfd; } } free_overlapped(overlapped); _close(fd); return INVALID_WINFD; } void _free_index(int _index) { // Cancel any async IO (Don't care about the validity of our handles for this) cancel_io(_index); // close fake handle for devices if ( (poll_fd[_index].handle != INVALID_HANDLE_VALUE) && (poll_fd[_index].handle != 0) && (GetFileType(poll_fd[_index].handle) == FILE_TYPE_UNKNOWN) ) { _close(poll_fd[_index].fd); } // close the duplicate handle (if we have an actual duplicate) if (!CancelIoEx_Available) { 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(int fd) { int _index; CHECK_INIT_POLLING; _index = _fd_to_index_and_lock(fd); if (_index < 0) { return; } _free_index(_index); 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; #if defined(DYNAMIC_FDS) // If this fd from the poll set is also part of the new_fd event handle table, remove it usbi_mutex_lock(&new_fd_mutex); for (j=0; j= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { #if defined(DYNAMIC_FDS) if ((DWORD)object_index >= (nb_handles_to_wait_on-nb_extra_handles)) { // Detected fd update => flag a poll interruption if ((DWORD)object_index == (nb_handles_to_wait_on-1)) poll_dbg(" dynamic_fds: fd_update event"); else poll_dbg(" dynamic_fds: new fd I/O event"); errno = EINTR; triggered = -1; goto poll_exit; } #endif 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); } #if defined(DYNAMIC_FDS) usbi_mutex_lock(&new_fd_mutex); nb_new_fds = 0; usbi_mutex_unlock(&new_fd_mutex); #endif 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 { if (poll_fd[_index].overlapped != NULL) { // Must be a different event for each end of the pipe CloseHandle(poll_fd[_index].overlapped->hEvent); free(poll_fd[_index].overlapped); } r = _close(poll_fd[_index].fd); if (r != 0) { errno = EIO; } 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; 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; 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; }