summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToby Gray <toby.gray@realvnc.com>2013-01-16 02:07:29 +0000
committerToby Gray <toby.gray@realvnc.com>2013-01-23 00:40:18 +0000
commit244271931a782028fd9bc1cdcdb12200f65cf8d9 (patch)
treeeea17ea7e2e4c8638b26aa5a8984ef1b272a1566
parent790ffc78b008a03c95d10899f53997b504f55c72 (diff)
downloadlibusb-244271931a782028fd9bc1cdcdb12200f65cf8d9.tar.gz
WinCE: Add support for WinCE (sources)
-rw-r--r--examples/listdevs.c2
-rw-r--r--examples/xusb.c2
-rw-r--r--libusb/core.c12
-rw-r--r--libusb/libusb-1.0.rc2
-rw-r--r--libusb/libusb.h10
-rw-r--r--libusb/libusbi.h5
-rw-r--r--libusb/os/wince_usb.c1013
-rw-r--r--libusb/os/wince_usb.h131
-rw-r--r--libusb/version_nano.h2
-rw-r--r--msvc/config.h10
-rw-r--r--msvc/errno.h100
-rw-r--r--msvc/missing.c80
-rw-r--r--msvc/missing.h29
-rw-r--r--tests/stress.c4
-rw-r--r--tests/testlib.c16
15 files changed, 1409 insertions, 9 deletions
diff --git a/examples/listdevs.c b/examples/listdevs.c
index 9f7e04e..8538bfa 100644
--- a/examples/listdevs.c
+++ b/examples/listdevs.c
@@ -18,7 +18,9 @@
*/
#include <stdio.h>
+#if !defined(_WIN32_WCE)
#include <sys/types.h>
+#endif
#include "libusb.h"
diff --git a/examples/xusb.c b/examples/xusb.c
index 1117ace..707b58f 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -33,7 +33,7 @@
#define msleep(msecs) usleep(1000*msecs)
#endif
-#if !defined(_MSC_VER) || _MSC_VER<=1200
+#if !defined(_MSC_VER) || _MSC_VER<=1200 || defined(_WIN32_WCE)
#define sscanf_s sscanf
#endif
diff --git a/libusb/core.c b/libusb/core.c
index 77e21bf..5bface2 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -34,6 +34,10 @@
#include <sys/time.h>
#endif
+#if defined(OS_WINCE)
+#include "missing.h" // getenv()
+#endif
+
#include "libusbi.h"
#if defined(OS_LINUX)
@@ -44,6 +48,8 @@ const struct usbi_os_backend * const usbi_backend = &darwin_backend;
const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
#elif defined(OS_WINDOWS)
const struct usbi_os_backend * const usbi_backend = &windows_backend;
+#elif defined(OS_WINCE)
+const struct usbi_os_backend * const usbi_backend = &wince_backend;
#else
#error "Unsupported OS"
#endif
@@ -1789,7 +1795,13 @@ int usbi_gettimeofday(struct timeval *tp, void *tzp)
UNUSED(tzp);
if(tp) {
+#if defined(OS_WINCE)
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &_now.ft);
+#else
GetSystemTimeAsFileTime (&_now.ft);
+#endif
tp->tv_usec=(long)((_now.ns100 / 10) % 1000000 );
tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000);
}
diff --git a/libusb/libusb-1.0.rc b/libusb/libusb-1.0.rc
index 86cb853..ae49757 100644
--- a/libusb/libusb-1.0.rc
+++ b/libusb/libusb-1.0.rc
@@ -5,7 +5,9 @@
* The information can then be queried using standard APIs and can also be
* viewed with utilities such as Windows Explorer.
*/
+#ifndef _WIN32_WCE
#include "winresrc.h"
+#endif
#include "version.h"
#ifndef LIBUSB_VERSIONSTRING
diff --git a/libusb/libusb.h b/libusb/libusb.h
index da94c9b..c3b2f67 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -49,7 +49,9 @@ typedef unsigned __int32 uint32_t;
#include <stdint.h>
#endif
+#if !defined(_WIN32_WCE)
#include <sys/types.h>
+#endif
#include <time.h>
#include <limits.h>
@@ -62,11 +64,15 @@ typedef unsigned __int32 uint32_t;
* libusb_config_descriptor has an 'interface' member
* As this can be problematic if you include windows.h after libusb.h
* in your sources, we force windows.h to be included first. */
-#if defined(_WIN32) || defined(__CYGWIN__)
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
#include <windows.h>
#if defined(interface)
#undef interface
#endif
+#if defined(_WIN32_WCE)
+// Needed for "struct timeval" definition
+#include <winsock2.h>
+#endif
#endif
/** \def LIBUSB_CALL
@@ -101,7 +107,7 @@ typedef unsigned __int32 uint32_t;
* return type, before the function name. See internal documentation for
* API_EXPORTED.
*/
-#if defined(_WIN32) || defined(__CYGWIN__)
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
#define LIBUSB_CALL WINAPI
#else
#define LIBUSB_CALL
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 39e4c79..ac7f71c 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -195,7 +195,7 @@ static inline void usbi_dbg(const char *format, ...)
/* Internal abstraction for thread synchronization */
#if defined(THREADS_POSIX)
#include "os/threads_posix.h"
-#elif defined(OS_WINDOWS)
+#elif defined(OS_WINDOWS) || defined(OS_WINCE)
#include <os/threads_windows.h>
#endif
@@ -396,7 +396,7 @@ int usbi_get_config_index_by_value(struct libusb_device *dev,
#include <os/poll_windows.h>
#endif
-#if defined(OS_WINDOWS) && !defined(__GCC__)
+#if (defined(OS_WINDOWS) || defined(OS_WINCE)) && !defined(__GCC__)
#undef HAVE_GETTIMEOFDAY
int usbi_gettimeofday(struct timeval *tp, void *tzp);
#define LIBUSB_GETTIMEOFDAY_WIN32
@@ -905,5 +905,6 @@ extern const struct usbi_os_backend linux_usbfs_backend;
extern const struct usbi_os_backend darwin_backend;
extern const struct usbi_os_backend openbsd_backend;
extern const struct usbi_os_backend windows_backend;
+extern const struct usbi_os_backend wince_backend;
#endif
diff --git a/libusb/os/wince_usb.c b/libusb/os/wince_usb.c
new file mode 100644
index 0000000..8f5bb92
--- /dev/null
+++ b/libusb/os/wince_usb.c
@@ -0,0 +1,1013 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright (C) 2011-2012 RealVNC Ltd.
+ * Large portions taken from Windows backend, which is
+ * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * 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
+ */
+
+#include <libusbi.h>
+
+#include <stdint.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "wince_usb.h"
+
+// Forward declares
+static int wince_clock_gettime(int clk_id, struct timespec *tp);
+unsigned __stdcall wince_clock_gettime_threaded(void* param);
+
+// Global variables
+uint64_t hires_frequency, hires_ticks_to_ps;
+int errno;
+const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime
+enum windows_version windows_version = WINDOWS_CE;
+static int concurrent_usage = -1;
+// Timer thread
+// NB: index 0 is for monotonic and 1 is for the thread exit event
+HANDLE timer_thread = NULL;
+HANDLE timer_mutex = NULL;
+struct timespec timer_tp;
+volatile LONG request_count[2] = {0, 1}; // last one must be > 0
+HANDLE timer_request[2] = { NULL, NULL };
+HANDLE timer_response = NULL;
+HANDLE driver_handle = INVALID_HANDLE_VALUE;
+
+/*
+ * Converts a windows error to human readable string
+ * uses retval as errorcode, or, if 0, use GetLastError()
+ */
+#if defined(ENABLE_LOGGING)
+static char* windows_error_str(uint32_t retval)
+{
+ static TCHAR wErr_string[ERR_BUFFER_SIZE];
+ static char err_string[ERR_BUFFER_SIZE];
+
+ DWORD size;
+ size_t i;
+ uint32_t error_code, format_error;
+
+ error_code = retval?retval:GetLastError();
+
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE, _T("[%d] "), error_code);
+
+ size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &wErr_string[safe_tcslen(wErr_string)],
+ ERR_BUFFER_SIZE - (DWORD)safe_tcslen(wErr_string), NULL);
+ if (size == 0) {
+ format_error = GetLastError();
+ if (format_error)
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE,
+ _T("Windows error code %u (FormatMessage error code %u)"), error_code, format_error);
+ else
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE, _T("Unknown error code %u"), error_code);
+ } else {
+ // Remove CR/LF terminators
+ for (i=safe_tcslen(wErr_string)-1; ((wErr_string[i]==0x0A) || (wErr_string[i]==0x0D)); i--) {
+ wErr_string[i] = 0;
+ }
+ }
+ if (WideCharToMultiByte(CP_ACP, 0, wErr_string, -1, err_string, ERR_BUFFER_SIZE, NULL, NULL) < 0)
+ {
+ strcpy(err_string, "Unable to convert error string");
+ }
+ return err_string;
+}
+#endif
+
+static struct wince_device_priv *_device_priv(struct libusb_device *dev)
+{
+ return (struct wince_device_priv *) dev->os_priv;
+}
+
+// ceusbkwrapper to libusb error code mapping
+static int translate_driver_error(int error)
+{
+ switch (error) {
+ case ERROR_INVALID_PARAMETER:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ERROR_CALL_NOT_IMPLEMENTED:
+ case ERROR_NOT_SUPPORTED:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return LIBUSB_ERROR_NO_MEM;
+ case ERROR_INVALID_HANDLE:
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_BUSY:
+ return LIBUSB_ERROR_BUSY;
+
+ // Error codes that are either unexpected, or have
+ // no suitable LIBUSB_ERROR equivilant.
+ case ERROR_CANCELLED:
+ case ERROR_INTERNAL_ERROR:
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+static int init_dllimports()
+{
+ DLL_LOAD(ceusbkwrapper.dll, UkwOpenDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceList, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwReleaseDeviceList, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceAddress, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceDescriptor, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetConfigDescriptor, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwCloseDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwCancelTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIssueControlTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClaimInterface, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwReleaseInterface, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwSetInterfaceAlternateSetting, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClearHaltHost, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClearHaltDevice, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetConfig, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwSetConfig, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwResetDevice, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwKernelDriverActive, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwAttachKernelDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwDetachKernelDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIssueBulkTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIsPipeHalted, TRUE);
+ return LIBUSB_SUCCESS;
+}
+
+static int init_device(struct libusb_device *dev, UKW_DEVICE drv_dev,
+ unsigned char bus_addr, unsigned char dev_addr)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+ int r = LIBUSB_SUCCESS;
+
+ dev->bus_number = bus_addr;
+ dev->device_address = dev_addr;
+ priv->dev = drv_dev;
+
+ if (!UkwGetDeviceDescriptor(priv->dev, &(priv->desc))) {
+ r = translate_driver_error(GetLastError());
+ }
+ return r;
+}
+
+// Internal API functions
+static int wince_init(struct libusb_context *ctx)
+{
+ int i, r = LIBUSB_ERROR_OTHER;
+ HANDLE semaphore;
+ TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore's release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
+ CloseHandle(semaphore);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // NB: concurrent usage supposes that init calls are equally balanced with
+ // exit calls. If init is called more than exit, we will not exit properly
+ if ( ++concurrent_usage == 0 ) { // First init?
+ // Initialize pollable file descriptors
+ init_polling();
+
+ // Load DLL imports
+ if (init_dllimports() != LIBUSB_SUCCESS) {
+ usbi_err(ctx, "could not resolve DLL functions");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // try to open a handle to the driver
+ driver_handle = UkwOpenDriver();
+ if (driver_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(ctx, "could not connect to driver");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // Windows CE doesn't have a way of specifying thread affinity, so this code
+ // just has to hope QueryPerformanceCounter doesn't report different values when
+ // running on different cores.
+ r = LIBUSB_ERROR_NO_MEM;
+ for (i = 0; i < 2; i++) {
+ timer_request[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (timer_request[i] == NULL) {
+ usbi_err(ctx, "could not create timer request event %d - aborting", i);
+ goto init_exit;
+ }
+ }
+ timer_response = CreateSemaphore(NULL, 0, MAX_TIMER_SEMAPHORES, NULL);
+ if (timer_response == NULL) {
+ usbi_err(ctx, "could not create timer response semaphore - aborting");
+ goto init_exit;
+ }
+ timer_mutex = CreateMutex(NULL, FALSE, NULL);
+ if (timer_mutex == NULL) {
+ usbi_err(ctx, "could not create timer mutex - aborting");
+ goto init_exit;
+ }
+ timer_thread = CreateThread(NULL, 0, wince_clock_gettime_threaded, NULL, 0, NULL);
+ if (timer_thread == NULL) {
+ usbi_err(ctx, "Unable to create timer thread - aborting");
+ goto init_exit;
+ }
+ }
+ // At this stage, either we went through full init successfully, or didn't need to
+ r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here.
+ if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ if (timer_thread) {
+ SetEvent(timer_request[1]); // actually the signal to quit the thread.
+ if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+ usbi_warn(ctx, "could not wait for timer thread to quit");
+ TerminateThread(timer_thread, 1); // shouldn't happen, but we're destroying
+ // all objects it might have held anyway.
+ }
+ CloseHandle(timer_thread);
+ timer_thread = NULL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (timer_request[i]) {
+ CloseHandle(timer_request[i]);
+ timer_request[i] = NULL;
+ }
+ }
+ if (timer_response) {
+ CloseHandle(timer_response);
+ timer_response = NULL;
+ }
+ if (timer_mutex) {
+ CloseHandle(timer_mutex);
+ timer_mutex = NULL;
+ }
+ }
+
+ if (r != LIBUSB_SUCCESS)
+ --concurrent_usage; // Not expected to call libusb_exit if we failed.
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+ return r;
+}
+
+static void wince_exit(void)
+{
+ int i;
+ HANDLE semaphore;
+ TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ return;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ CloseHandle(semaphore);
+ return;
+ }
+
+ // Only works if exits and inits are balanced exactly
+ if (--concurrent_usage < 0) { // Last exit
+ exit_polling();
+
+ if (timer_thread) {
+ SetEvent(timer_request[1]); // actually the signal to quit the thread.
+ if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+ usbi_dbg("could not wait for timer thread to quit");
+ TerminateThread(timer_thread, 1);
+ }
+ CloseHandle(timer_thread);
+ timer_thread = NULL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (timer_request[i]) {
+ CloseHandle(timer_request[i]);
+ timer_request[i] = NULL;
+ }
+ }
+ if (timer_response) {
+ CloseHandle(timer_response);
+ timer_response = NULL;
+ }
+ if (timer_mutex) {
+ CloseHandle(timer_mutex);
+ timer_mutex = NULL;
+ }
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+}
+
+static int wince_get_device_list(
+ struct libusb_context *ctx,
+ struct discovered_devs **discdevs)
+{
+ UKW_DEVICE devices[MAX_DEVICE_COUNT];
+ struct discovered_devs * new_devices = *discdevs;
+ DWORD count = 0, i;
+ struct libusb_device *dev;
+ unsigned char bus_addr, dev_addr;
+ unsigned long session_id;
+ BOOL success, need_unref = FALSE;
+ DWORD release_list_offset = 0;
+ int r = LIBUSB_SUCCESS;
+
+ success = UkwGetDeviceList(driver_handle, devices, MAX_DEVICE_COUNT, &count);
+ if (!success) {
+ int libusbErr = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get devices: %s", windows_error_str(0));
+ return libusbErr;
+ }
+ for(i = 0; i < count; ++i) {
+ release_list_offset = i;
+ success = UkwGetDeviceAddress(devices[i], &bus_addr, &dev_addr, &session_id);
+ if (!success) {
+ r = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get device address for %d: %s", i, windows_error_str(0));
+ goto err_out;
+ }
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ usbi_dbg("using existing device for %d/%d (session %ld)",
+ bus_addr, dev_addr, session_id);
+ // Release just this element in the device list (as we already hold a
+ // reference to it).
+ UkwReleaseDeviceList(driver_handle, &devices[i], 1);
+ release_list_offset++;
+ } else {
+ usbi_dbg("allocating new device for %d/%d (session %ld)",
+ bus_addr, dev_addr, session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+ need_unref = TRUE;
+ r = init_device(dev, devices[i], bus_addr, dev_addr);
+ if (r < 0)
+ goto err_out;
+ r = usbi_sanitize_device(dev);
+ if (r < 0)
+ goto err_out;
+ }
+ new_devices = discovered_devs_append(new_devices, dev);
+ if (!discdevs) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+ need_unref = FALSE;
+ }
+ *discdevs = new_devices;
+ return r;
+err_out:
+ *discdevs = new_devices;
+ if (need_unref)
+ libusb_unref_device(dev);
+ // Release the remainder of the unprocessed device list.
+ // The devices added to new_devices already will still be passed up to libusb,
+ // which can dispose of them at its leisure.
+ UkwReleaseDeviceList(driver_handle, &devices[release_list_offset], count - release_list_offset);
+ return r;
+}
+
+static int wince_open(struct libusb_device_handle *handle)
+{
+ // Nothing to do to open devices as a handle to it has
+ // been retrieved by wince_get_device_list
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_close(struct libusb_device_handle *handle)
+{
+ // Nothing to do as wince_open does nothing.
+}
+
+static int wince_get_device_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+
+ *host_endian = 1;
+ memcpy(buffer, &priv->desc, DEVICE_DESC_LENGTH);
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_get_active_config_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+ *host_endian = 1;
+ if (!UkwGetConfigDescriptor(priv->dev, UKW_ACTIVE_CONFIGURATION, buffer, len, &actualSize)) {
+ return translate_driver_error(GetLastError());
+ }
+ return actualSize;
+}
+
+static int wince_get_config_descriptor(
+ struct libusb_device *device,
+ uint8_t config_index,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+ *host_endian = 0;
+ if (!UkwGetConfigDescriptor(priv->dev, config_index, buffer, len, &actualSize)) {
+ return translate_driver_error(GetLastError());
+ }
+ return actualSize;
+}
+
+static int wince_get_configuration(
+ struct libusb_device_handle *handle,
+ int *config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ UCHAR cv = 0;
+ if (!UkwGetConfig(priv->dev, &cv)) {
+ return translate_driver_error(GetLastError());
+ }
+ (*config) = cv;
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_configuration(
+ struct libusb_device_handle *handle,
+ int config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ // Setting configuration 0 places the device in Address state.
+ // This should correspond to the "unconfigured state" required by
+ // libusb when the specified configuration is -1.
+ UCHAR cv = (config < 0) ? 0 : config;
+ if (!UkwSetConfig(priv->dev, cv)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_claim_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwClaimInterface(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_release_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, 0)) {
+ return translate_driver_error(GetLastError());
+ }
+ if (!UkwReleaseInterface(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_interface_altsetting(
+ struct libusb_device_handle *handle,
+ int interface_number, int altsetting)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, altsetting)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_clear_halt(
+ struct libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwClearHaltHost(priv->dev, endpoint)) {
+ return translate_driver_error(GetLastError());
+ }
+ if (!UkwClearHaltDevice(priv->dev, endpoint)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_reset_device(
+ struct libusb_device_handle *handle)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwResetDevice(priv->dev)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_kernel_driver_active(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ BOOL result = FALSE;
+ if (!UkwKernelDriverActive(priv->dev, interface_number, &result)) {
+ return translate_driver_error(GetLastError());
+ }
+ return result ? 1 : 0;
+}
+
+static int wince_detach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwDetachKernelDriver(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_attach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwAttachKernelDriver(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_destroy_device(
+ struct libusb_device *dev)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+ UkwReleaseDeviceList(driver_handle, &priv->dev, 1);
+}
+
+static void wince_clear_transfer_priv(
+ struct usbi_transfer *itransfer)
+{
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd);
+ // No need to cancel transfer as it is either complete or abandoned
+ wfd.itransfer = NULL;
+ CloseHandle(wfd.handle);
+ usbi_free_fd(transfer_priv->pollable_fd.fd);
+}
+
+static int wince_cancel_transfer(
+ struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+
+ if (!UkwCancelTransfer(priv->dev, transfer_priv->pollable_fd.overlapped, UKW_TF_NO_WAIT)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ BOOL direction_in, ret;
+ struct winfd wfd;
+ DWORD flags;
+ HANDLE eventHandle;
+ PUKW_CONTROL_HEADER setup = NULL;
+ const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ if (control_transfer) {
+ setup = (PUKW_CONTROL_HEADER) transfer->buffer;
+ direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
+ } else {
+ direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+ }
+ flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER;
+ flags |= UKW_TF_SHORT_TRANSFER_OK;
+
+ eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (eventHandle == NULL) {
+ usbi_err(ctx, "Failed to create event for async transfer");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer);
+ if (wfd.fd < 0) {
+ CloseHandle(eventHandle);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ if (control_transfer) {
+ // Split out control setup header and data buffer
+ DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
+ PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)];
+
+ ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped);
+ } else {
+ ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer,
+ transfer->length, &transfer->actual_length, wfd.overlapped);
+ }
+ if (!ret) {
+ int libusbErr = translate_driver_error(GetLastError());
+ usbi_err(ctx, "UkwIssue%sTransfer failed: error %d",
+ control_transfer ? "Control" : "Bulk", GetLastError());
+ wince_clear_transfer_priv(itransfer);
+ return libusbErr;
+ }
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
+ itransfer->flags |= USBI_TRANSFER_UPDATED_FDS;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_submit_iso_transfer(struct usbi_transfer *itransfer)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int wince_submit_transfer(
+ struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return wince_submit_control_or_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return wince_submit_iso_transfer(itransfer);
+ default:
+ usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void wince_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int status;
+
+ usbi_dbg("handling I/O completion with errcode %d", io_result);
+
+ if (io_result == ERROR_NOT_SUPPORTED) {
+ /* The WinCE USB layer (and therefore the USB Kernel Wrapper Driver) will report
+ * USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the endpoint isn't actually
+ * stalled.
+ *
+ * One example of this is that some devices will occasionally fail to reply to an IN
+ * token. The WinCE USB layer carries on with the transaction until it is completed
+ * (or cancelled) but then completes it with USB_ERROR_STALL.
+ *
+ * This code therefore needs to confirm that there really is a stall error, but checking
+ * the pipe status and requesting the endpoint status from the device.
+ */
+ BOOL halted = FALSE;
+ usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall");
+ if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) {
+ /* The host side doesn't think the endpoint is halted, so check with the device if
+ * it is stalled.
+ *
+ * So form a GET_STATUS control request. This is done synchronously,
+ * which is a bit naughty, but this is a special corner case. */
+ WORD wStatus = 0;
+ DWORD written = 0;
+ UKW_CONTROL_HEADER ctrlHeader;
+ ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD |
+ LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT;
+ ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS;
+ ctrlHeader.wValue = 0;
+ ctrlHeader.wIndex = transfer->endpoint;
+ ctrlHeader.wLength = sizeof(wStatus);
+ if (UkwIssueControlTransfer(priv->dev,
+ UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT,
+ &ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) {
+ if (written == sizeof(wStatus) &&
+ (wStatus & STATUS_HALT_FLAG) == 0) {
+ if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) {
+ usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success");
+ io_result = ERROR_SUCCESS;
+ } else {
+ usbi_dbg("Endpoint does appear to be stalled, but the host is halted, changing error");
+ io_result = ERROR_IO_DEVICE;
+ }
+ }
+ }
+ }
+ }
+
+ switch(io_result) {
+ case ERROR_SUCCESS:
+ itransfer->transferred += io_size;
+ status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ case ERROR_CANCELLED:
+ usbi_dbg("detected transfer cancel");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ case ERROR_NOT_SUPPORTED:
+ case ERROR_GEN_FAILURE:
+ usbi_dbg("detected endpoint stall");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ case ERROR_SEM_TIMEOUT:
+ usbi_dbg("detected semaphore timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ break;
+ case ERROR_OPERATION_ABORTED:
+ if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) {
+ usbi_dbg("detected timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ } else {
+ usbi_dbg("detected operation aborted");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ }
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result));
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+ wince_clear_transfer_priv(itransfer);
+ if (status == LIBUSB_TRANSFER_CANCELLED) {
+ usbi_handle_transfer_cancellation(itransfer);
+ } else {
+ usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
+ }
+}
+
+static void wince_handle_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ wince_transfer_callback (itransfer, io_result, io_size);
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+ }
+}
+
+static int wince_handle_events(
+ struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+ struct wince_transfer_priv* transfer_priv = NULL;
+ POLL_NFDS_TYPE i = 0;
+ BOOL found = FALSE;
+ struct usbi_transfer *transfer;
+ DWORD io_size, io_result;
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+
+ usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
+
+ if (!fds[i].revents) {
+ continue;
+ }
+
+ num_ready--;
+
+ // Because a Windows OVERLAPPED is used for poll emulation,
+ // a pollable fd is created and stored with each transfer
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ transfer_priv = usbi_transfer_get_os_priv(transfer);
+ if (transfer_priv->pollable_fd.fd == fds[i].fd) {
+ found = TRUE;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (found && HasOverlappedIoCompleted(transfer_priv->pollable_fd.overlapped)) {
+ io_result = (DWORD)transfer_priv->pollable_fd.overlapped->Internal;
+ io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh;
+ usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd);
+ // let handle_callback free the event using the transfer wfd
+ // If you don't use the transfer wfd, you run a risk of trying to free a
+ // newly allocated wfd that took the place of the one from the transfer.
+ wince_handle_callback(transfer, io_result, io_size);
+ } else if (found) {
+ usbi_err(ctx, "matching transfer for fd %x has not completed", fds[i]);
+ return LIBUSB_ERROR_OTHER;
+ } else {
+ usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * Monotonic and real time functions
+ */
+unsigned __stdcall wince_clock_gettime_threaded(void* param)
+{
+ LARGE_INTEGER hires_counter, li_frequency;
+ LONG nb_responses;
+ int timer_index;
+
+ // Init - find out if we have access to a monotonic (hires) timer
+ if (!QueryPerformanceFrequency(&li_frequency)) {
+ usbi_dbg("no hires timer available on this platform");
+ hires_frequency = 0;
+ hires_ticks_to_ps = UINT64_C(0);
+ } else {
+ hires_frequency = li_frequency.QuadPart;
+ // The hires frequency can go as high as 4 GHz, so we'll use a conversion
+ // to picoseconds to compute the tv_nsecs part in clock_gettime
+ hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
+ usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
+ }
+
+ // Main loop - wait for requests
+ while (1) {
+ timer_index = WaitForMultipleObjects(2, timer_request, FALSE, INFINITE) - WAIT_OBJECT_0;
+ if ( (timer_index != 0) && (timer_index != 1) ) {
+ usbi_dbg("failure to wait on requests: %s", windows_error_str(0));
+ continue;
+ }
+ if (request_count[timer_index] == 0) {
+ // Request already handled
+ ResetEvent(timer_request[timer_index]);
+ // There's still a possiblity that a thread sends a request between the
+ // time we test request_count[] == 0 and we reset the event, in which case
+ // the request would be ignored. The simple solution to that is to test
+ // request_count again and process requests if non zero.
+ if (request_count[timer_index] == 0)
+ continue;
+ }
+ switch (timer_index) {
+ case 0:
+ WaitForSingleObject(timer_mutex, INFINITE);
+ // Requests to this thread are for hires always
+ if (QueryPerformanceCounter(&hires_counter) != 0) {
+ timer_tp.tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+ timer_tp.tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency)/1000) * hires_ticks_to_ps);
+ } else {
+ // Fallback to real-time if we can't get monotonic value
+ // Note that real-time clock does not wait on the mutex or this thread.
+ wince_clock_gettime(USBI_CLOCK_REALTIME, &timer_tp);
+ }
+ ReleaseMutex(timer_mutex);
+
+ nb_responses = InterlockedExchange((LONG*)&request_count[0], 0);
+ if ( (nb_responses)
+ && (ReleaseSemaphore(timer_response, nb_responses, NULL) == 0) ) {
+ usbi_dbg("unable to release timer semaphore %d: %s", windows_error_str(0));
+ }
+ continue;
+ case 1: // time to quit
+ usbi_dbg("timer thread quitting");
+ return 0;
+ }
+ }
+ usbi_dbg("ERROR: broken timer thread");
+ return 1;
+}
+
+static int wince_clock_gettime(int clk_id, struct timespec *tp)
+{
+ FILETIME filetime;
+ ULARGE_INTEGER rtime;
+ DWORD r;
+ SYSTEMTIME st;
+ switch(clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ if (hires_frequency != 0) {
+ while (1) {
+ InterlockedIncrement((LONG*)&request_count[0]);
+ SetEvent(timer_request[0]);
+ r = WaitForSingleObject(timer_response, TIMER_REQUEST_RETRY_MS);
+ switch(r) {
+ case WAIT_OBJECT_0:
+ WaitForSingleObject(timer_mutex, INFINITE);
+ *tp = timer_tp;
+ ReleaseMutex(timer_mutex);
+ return LIBUSB_SUCCESS;
+ case WAIT_TIMEOUT:
+ usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
+ break; // Retry until successful
+ default:
+ usbi_dbg("WaitForSingleObject failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+ }
+ // Fall through and return real-time if monotonic was not detected @ timer init
+ case USBI_CLOCK_REALTIME:
+ // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
+ // with a predef epoch_time to have an epoch that starts at 1970.01.01 00:00
+ // Note however that our resolution is bounded by the Windows system time
+ // functions and is at best of the order of 1 ms (or, usually, worse)
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &filetime);
+ rtime.LowPart = filetime.dwLowDateTime;
+ rtime.HighPart = filetime.dwHighDateTime;
+ rtime.QuadPart -= epoch_time;
+ tp->tv_sec = (long)(rtime.QuadPart / 10000000);
+ tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+ return LIBUSB_SUCCESS;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+const struct usbi_os_backend wince_backend = {
+ "Windows CE",
+ wince_init,
+ wince_exit,
+
+ wince_get_device_list,
+ wince_open,
+ wince_close,
+
+ wince_get_device_descriptor,
+ wince_get_active_config_descriptor,
+ wince_get_config_descriptor,
+
+ wince_get_configuration,
+ wince_set_configuration,
+ wince_claim_interface,
+ wince_release_interface,
+
+ wince_set_interface_altsetting,
+ wince_clear_halt,
+ wince_reset_device,
+
+ wince_kernel_driver_active,
+ wince_detach_kernel_driver,
+ wince_attach_kernel_driver,
+
+ wince_destroy_device,
+
+ wince_submit_transfer,
+ wince_cancel_transfer,
+ wince_clear_transfer_priv,
+
+ wince_handle_events,
+
+ wince_clock_gettime,
+ sizeof(struct wince_device_priv),
+ sizeof(struct wince_device_handle_priv),
+ sizeof(struct wince_transfer_priv),
+ 0,
+};
diff --git a/libusb/os/wince_usb.h b/libusb/os/wince_usb.h
new file mode 100644
index 0000000..b125721
--- /dev/null
+++ b/libusb/os/wince_usb.h
@@ -0,0 +1,131 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright (C) 2011-2012 RealVNC Ltd.
+ * Portions taken from Windows backend, which is
+ * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * 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
+ */
+#pragma once
+
+#include "windows_common.h"
+
+#include <windows.h>
+#include "poll_windows.h"
+
+#define MAX_DEVICE_COUNT 256
+
+// This is a modified dump of the types in the ceusbkwrapper.h library header
+// with functions transformed into extern pointers.
+//
+// This backend dynamically loads ceusbkwrapper.dll and doesn't include
+// ceusbkwrapper.h directly to simplify the build process. The kernel
+// side wrapper driver is built using the platform image build tools,
+// which makes it difficult to reference directly from the libusbx build
+// system.
+struct UKW_DEVICE_PRIV;
+typedef struct UKW_DEVICE_PRIV *UKW_DEVICE;
+typedef UKW_DEVICE *PUKW_DEVICE, *LPUKW_DEVICE;
+
+typedef struct {
+ UINT8 bLength;
+ UINT8 bDescriptorType;
+ UINT16 bcdUSB;
+ UINT8 bDeviceClass;
+ UINT8 bDeviceSubClass;
+ UINT8 bDeviceProtocol;
+ UINT8 bMaxPacketSize0;
+ UINT16 idVendor;
+ UINT16 idProduct;
+ UINT16 bcdDevice;
+ UINT8 iManufacturer;
+ UINT8 iProduct;
+ UINT8 iSerialNumber;
+ UINT8 bNumConfigurations;
+} UKW_DEVICE_DESCRIPTOR, *PUKW_DEVICE_DESCRIPTOR, *LPUKW_DEVICE_DESCRIPTOR;
+
+typedef struct {
+ UINT8 bmRequestType;
+ UINT8 bRequest;
+ UINT16 wValue;
+ UINT16 wIndex;
+ UINT16 wLength;
+} UKW_CONTROL_HEADER, *PUKW_CONTROL_HEADER, *LPUKW_CONTROL_HEADER;
+
+// Collection of flags which can be used when issuing transfer requests
+/* Indicates that the transfer direction is 'in' */
+#define UKW_TF_IN_TRANSFER 0x00000001
+/* Indicates that the transfer direction is 'out' */
+#define UKW_TF_OUT_TRANSFER 0x00000000
+/* Specifies that the transfer should complete as soon as possible,
+ * even if no OVERLAPPED structure has been provided. */
+#define UKW_TF_NO_WAIT 0x00000100
+/* Indicates that transfers shorter than the buffer are ok */
+#define UKW_TF_SHORT_TRANSFER_OK 0x00000200
+#define UKW_TF_SEND_TO_DEVICE 0x00010000
+#define UKW_TF_SEND_TO_INTERFACE 0x00020000
+#define UKW_TF_SEND_TO_ENDPOINT 0x00040000
+/* Don't block when waiting for memory allocations */
+#define UKW_TF_DONT_BLOCK_FOR_MEM 0x00080000
+
+/* Value to use when dealing with configuration values, such as UkwGetConfigDescriptor,
+ * to specify the currently active configuration for the device. */
+#define UKW_ACTIVE_CONFIGURATION -1
+
+DLL_DECLARE(WINAPI, HANDLE, UkwOpenDriver, ());
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceList, (HANDLE, LPUKW_DEVICE, DWORD, LPDWORD));
+DLL_DECLARE(WINAPI, void, UkwReleaseDeviceList, (HANDLE, LPUKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceAddress, (UKW_DEVICE, unsigned char*, unsigned char*, unsigned long*));
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceDescriptor, (UKW_DEVICE, LPUKW_DEVICE_DESCRIPTOR));
+DLL_DECLARE(WINAPI, BOOL, UkwGetConfigDescriptor, (UKW_DEVICE, DWORD, LPVOID, DWORD, LPDWORD));
+DLL_DECLARE(WINAPI, void, UkwCloseDriver, (HANDLE));
+DLL_DECLARE(WINAPI, BOOL, UkwCancelTransfer, (UKW_DEVICE, LPOVERLAPPED, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwIssueControlTransfer, (UKW_DEVICE, DWORD, LPUKW_CONTROL_HEADER, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, UkwClaimInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwReleaseInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwSetInterfaceAlternateSetting, (UKW_DEVICE, DWORD, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwClearHaltHost, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwClearHaltDevice, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwGetConfig, (UKW_DEVICE, PUCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwSetConfig, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwResetDevice, (UKW_DEVICE));
+DLL_DECLARE(WINAPI, BOOL, UkwKernelDriverActive, (UKW_DEVICE, DWORD, PBOOL));
+DLL_DECLARE(WINAPI, BOOL, UkwAttachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwDetachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwIssueBulkTransfer, (UKW_DEVICE, DWORD, UCHAR, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, UkwIsPipeHalted, (UKW_DEVICE, UCHAR, LPBOOL));
+
+// Used to determine if an endpoint status really is halted on a failed transfer.
+#define STATUS_HALT_FLAG 0x1
+
+struct wince_device_priv {
+ UKW_DEVICE dev;
+ UKW_DEVICE_DESCRIPTOR desc;
+};
+
+struct wince_device_handle_priv {
+ // This member isn't used, but only exists to avoid an empty structure
+ // for private data for the device handle.
+ int reserved;
+};
+
+struct wince_transfer_priv {
+ struct winfd pollable_fd;
+ uint8_t interface_number;
+};
+
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 40e8a76..b175dda 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10595
+#define LIBUSB_NANO 10596
diff --git a/msvc/config.h b/msvc/config.h
index 730d091..eb05d3d 100644
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -23,14 +23,22 @@
/* Message logging */
#define ENABLE_LOGGING 1
-/* Windows backend */
+/* Windows/WinCE backend */
+#if defined(_WIN32_WCE)
+#define OS_WINCE 1
+#else
#define OS_WINDOWS 1
+#endif
/* type of second poll() argument */
#define POLL_NFDS_TYPE unsigned int
+#if !defined(_WIN32_WCE)
+
/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
+
+#endif \ No newline at end of file
diff --git a/msvc/errno.h b/msvc/errno.h
new file mode 100644
index 0000000..a1d4f64
--- /dev/null
+++ b/msvc/errno.h
@@ -0,0 +1,100 @@
+/*
+ * errno.h
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within the package.
+ *
+ * Error numbers and access to error reporting.
+ *
+ */
+
+#ifndef _ERRNO_H_
+#define _ERRNO_H_
+
+#include <crtdefs.h>
+
+/*
+ * Error numbers.
+ * TODO: Can't be sure of some of these assignments, I guessed from the
+ * names given by strerror and the defines in the Cygnus errno.h. A lot
+ * of the names from the Cygnus errno.h are not represented, and a few
+ * of the descriptions returned by strerror do not obviously match
+ * their error naming.
+ */
+#define EPERM 1 /* Operation not permitted */
+#define ENOFILE 2 /* No such file or directory */
+#define ENOENT 2
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted function call */
+#define EIO 5 /* Input/output error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file descriptor */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Resource temporarily unavailable */
+#define ENOMEM 12 /* Not enough space */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+/* 15 - Unknown Error */
+#define EBUSY 16 /* strerror reports "Resource device" */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Improper link (cross-device link?) */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* Too many open files in system */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Inappropriate I/O control operation */
+/* 26 - Unknown Error */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Invalid seek (seek on a pipe?) */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Domain error (math functions) */
+#define ERANGE 34 /* Result too large (possibly too small) */
+/* 35 - Unknown Error */
+#define EDEADLOCK 36 /* Resource deadlock avoided (non-Cyg) */
+#define EDEADLK 36
+/* 37 - Unknown Error */
+#define ENAMETOOLONG 38 /* Filename too long (91 in Cyg?) */
+#define ENOLCK 39 /* No locks available (46 in Cyg?) */
+#define ENOSYS 40 /* Function not implemented (88 in Cyg?) */
+#define ENOTEMPTY 41 /* Directory not empty (90 in Cyg?) */
+#define EILSEQ 42 /* Illegal byte sequence */
+
+/*
+ * NOTE: ENAMETOOLONG and ENOTEMPTY conflict with definitions in the
+ * sockets.h header provided with windows32api-0.1.2.
+ * You should go and put an #if 0 ... #endif around the whole block
+ * of errors (look at the comment above them).
+ */
+
+#ifndef RC_INVOKED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Definitions of errno. For _doserrno, sys_nerr and * sys_errlist, see
+ * stdlib.h.
+ */
+#if defined(_UWIN) || defined(_WIN32_WCE)
+#undef errno
+extern int errno;
+#else
+_CRTIMP int* __cdecl _errno(void);
+#define errno (*_errno())
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Not RC_INVOKED */
+
+#endif /* Not _ERRNO_H_ */ \ No newline at end of file
diff --git a/msvc/missing.c b/msvc/missing.c
new file mode 100644
index 0000000..85d9d6f
--- /dev/null
+++ b/msvc/missing.c
@@ -0,0 +1,80 @@
+/*
+ * Source file for missing WinCE functionality
+ * Copyright © 2012 RealVNC Ltd.
+ *
+ * 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
+ */
+
+#include "missing.h"
+
+#include <config.h>
+#include <libusbi.h>
+
+#include <windows.h>
+
+// The registry path to store environment variables
+#define ENVIRONMENT_REG_PATH _T("Software\\libusb\\environment")
+
+/* Workaround getenv not being available on WinCE.
+ * Instead look in HKLM\Software\libusb\environment */
+char *getenv(const char *name)
+{
+ static char value[MAX_PATH];
+ TCHAR wValue[MAX_PATH];
+ WCHAR wName[MAX_PATH];
+ DWORD dwType, dwData;
+ HKEY hkey;
+ LONG rc;
+
+ if (!name)
+ return NULL;
+
+ if (MultiByteToWideChar(CP_UTF8, 0, name, -1, wName, MAX_PATH) <= 0) {
+ usbi_dbg("Failed to convert environment variable name to wide string");
+ return NULL;
+ }
+ wName[MAX_PATH - 1] = 0; // Be sure it's NUL terminated
+
+ rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ENVIRONMENT_REG_PATH, 0, KEY_QUERY_VALUE, &hkey);
+ if (rc != ERROR_SUCCESS) {
+ usbi_dbg("Failed to open registry key for getenv with error %d", rc);
+ return NULL;
+ }
+
+ // Attempt to read the key
+ dwData = sizeof(wValue);
+ rc = RegQueryValueEx(hkey, wName, NULL, &dwType,
+ (LPBYTE)&wValue, &dwData);
+ RegCloseKey(hkey);
+ if (rc != ERROR_SUCCESS) {
+ usbi_dbg("Failed to read registry key value for getenv with error %d", rc);
+ return NULL;
+ }
+ if (dwType != REG_SZ) {
+ usbi_dbg("Registry value was of type %d instead of REG_SZ", dwType);
+ return NULL;
+ }
+
+ // Success in reading the key, convert from WCHAR to char
+ if (WideCharToMultiByte(CP_UTF8, 0,
+ wValue, dwData / sizeof(*wValue),
+ value, MAX_PATH,
+ NULL, NULL) <= 0) {
+ usbi_dbg("Failed to convert environment variable value to narrow string");
+ return NULL;
+ }
+ value[MAX_PATH - 1] = 0; // Be sure it's NUL terminated
+ return value;
+}
diff --git a/msvc/missing.h b/msvc/missing.h
new file mode 100644
index 0000000..7846976
--- /dev/null
+++ b/msvc/missing.h
@@ -0,0 +1,29 @@
+/*
+ * Header file for missing WinCE functionality
+ * Copyright © 2012 RealVNC Ltd.
+ *
+ * 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
+ */
+
+#ifndef MISSING_H
+#define MISSING_H
+
+/* Windows CE doesn't have any APIs to query environment variables.
+ *
+ * This contains a registry based implementation of getenv.
+ */
+char *getenv(const char *name);
+
+#endif
diff --git a/tests/stress.c b/tests/stress.c
index c4a92fa..26860a0 100644
--- a/tests/stress.c
+++ b/tests/stress.c
@@ -18,8 +18,10 @@
*/
#include <stdio.h>
-#include <sys/types.h>
#include <memory.h>
+#if !defined(_WIN32_WCE)
+#include <sys/types.h>
+#endif
#include "libusb.h"
diff --git a/tests/testlib.c b/tests/testlib.c
index 9e45d31..3a91933 100644
--- a/tests/testlib.c
+++ b/tests/testlib.c
@@ -23,11 +23,17 @@
#include <stdarg.h>
#include <string.h>
#include <errno.h>
+#if !defined(_WIN32_WCE)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#endif
-#ifdef _WIN32
+#if defined(_WIN32_WCE)
+// No support for selective redirection of STDOUT on WinCE.
+#define DISABLE_STDOUT_REDIRECTION
+#define STDOUT_FILENO 1
+#elif defined(_WIN32)
#include <io.h>
#define dup _dup
#define dup2 _dup2
@@ -72,6 +78,7 @@ static void print_usage(int argc, char ** argv)
static void cleanup_test_output(libusbx_testlib_ctx * ctx)
{
+#ifndef DISABLE_STDOUT_REDIRECTION
if (ctx->output_file != NULL) {
fclose(ctx->output_file);
ctx->output_file = NULL;
@@ -84,6 +91,7 @@ static void cleanup_test_output(libusbx_testlib_ctx * ctx)
close(ctx->null_fd);
ctx->null_fd = INVALID_FD;
}
+#endif
}
/**
@@ -92,6 +100,11 @@ static void cleanup_test_output(libusbx_testlib_ctx * ctx)
*/
static int setup_test_output(libusbx_testlib_ctx * ctx)
{
+#ifdef DISABLE_STDOUT_REDIRECTION
+ ctx->output_fd = STDOUT_FILENO;
+ ctx->output_file = stdout;
+ return 0;
+#else
/* Keep a copy of STDOUT for test output */
ctx->output_fd = dup(STDOUT_FILENO);
if (ctx->output_fd < 0) {
@@ -122,6 +135,7 @@ static int setup_test_output(libusbx_testlib_ctx * ctx)
}
}
return 0;
+#endif
}
void libusbx_testlib_logf(libusbx_testlib_ctx * ctx,