diff options
author | Joshua Hill | 2010-11-06 18:09:53 -0400 |
---|---|---|
committer | Joshua Hill | 2010-11-06 18:09:53 -0400 |
commit | d82a73f1180a5f201397b92e6f6db33411cbe686 (patch) | |
tree | bcc7c4ba2e103770f905972854392d8c3e448f16 | |
parent | e89c9418e7072d5a1491055c8f78748bf82de49c (diff) | |
download | libirecovery-d82a73f1180a5f201397b92e6f6db33411cbe686.tar.gz libirecovery-d82a73f1180a5f201397b92e6f6db33411cbe686.tar.bz2 |
updated libirecovery to the latest version from greenpois0n. it at least compiles on osx now, I need to test on other systems
23 files changed, 16448 insertions, 748 deletions
@@ -1,37 +1,101 @@ -all: linux - @echo "Please choose either macosx, linux, or windows" - -static: - gcc -o libirecovery.o -c src/libirecovery.c -g -I./include - ar rs libirecovery.a libirecovery.o - gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline -lusb-1.0 - -linux: - gcc -o libirecovery.o -c src/libirecovery.c -g -I./include -lreadline -fPIC - gcc -o libirecovery.so libirecovery.o -g -shared -Wl,-soname,libirecovery.so -lusb-1.0 - gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline - -macosx: - gcc -o libirecovery.dylib -c src/libirecovery.c -dynamiclib - gcc -o irecovery src/irecovery.c -I./include -L. -lirecovery -lreadline -lusb-1.0 - -windows: - gcc -o libirecovery.dll -c src/libirecovery.c -I. -lusb-1.0 -lreadline -shared -fPIC - gcc -o irecovery irecovery.c -I. -lirecovery -lreadline - -install: - cp libirecovery.so /usr/local/lib/libirecovery.so - cp include/libirecovery.h /usr/local/include/libirecovery.h - cp irecovery /usr/local/bin/irecovery - ldconfig - -uninstall: - rm -rf /usr/local/lib/libirecovery.so - rm -rf /usr/local/include/libirecovery.h - rm -rf /usr/local/bin/irecovery - -clean: - rm -rf irecovery libirecovery.o libirecovery.so libirecovery.a +AR := ar +CP := cp +CC := gcc + +UNAME := $(shell uname -s) + +ifeq ($(UNAME),Darwin) + CFLAGS = -I./include -I/usr/local/include -I/opt/local/include + LDFLAGS = -L/usr/lib -L/opt/local/lib + LIBS = -lreadline -lusb-1.0 -framework CoreFoundation -framework IOKit + + IRECOVERY_TARGET = irecovery + IRECOVERY_OBJECTS = irecovery.o libirecovery.a + IRECOVERY_CFLAGS = $(CFLAGS) + IRECOVERY_LDFLAGS = $(LDFLAGS) + IRECOVERY_LIBS = $(LIBS) + + LIBIRECOVERY_STATIC_TARGET = libirecovery.a + LIBIRECOVERY_STATIC_OBJECTS = libirecovery.o + LIBIRECOVERY_STATIC_CFLAGS = $(CFLAGS) + LIBIRECOVERY_STATIC_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_STATIC_LIBS = $(LIBS) + + LIBIRECOVERY_SHARED_TARGET = libirecovery.dylib + LIBIRECOVERY_SHARED_OBJECTS = libirecovery.o + LIBIRECOVERY_SHARED_CFLAGS = $(CFLAGS) + LIBIRECOVERY_SHARED_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_SHARED_LIBS = $(LIBS) +else + ifeq ($(UNAME),MINGW32_NT-5.1) + CFLAGS = -O3 -I include -I c:\mingw\include + LDFLAGS = -L c:\mingw\lib -lreadline + LIBS = -lreadline -lusb-1.0 -framework CoreFoundation -framework IOKit + + IRECOVERY_TARGET = irecovery.exe + IRECOVERY_OBJECTS = irecovery.o + IRECOVERY_CFLAGS = $(CFLAGS) + IRECOVERY_LDFLAGS = $(LDFLAGS) + IRECOVERY_LIBS = $(LIBS) libirecovery.a + + LIBIRECOVERY_STATIC_TARGET = libirecovery.a + LIBIRECOVERY_STATIC_OBJECTS = libirecovery.o + LIBIRECOVERY_STATIC_CFLAGS = $(CFLAGS) + LIBIRECOVERY_STATIC_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_STATIC_LIBS = $(LIBS) + + LIBIRECOVERY_SHARED_TARGET = libirecovery.dll + LIBIRECOVERY_SHARED_OBJECTS = libirecovery.o + LIBIRECOVERY_SHARED_CFLAGS = $(CFLAGS) + LIBIRECOVERY_SHARED_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_SHARED_LIBS = $(LIBS) + else + CFLAGS = -O3 -I./include -I/usr/include -I/usr/local/include + LDFLAGS = -L/usr/lib -L/usr/local/lib + LIBS = -lusb-1.0 -lreadline + IRECOVERY_TARGET = irecovery + IRECOVERY_OBJECTS = irecovery.o + IRECOVERY_CFLAGS = $(CFLAGS) + IRECOVERY_LDFLAGS = $(LDFLAGS) + IRECOVERY_LIBS = $(LIBS) + + LIBIRECOVERY_STATIC_TARGET = libirecovery.a + LIBIRECOVERY_STATIC_OBJECTS = libirecovery.o + LIBIRECOVERY_STATIC_CFLAGS = $(CLFAGS) + LIBIRECOVERY_STATIC_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_STATIC_LIBS = $(LIBS) + + LIBIRECOVERY_SHARED_TARGET = libirecovery.so + LIBIRECOVERY_SHARED_OBJECTS = libirecovery.o + LIBIRECOVERY_SHARED_CFLAGS = $(CFLAGS) + LIBIRECOVERY_SHARED_LDFLAGS = $(LDFLAGS) + LIBIRECOVERY_SHARED_LIBS = $(LIBS) + endif +endif + +LIBIRECOVERY_OBJECTS = libirecovery.o +IRECOVERY_OBJECTS = irecovery.o libirecovery.a + +TARGETS = $(LIBIRECOVERY_STATIC_TARGET) $(IRECOVERY_TARGET) +OBJECTS = libirecovery.o irecovery.o + +%.o: %.S + $(CC) -c $(<) -o $(@) $(CFLAGS) + +%.o: %.c + $(CC) -c $(<) -o $(@) $(CFLAGS) +$(LIBIRECOVERY_STATIC_TARGET): $(LIBIRECOVERY_STATIC_OBJECTS) + $(AR) rs $(LIBIRECOVERY_STATIC_TARGET) $(LIBIRECOVERY_STATIC_OBJECTS) + +$(LIBIRECOVERY_SHARED_TARGET): $(LIBIRECOVERY_SHARED_OBJECTS) + +$(IRECOVERY_TARGET): $(IRECOVERY_OBJECTS) + $(CC) -o $(IRECOVERY_TARGET) $(IRECOVERY_OBJECTS) $(IRECOVERY_CFLAGS) $(IRECOVERY_LDFLAGS) $(IRECOVERY_LIBS) + +all: $(TARGETS) + +clean: + $(RM) $(LIBIRECOVERY_STATIC_TARGET) $(IRECOVERY_TARGET) *.o
\ No newline at end of file diff --git a/include/libirecovery.h b/include/libirecovery.h index 2ef1ae3..3220669 100644 --- a/include/libirecovery.h +++ b/include/libirecovery.h @@ -1,19 +1,20 @@ /** - * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode - * Copyright (C) 2008 - 2009 westbaer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * GreenPois0n iRecovery - libirecovery.h + * Copyright (C) 2010 Chronic-Dev Team + * Copyright (C) 2010 Joshua Hill + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #ifndef LIBIRECOVERY_H @@ -23,10 +24,52 @@ extern "C" { #endif +#ifndef WIN32 #include <libusb-1.0/libusb.h> +#else +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#define sleep(n) Sleep(1000 * n) +#endif #define APPLE_VENDOR_ID 0x05AC +#define CPID_UNKNOWN -1 +#define CPID_IPHONE2G 8900 +#define CPID_IPOD1G 8900 +#define CPID_IPHONE3G 8900 +#define CPID_IPOD2G 8720 +#define CPID_IPHONE3GS 8920 +#define CPID_IPOD3G 8922 +#define CPID_IPAD1G 8930 +#define CPID_IPHONE4 8930 +#define CPID_IPOD4G 8930 +#define CPID_APPLETV2 8930 + +#define BDID_UNKNOWN -1 +#define BDID_IPHONE2G 0 +#define BDID_IPOD1G 2 +#define BDID_IPHONE3G 4 +#define BDID_IPOD2G 0 +#define BDID_IPHONE3GS 0 +#define BDID_IPOD3G 2 +#define BDID_IPAD1G 2 +#define BDID_IPHONE4 0 +#define BDID_IPOD4G 8 +#define BDID_APPLETV2 10 + +#define DEVICE_UNKNOWN -1 +#define DEVICE_IPHONE2G 0 +#define DEVICE_IPOD1G 1 +#define DEVICE_IPHONE3G 2 +#define DEVICE_IPOD2G 3 +#define DEVICE_IPHONE3GS 4 +#define DEVICE_IPOD3G 5 +#define DEVICE_IPAD1G 6 +#define DEVICE_IPHONE4 7 +#define DEVICE_IPOD4G 8 +#define DEVICE_APPLETV2 9 + enum { kRecoveryMode1 = 0x1280, kRecoveryMode2 = 0x1281, @@ -46,6 +89,8 @@ typedef enum { IRECV_E_USB_STATUS = -7, IRECV_E_USB_INTERFACE = -8, IRECV_E_USB_CONFIGURATION = -9, + IRECV_E_PIPE = -10, + IRECV_E_TIMEOUT = -11, IRECV_E_UNKNOWN_ERROR = -255 } irecv_error_t; @@ -67,6 +112,7 @@ typedef struct { struct irecv_client; typedef struct irecv_client* irecv_client_t; +typedef struct irecv_device* irecv_device_t; typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event); struct irecv_client { @@ -75,7 +121,18 @@ struct irecv_client { int interface; int alt_interface; unsigned short mode; + char serial[256]; + +#ifndef WIN32 libusb_device_handle* handle; +#else + HANDLE handle; + HANDLE hDFU; + HANDLE hIB; + LPSTR iBootPath; + LPSTR DfuPath; +#endif + irecv_event_cb_t progress_callback; irecv_event_cb_t received_callback; irecv_event_cb_t connected_callback; @@ -84,26 +141,75 @@ struct irecv_client { irecv_event_cb_t disconnected_callback; }; +struct irecv_device { + int index; + const char* product; + const char* model; + unsigned int board_id; + unsigned int chip_id; + const char* url; +}; + +static struct irecv_device irecv_devices[] = { + { 0, "iPhone1,1", "m68ap", 0, 8900, + "http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-7481.20100202.4orot/iPhone1,1_3.1.3_7E18_Restore.ipsw" }, + { 1, "iPod1,1", "n45ap", 2, 8900, + NULL }, + { 2, "iPhone1,2", "n82ap", 4, 8900, + "http://appldnld.apple.com/iPhone4/061-7932.20100908.3fgt5/iPhone1,2_4.1_8B117_Restore.ipsw" }, + { 3, "iPod2,1", "n72ap", 0, 8720, + "http://appldnld.apple.com/iPhone4/061-7937.20100908.ghj4f/iPod2,1_4.1_8B117_Restore.ipsw" }, + { 4, "iPhone2,1", "n88ap", 0, 8920, + "http://appldnld.apple.com/iPhone4/061-7938.20100908.F3rCk/iPhone2,1_4.1_8B117_Restore.ipsw" }, + { 5, "iPod3,1", "n18ap", 2, 8922, + "http://appldnld.apple.com/iPhone4/061-7941.20100908.sV9KE/iPod3,1_4.1_8B117_Restore.ipsw" }, + { 6, "iPad1,1", "k48ap", 2, 8930, + "http://appldnld.apple.com/iPad/061-8801.20100811.CvfR5/iPad1,1_3.2.2_7B500_Restore.ipsw" }, + { 7, "iPhone3,1", "n90ap", 0, 8930, + "http://appldnld.apple.com/iPhone4/061-7939.20100908.Lcyg3/iPhone3,1_4.1_8B117_Restore.ipsw" }, + { 8, "iPod4,1", "n81ap", 8, 8930, + "http://appldnld.apple.com/iPhone4/061-8490.20100901.hyjtR/iPod4,1_4.1_8B117_Restore.ipsw" }, + { 9, "AppleTV2,1", "k66ap", 10, 8930, + "http://appldnld.apple.com/AppleTV/061-8940.20100926.Tvtnz/AppleTV2,1_4.1_8M89_Restore.ipsw" }, + { -1, NULL, NULL, -1, -1, + NULL } +}; + void irecv_set_debug_level(int level); const char* irecv_strerror(irecv_error_t error); +irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts); irecv_error_t irecv_open(irecv_client_t* client); irecv_error_t irecv_reset(irecv_client_t client); irecv_error_t irecv_close(irecv_client_t client); irecv_error_t irecv_receive(irecv_client_t client); irecv_error_t irecv_send_exploit(irecv_client_t client); -irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid); -irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid); -irecv_error_t irecv_send_file(irecv_client_t client, const char* filename); -irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid); irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename); -irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command); irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration); + +irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data); irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type); + +irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished); +irecv_error_t irecv_send_command(irecv_client_t client, char* command); +irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished); + +irecv_error_t irecv_saveenv(irecv_client_t client); +irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value); irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value); irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value); irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface); -irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned int length); -irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data); +irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid); +irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid); +irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid); +void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr); + +void irecv_init(); +void irecv_exit(); +irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause); +irecv_error_t irecv_reset_counters(irecv_client_t client); +irecv_error_t irecv_finish_transfer(irecv_client_t client); +irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length); +irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device); #ifdef __cplusplus } diff --git a/include/libusb-1.0/config.h b/include/libusb-1.0/config.h new file mode 100644 index 0000000..643d4af --- /dev/null +++ b/include/libusb-1.0/config.h @@ -0,0 +1,105 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Default visibility */ +#define DEFAULT_VISIBILITY __attribute__((visibility("default"))) + +/* Debug message logging */ +/* #undef ENABLE_DEBUG_LOGGING */ + +/* Message logging */ +#define ENABLE_LOGGING 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if the system has the type `struct timespec'. */ +#define HAVE_STRUCT_TIMESPEC 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Darwin backend */ +#define OS_DARWIN + +/* Linux backend */ +/* #undef OS_LINUX */ + +/* Windows backend */ +/* #undef OS_WINDOWS */ + +/* Name of package */ +#define PACKAGE "libusb" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libusb-devel@lists.sourceforge.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libusb" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libusb 1.0.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libusb" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Use Posix Threads */ +#define THREADS_POSIX + +/* Backend handles timeout */ +#define USBI_OS_HANDLES_TIMEOUT + +/* timerfd headers available */ +/* #undef USBI_TIMERFD_AVAILABLE */ + +/* Version number of package */ +#define VERSION "1.0.8" + +/* Use GNU extensions */ +#define _GNU_SOURCE + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/include/libusb-1.0/libusb.h b/include/libusb-1.0/libusb.h new file mode 100644 index 0000000..8dc3362 --- /dev/null +++ b/include/libusb-1.0/libusb.h @@ -0,0 +1,1322 @@ +/* + * Public libusb header file + * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 __LIBUSB_H__ +#define __LIBUSB_H__ + +/* MSVC doesn't like inline, but does accept __inline ?? */ +#ifdef _MSC_VER +#define inline __inline +#endif + +#include <stdint.h> +#include <sys/types.h> +#include <time.h> +#include <limits.h> + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) +#include <sys/time.h> +#endif + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * 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__) +#include <windows.h> +#if defined(interface) +#undef interface +#endif +#endif + +/** \def LIBUSB_CALL + * \ingroup misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not <em>one true calling + * convention</em> (calling convention: the manner in which parameters are + * passed to funcions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the <tt>stdcall</tt> convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** \def libusb_cpu_to_le16 + * \ingroup misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = x >> 8; + _tmp.b8[0] = x & 0xff; + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Printer dclass */ + LIBUSB_CLASS_PRINTER = 7, + + /** Picture transfer protocol class */ + LIBUSB_CLASS_PTP = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03 +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2 +}; + +/** \ingroup desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully opreation. Expressed in units + * of 2 mA. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_device_ref() and + * libusb_device_unref(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can use libusb_strerror() to retrieve a short string description of + * a libusb_error enumeration value. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /** Other error */ + LIBUSB_ERROR_OTHER = -99 + + /* IMPORTANT: when adding new values to this enum, remember to + update the libusb_strerror() function implementation! */ +}; + +/** \ingroup asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW +}; + +/** \ingroup asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer() */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2 +}; + +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, + int interface_number); + +/* async I/O */ + +/** \ingroup asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +/** \ingroup asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * <tt>buffer + (packet_size * packet)</tt> + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + (uint16_t) length, 1000); +} + +/** \ingroup desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from <poll.h>. POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libusb-1.0/libusbi.h b/include/libusb-1.0/libusbi.h new file mode 100644 index 0000000..9b2fa70 --- /dev/null +++ b/include/libusb-1.0/libusbi.h @@ -0,0 +1,872 @@ +/* + * Internal header for libusb + * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 __LIBUSBI_H__ +#define __LIBUSBI_H__ + +#include <config.h> + +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +#include <libusb.h> + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY + +#define DEVICE_DESC_LENGTH 18 + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) + +enum usbi_log_level { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, +}; + +void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, ...); + +#if !defined(_MSC_VER) || _MSC_VER > 1200 + +#ifdef ENABLE_LOGGING +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) +#else +#define _usbi_log(ctx, level, ...) +#endif + +#ifdef ENABLE_DEBUG_LOGGING +#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) +#else +#define usbi_dbg(...) +#endif + +#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) + +#else /* !defined(_MSC_VER) || _MSC_VER > 1200 */ + +void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, va_list args); + +#ifdef ENABLE_LOGGING +#define LOG_BODY(ctxt, level) \ +{ \ + va_list args; \ + va_start (args, format); \ + usbi_log_v(ctxt, level, "", format, args); \ + va_end(args); \ +} +#else +#define LOG_BODY(ctxt, level) { } +#endif + +static inline void usbi_info(struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_INFO) +static inline void usbi_warn(struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_WARNING) +static inline void usbi_err( struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_ERROR) + +static inline void usbi_dbg(const char *format, ...) +#ifdef ENABLE_DEBUG_LOGGING + LOG_BODY(NULL,LOG_LEVEL_DEBUG) +#else +{ } +#endif + +#endif /* !defined(_MSC_VER) || _MSC_VER > 1200 */ + +#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) \ + (TRANSFER_CTX(__USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +/* Internal abstractions for thread synchronization and poll */ +#if defined(THREADS_POSIX) +#include <os/threads_posix.h> +#elif defined(OS_WINDOWS) +#include <os/threads_windows.h> +#endif + +#if defined(OS_LINUX) || defined(OS_DARWIN) +#include <os/poll_posix.h> +#elif defined(OS_WINDOWS) +#include <os/poll_windows.h> +#endif + +extern struct libusb_context *usbi_default_context; + +struct libusb_context { + int debug; + int debug_fixed; + + /* internal control pipe, used for interrupting event handling when + * something needs to modify poll fds. */ + int ctrl_pipe[2]; + + struct list_head usb_devs; + usbi_mutex_t usb_devs_lock; + + /* A list of open handles. Backends are free to traverse this if required. + */ + struct list_head open_devs; + usbi_mutex_t open_devs_lock; + + /* this is a list of in-flight transfer handles, sorted by timeout + * expiration. URBs to timeout the soonest are placed at the beginning of + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + usbi_mutex_t flying_transfers_lock; + + /* list of poll fds */ + struct list_head pollfds; + usbi_mutex_t pollfds_lock; + + /* a counter that is set when we want to interrupt event handling, in order + * to modify the poll fd set. and a lock to protect it. */ + unsigned int pollfd_modify; + usbi_mutex_t pollfd_modify_lock; + + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + +#ifdef USBI_TIMERFD_AVAILABLE + /* used for timeout handling, if supported by OS. + * this timerfd is maintained to trigger on the next pending timeout */ + int timerfd; +#endif +}; + +#ifdef USBI_TIMERFD_AVAILABLE +#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) +#else +#define usbi_using_timerfd(ctx) (0) +#endif + +struct libusb_device { + /* lock protects refcnt, everything else is finalized at initialization + * time */ + usbi_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t device_address; + uint8_t num_configurations; + + struct list_head list; + unsigned long session_data; + unsigned char os_priv[0]; +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + unsigned char os_priv[0]; +}; + +#define USBI_TRANSFER_TIMED_OUT (1<<0) + +enum { + USBI_CLOCK_MONOTONIC, + USBI_CLOCK_REALTIME +}; + +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ + +struct usbi_transfer { + int num_iso_packets; + struct list_head list; + struct timeval timeout; + int transferred; + uint8_t flags; + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). */ + usbi_mutex_t lock; +}; + +#define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + +/* bus structures */ + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* shared data and functions */ + +int usbi_io_init(struct libusb_context *ctx); +void usbi_io_exit(struct libusb_context *ctx); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_device_handle *handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); + +int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, + int host_endian); +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx); + +/* polling */ + +struct usbi_pollfd { + /* must come first */ + struct libusb_pollfd pollfd; + + struct list_head list; +}; + +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); +void usbi_remove_pollfd(struct libusb_context *ctx, int fd); +void usbi_fd_notification(struct libusb_context *ctx); + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices[0]; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * prior to use. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * that was set up by init. + * + * This function is called when the user deinitializes the library. + */ + void (*exit)(void); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a libusb_device + * structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, we can now continue. + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_pollfd() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_pollfd() to inform + * libusb of any file descriptors associated with this device that should + * no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *handle); + + /* Retrieve the device descriptor from a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. Alternatively, you may be able + * to retrieve it from a kernel interface (some Linux setups can do this) + * still without generating bus I/O. + * + * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into + * buffer, which is guaranteed to be big enough. + * + * This function is called when sanity-checking a device before adding + * it to the list of discovered devices, and also when the user requests + * to read the device descriptor. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_device_descriptor)(struct libusb_device *device, + unsigned char *buffer, int *host_endian); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *handle, int *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applicatiosn can use the interface because + * we now "own" it. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *handle, + int interface_number, int altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. + * + * If possible, the handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the resetted + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *handle); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *handle, + int interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events. This involves monitoring any active + * transfers and processing their completion or cancellation. + * + * The function is passed an array of pollfd structures (size nfds) + * as a result of the poll() system call. The num_ready parameter + * indicates the number of file descriptors that have reported events + * (i.e. the poll() return value). This should be enough information + * for you to determine which actions need to be taken on the currently + * active transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + struct pollfd *fds, nfds_t nfds, int num_ready); + + /* Get time from specified clock. At least two clocks must be implemented + by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. + + Description of clocks: + USBI_CLOCK_REALTIME : clock returns time since system epoch. + USBI_CLOCK_MONOTONIC: clock returns time since unspecified start + time (usually boot). + */ + int (*clock_gettime)(int clkid, struct timespec *tp); + +#ifdef USBI_TIMERFD_AVAILABLE + /* clock ID of the clock that should be used for timerfd */ + clockid_t (*get_timerfd_clockid)(void); +#endif + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + */ + size_t transfer_priv_size; + + /* Mumber of additional bytes for os_priv for each iso packet. + * Can your backend use this? */ + /* FIXME: linux can't use this any more. if other OS's cannot either, + * then remove this */ + size_t add_iso_packet_size; +}; + +extern const struct usbi_os_backend * const usbi_backend; + +extern const struct usbi_os_backend linux_usbfs_backend; +extern const struct usbi_os_backend darwin_backend; +extern const struct usbi_os_backend windows_backend; + +#endif + diff --git a/include/libusb-1.0/os/config.h b/include/libusb-1.0/os/config.h new file mode 100644 index 0000000..643d4af --- /dev/null +++ b/include/libusb-1.0/os/config.h @@ -0,0 +1,105 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Default visibility */ +#define DEFAULT_VISIBILITY __attribute__((visibility("default"))) + +/* Debug message logging */ +/* #undef ENABLE_DEBUG_LOGGING */ + +/* Message logging */ +#define ENABLE_LOGGING 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if the system has the type `struct timespec'. */ +#define HAVE_STRUCT_TIMESPEC 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Darwin backend */ +#define OS_DARWIN + +/* Linux backend */ +/* #undef OS_LINUX */ + +/* Windows backend */ +/* #undef OS_WINDOWS */ + +/* Name of package */ +#define PACKAGE "libusb" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libusb-devel@lists.sourceforge.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libusb" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libusb 1.0.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libusb" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Use Posix Threads */ +#define THREADS_POSIX + +/* Backend handles timeout */ +#define USBI_OS_HANDLES_TIMEOUT + +/* timerfd headers available */ +/* #undef USBI_TIMERFD_AVAILABLE */ + +/* Version number of package */ +#define VERSION "1.0.8" + +/* Use GNU extensions */ +#define _GNU_SOURCE + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/include/libusb-1.0/os/darwin_usb.c b/include/libusb-1.0/os/darwin_usb.c new file mode 100644 index 0000000..646c938 --- /dev/null +++ b/include/libusb-1.0/os/darwin_usb.c @@ -0,0 +1,1545 @@ +/* + * darwin backend for libusb 1.0 + * Copyright (C) 2008-2010 Nathan Hjelm <hjelmn@users.sourceforge.net> + * + * 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 <config.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <mach/clock.h> +#include <mach/clock_types.h> +#include <mach/mach_host.h> + +#include <mach/mach_port.h> +#include <IOKit/IOCFBundle.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/IOCFPlugIn.h> + +#include "darwin_usb.h" + +static mach_port_t libusb_darwin_mp = 0; /* master port */ +static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */ +static int initCount = 0; + +/* async event thread */ +static pthread_t libusb_darwin_at; + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian); +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int darwin_reset_device(struct libusb_device_handle *dev_handle); +static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0); + +static const char *darwin_error_str (int result) { + switch (result) { + case kIOReturnSuccess: + return "no error"; + case kIOReturnNotOpen: + return "device not opened for exclusive access"; + case kIOReturnNoDevice: + return "no connection to an IOService"; + case kIOUSBNoAsyncPortErr: + return "no async port has been opened for interface"; + case kIOReturnExclusiveAccess: + return "another process has device opened for exclusive access"; + case kIOUSBPipeStalled: + return "pipe is stalled"; + case kIOReturnError: + return "could not establish a connection to the Darwin kernel"; + case kIOUSBTransactionTimeout: + return "transaction timed out"; + case kIOReturnBadArgument: + return "invalid argument"; + case kIOReturnAborted: + return "transaction aborted"; + case kIOReturnNotResponding: + return "device not responding"; + case kIOReturnOverrun: + return "data overrun"; + case kIOReturnCannotWire: + return "physical memory can not be wired down"; + default: + return "unknown error"; + } +} + +static int darwin_to_libusb (int result) { + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_SUCCESS; + case kIOReturnNotOpen: + case kIOReturnNoDevice: + return LIBUSB_ERROR_NO_DEVICE; + case kIOReturnExclusiveAccess: + return LIBUSB_ERROR_ACCESS; + case kIOUSBPipeStalled: + return LIBUSB_ERROR_PIPE; + case kIOReturnBadArgument: + return LIBUSB_ERROR_INVALID_PARAM; + case kIOUSBTransactionTimeout: + return LIBUSB_ERROR_TIMEOUT; + case kIOReturnNotResponding: + case kIOReturnAborted: + case kIOReturnError: + case kIOUSBNoAsyncPortErr: + default: + return LIBUSB_ERROR_OTHER; + } +} + + +static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + + /* current interface */ + struct __darwin_interface *cInterface; + + int8_t i, iface; + + usbi_info (HANDLE_CTX(dev_handle), "converting ep address 0x%02x to pipeRef and interface", ep); + + for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { + cInterface = &priv->interfaces[iface]; + + if (dev_handle->claimed_interfaces & (1 << iface)) { + for (i = 0 ; i < cInterface->num_endpoints ; i++) { + if (cInterface->endpoint_addrs[i] == ep) { + *pipep = i + 1; + *ifcp = iface; + usbi_info (HANDLE_CTX(dev_handle), "pipe %d on interface %d matches", *pipep, *ifcp); + return 0; + } + } + } + } + + /* No pipe found with the correct endpoint address */ + usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep); + + return -1; +} + +static int usb_setup_device_iterator (io_iterator_t *deviceIterator) { + return IOServiceGetMatchingServices(libusb_darwin_mp, IOServiceMatching(kIOUSBDeviceClassName), deviceIterator); +} + +static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) { + io_cf_plugin_ref_t *plugInInterface = NULL; + usb_device_t **device; + io_service_t usbDevice; + long result; + SInt32 score; + + if (!IOIteratorIsValid (deviceIterator)) + return NULL; + + + while ((usbDevice = IOIteratorNext(deviceIterator))) { + result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); + if (kIOReturnSuccess == result && plugInInterface) + break; + + usbi_dbg ("libusb/darwin.c usb_get_next_device: could not set up plugin for service: %s\n", darwin_error_str (result)); + } + + if (!usbDevice) + return NULL; + + (void)IOObjectRelease(usbDevice); + (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), + (LPVOID)&device); + + (*plugInInterface)->Stop(plugInInterface); + IODestroyPlugInInterface (plugInInterface); + + /* get the location from the device */ + if (locationp) + (*(device))->GetLocationID(device, locationp); + + return device; +} + +static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***darwin_device) { + kern_return_t kresult; + UInt32 location; + io_iterator_t deviceIterator; + + kresult = usb_setup_device_iterator (&deviceIterator); + if (kresult) + return kresult; + + /* This port of libusb uses locations to keep track of devices. */ + while ((*darwin_device = usb_get_next_device (deviceIterator, &location)) != NULL) { + if (location == dev_location) + break; + + (**darwin_device)->Release(*darwin_device); + } + + IOObjectRelease (deviceIterator); + + if (!(*darwin_device)) + return kIOReturnNoDevice; + + return kIOReturnSuccess; +} + + + +static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { + struct libusb_context *ctx = (struct libusb_context *)ptr; + struct libusb_device_handle *handle; + struct darwin_device_priv *dpriv; + struct darwin_device_handle_priv *priv; + + io_service_t device; + long location; + CFTypeRef locationCF; + UInt32 message; + + usbi_info (ctx, "a device has been detached"); + + while ((device = IOIteratorNext (rem_devices)) != 0) { + /* get the location from the i/o registry */ + locationCF = IORegistryEntryCreateCFProperty (device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); + + CFNumberGetValue(locationCF, kCFNumberLongType, &location); + CFRelease (locationCF); + IOObjectRelease (device); + + usbi_mutex_lock(&ctx->open_devs_lock); + list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { + dpriv = (struct darwin_device_priv *)handle->dev->os_priv; + + /* the device may have been opened several times. write to each handle's event descriptor */ + if (dpriv->location == location && handle->os_priv) { + priv = (struct darwin_device_handle_priv *)handle->os_priv; + + message = MESSAGE_DEVICE_GONE; + write (priv->fds[1], &message, sizeof (message)); + } + } + + usbi_mutex_unlock(&ctx->open_devs_lock); + } +} + +static void darwin_clear_iterator (io_iterator_t iter) { + io_service_t device; + + while ((device = IOIteratorNext (iter)) != 0) + IOObjectRelease (device); +} + +static void *event_thread_main (void *arg0) { + IOReturn kresult; + struct libusb_context *ctx = (struct libusb_context *)arg0; + + /* hotplug (device removal) source */ + CFRunLoopSourceRef libusb_notification_cfsource; + io_notification_port_t libusb_notification_port; + io_iterator_t libusb_rem_device_iterator; + + usbi_info (ctx, "creating hotplug event source"); + + CFRetain (CFRunLoopGetCurrent ()); + + /* add the notification port to the run loop */ + libusb_notification_port = IONotificationPortCreate (libusb_darwin_mp); + libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port); + CFRunLoopAddSource(CFRunLoopGetCurrent (), libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* create notifications for removed devices */ + kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, + IOServiceMatching(kIOUSBDeviceClassName), + (IOServiceMatchingCallback)darwin_devices_detached, + (void *)ctx, &libusb_rem_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + + pthread_exit ((void *)kresult); + } + + /* arm notifiers */ + darwin_clear_iterator (libusb_rem_device_iterator); + + /* let the main thread know about the async runloop */ + libusb_darwin_acfl = CFRunLoopGetCurrent (); + + usbi_info (ctx, "thread ready to receive events"); + + /* run the runloop */ + CFRunLoopRun(); + + usbi_info (ctx, "thread exiting"); + + /* delete notification port */ + CFRunLoopSourceInvalidate (libusb_notification_cfsource); + IONotificationPortDestroy (libusb_notification_port); + + CFRelease (CFRunLoopGetCurrent ()); + + libusb_darwin_acfl = NULL; + + pthread_exit (0); +} + +static int darwin_init(struct libusb_context *ctx) { + IOReturn kresult; + + if (!(initCount++)) { + /* Create the master port for talking to IOKit */ + if (!libusb_darwin_mp) { + kresult = IOMasterPort (MACH_PORT_NULL, &libusb_darwin_mp); + + if (kresult != kIOReturnSuccess || !libusb_darwin_mp) + return darwin_to_libusb (kresult); + } + + pthread_create (&libusb_darwin_at, NULL, event_thread_main, (void *)ctx); + + while (!libusb_darwin_acfl) + usleep (10); + } + + return 0; +} + +static void darwin_exit (void) { + if (!(--initCount)) { + void *ret; + + /* stop the async runloop */ + CFRunLoopStop (libusb_darwin_acfl); + pthread_join (libusb_darwin_at, &ret); + + if (libusb_darwin_mp) + mach_port_deallocate(mach_task_self(), libusb_darwin_mp); + + libusb_darwin_mp = 0; + } +} + +static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + + /* return cached copy */ + memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH); + + *host_endian = 0; + + return 0; +} + +static int get_configuration_index (struct libusb_device *dev, int config_value) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + UInt8 i, numConfig; + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + + /* is there a simpler way to determine the index? */ + kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + for (i = 0 ; i < numConfig ; i++) { + (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc); + + if (desc->bConfigurationValue == config_value) + return i; + } + + /* configuration not found */ + return LIBUSB_ERROR_OTHER; +} + +static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + int config_index; + + if (0 == priv->active_config) + return LIBUSB_ERROR_INVALID_PARAM; + + config_index = get_configuration_index (dev, priv->active_config); + if (config_index < 0) + return config_index; + + return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian); +} + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + usb_device_t **device = NULL; + + if (!priv) + return LIBUSB_ERROR_OTHER; + + if (!priv->device) { + kresult = darwin_get_device (priv->location, &device); + if (kresult || !device) { + usbi_err (DEVICE_CTX (dev), "could not find device: %s", darwin_error_str (kresult)); + + return darwin_to_libusb (kresult); + } + + /* don't have to open the device to get a config descriptor */ + } else + device = priv->device; + + kresult = (*device)->GetConfigurationDescriptorPtr (device, config_index, &desc); + if (kresult == kIOReturnSuccess) { + /* copy descriptor */ + if (libusb_le16_to_cpu(desc->wTotalLength) < len) + len = libusb_le16_to_cpu(desc->wTotalLength); + + memmove (buffer, desc, len); + + /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */ + *host_endian = 0; + } + + if (!priv->device) + (*device)->Release (device); + + return darwin_to_libusb (kresult); +} + +/* check whether the os has configured the device */ +static int darwin_check_configuration (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **darwin_device) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + + IOUSBConfigurationDescriptorPtr configDesc; + IOUSBFindInterfaceRequest request; + kern_return_t kresult; + io_iterator_t interface_iterator; + io_service_t firstInterface; + + if (priv->dev_descriptor.bNumConfigurations < 1) { + usbi_err (ctx, "device has no configurations"); + return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */ + } + + /* find the first configuration */ + kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc); + priv->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1; + + /* check if the device is already configured. there is probably a better way than iterating over the + to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices + might lock up on the device request) */ + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult) + return darwin_to_libusb (kresult); + + /* iterate once */ + firstInterface = IOIteratorNext(interface_iterator); + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + if (firstInterface) { + IOObjectRelease (firstInterface); + + /* device is configured */ + if (priv->dev_descriptor.bNumConfigurations == 1) + /* to avoid problems with some devices get the configurations value from the configuration descriptor */ + priv->active_config = priv->first_config; + else + /* devices with more than one configuration should work with GetConfiguration */ + (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config); + } else + /* not configured */ + priv->active_config = 0; + + usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config); + + return 0; +} + +static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) { + struct darwin_device_priv *priv; + struct libusb_device *dev; + struct discovered_devs *discdevs; + UInt16 address, idVendor, idProduct; + UInt8 bDeviceClass, bDeviceSubClass; + IOUSBDevRequest req; + int ret = 0, need_unref = 0; + + do { + dev = usbi_get_device_by_session_id(ctx, locationID); + if (!dev) { + usbi_info (ctx, "allocating new device for location 0x%08x", locationID); + dev = usbi_alloc_device(ctx, locationID); + need_unref = 1; + } else + usbi_info (ctx, "using existing device for location 0x%08x", locationID); + + if (!dev) { + ret = LIBUSB_ERROR_NO_MEM; + break; + } + + priv = (struct darwin_device_priv *)dev->os_priv; + + /* Set up request for device descriptor */ + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = kUSBDeviceDesc << 8; + req.wIndex = 0; + req.wLength = sizeof(IOUSBDeviceDescriptor); + req.pData = &(priv->dev_descriptor); + + (*(device))->GetDeviceAddress (device, (USBDeviceAddress *)&address); + (*(device))->GetDeviceProduct (device, &idProduct); + (*(device))->GetDeviceVendor (device, &idVendor); + (*(device))->GetDeviceClass (device, &bDeviceClass); + (*(device))->GetDeviceSubClass (device, &bDeviceSubClass); + + /**** retrieve device descriptors ****/ + /* according to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some + * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request */ + ret = (*(device))->DeviceRequest (device, &req); + if (ret != kIOReturnSuccess) { + int try_unsuspend = 1; +#if DeviceVersion >= 320 + UInt32 info; + + /* device may be suspended. unsuspend it and try again */ + /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ + (void)(*device)->GetUSBDeviceInformation (device, &info); + + try_unsuspend = info & (1 << kUSBInformationDeviceIsSuspendedBit); +#endif + + /* the device should be open before to device is unsuspended */ + (void) (*device)->USBDeviceOpenSeize(device); + + if (try_unsuspend) { + /* resume the device */ + (void)(*device)->USBDeviceSuspend (device, 0); + + ret = (*(device))->DeviceRequest (device, &req); + + /* resuspend the device */ + (void)(*device)->USBDeviceSuspend (device, 1); + } + + (*device)->USBDeviceClose (device); + } + + if (ret != kIOReturnSuccess) { + usbi_warn (ctx, "could not retrieve device descriptor: %s. skipping device", darwin_error_str (ret)); + ret = -1; + break; + } + + /**** end: retrieve device descriptors ****/ + + /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ + if (libusb_le16_to_cpu (priv->dev_descriptor.idProduct) != idProduct) { + /* not a valid device */ + usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device", + idProduct, libusb_le16_to_cpu (priv->dev_descriptor.idProduct)); + ret = -1; + break; + } + + dev->bus_number = locationID >> 24; + dev->device_address = address; + + /* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */ + ret = darwin_check_configuration (ctx, dev, device); + if (ret < 0) + break; + + /* save our location, we'll need this later */ + priv->location = locationID; + snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, idVendor, idProduct, bDeviceClass, bDeviceSubClass); + + ret = usbi_sanitize_device (dev); + if (ret < 0) + break; + + /* append the device to the list of discovered devices */ + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) { + ret = LIBUSB_ERROR_NO_MEM; + break; + } + + *_discdevs = discdevs; + + usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path); + } while (0); + + if (need_unref) + libusb_unref_device(dev); + + return ret; +} + +static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) { + io_iterator_t deviceIterator; + usb_device_t **device; + kern_return_t kresult; + UInt32 location; + + if (!libusb_darwin_mp) + return LIBUSB_ERROR_INVALID_PARAM; + + kresult = usb_setup_device_iterator (&deviceIterator); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { + (void) process_new_device (ctx, device, location, _discdevs); + + (*(device))->Release(device); + } + + IOObjectRelease(deviceIterator); + + return 0; +} + +static int darwin_open (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + usb_device_t **darwin_device; + IOReturn kresult; + + if (0 == dpriv->open_count) { + kresult = darwin_get_device (dpriv->location, &darwin_device); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "could not find device: %s", darwin_error_str (kresult)); + return darwin_to_libusb (kresult); + } + + dpriv->device = darwin_device; + + /* try to open the device */ + kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device); + + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); + + switch (kresult) { + case kIOReturnExclusiveAccess: + /* it is possible to perform some actions on a device that is not open so do not return an error */ + priv->is_open = 0; + + break; + default: + (*(dpriv->device))->Release (dpriv->device); + dpriv->device = NULL; + return darwin_to_libusb (kresult); + } + } else { + priv->is_open = 1; + + /* create async event source */ + kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource); + + CFRetain (libusb_darwin_acfl); + + /* add the cfSource to the aync run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); + } + } + + /* device opened successfully */ + dpriv->open_count++; + + /* create a file descriptor for notifications */ + pipe (priv->fds); + + /* set the pipe to be non-blocking */ + fcntl (priv->fds[1], F_SETFD, O_NONBLOCK); + + usbi_add_pollfd(HANDLE_CTX(dev_handle), priv->fds[0], POLLIN); + + usbi_info (HANDLE_CTX (dev_handle), "device open for access"); + + return 0; +} + +static void darwin_close (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + IOReturn kresult; + int i; + + if (dpriv->open_count == 0) { + /* something is probably very wrong if this is the case */ + usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!\n"); + return; + } + + dpriv->open_count--; + + /* make sure all interfaces are released */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + libusb_release_interface (dev_handle, i); + + if (0 == dpriv->open_count) { + if (priv->is_open) { + /* delete the device's async event source */ + if (priv->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode); + CFRelease (priv->cfSource); + } + + /* close the device */ + kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device); + if (kresult) { + /* Log the fact that we had a problem closing the file, however failing a + * close isn't really an error, so return success anyway */ + usbi_err (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); + } + } + + kresult = (*(dpriv->device))->Release(dpriv->device); + if (kresult) { + /* Log the fact that we had a problem closing the file, however failing a + * close isn't really an error, so return success anyway */ + usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); + } + + dpriv->device = NULL; + } + + /* file descriptors are maintained per-instance */ + usbi_remove_pollfd (HANDLE_CTX (dev_handle), priv->fds[0]); + close (priv->fds[1]); + close (priv->fds[0]); + + priv->fds[0] = priv->fds[1] = -1; +} + +static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + + *config = (int) dpriv->active_config; + + return 0; +} + +static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + IOReturn kresult; + int i; + + /* Setting configuration will invalidate the interface, so we need + to reclaim it. First, dispose of existing interfaces, if any. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + darwin_release_interface (dev_handle, i); + + kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* Reclaim any interfaces. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + darwin_claim_interface (dev_handle, i); + + dpriv->active_config = config; + + return 0; +} + +static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { + IOUSBFindInterfaceRequest request; + uint8_t current_interface; + kern_return_t kresult; + io_iterator_t interface_iterator; + + *usbInterfacep = IO_OBJECT_NULL; + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult) + return kresult; + + for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) { + *usbInterfacep = IOIteratorNext(interface_iterator); + if (current_interface != ifc) + (void) IOObjectRelease (*usbInterfacep); + } + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + return 0; +} + +static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + + /* current interface */ + struct __darwin_interface *cInterface = &priv->interfaces[iface]; + + kern_return_t kresult; + + u_int8_t numep, direction, number; + u_int8_t dont_care1, dont_care3; + u_int16_t dont_care2; + int i; + + usbi_info (HANDLE_CTX (dev_handle), "building table of endpoints."); + + /* retrieve the total number of endpoints on this interface */ + kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* iterate through pipe references */ + for (i = 1 ; i <= numep ; i++) { + kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, + &dont_care2, &dont_care3); + + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "error getting pipe information for pipe %d: %s", i, darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); + } + + usbi_info (HANDLE_CTX (dev_handle), "interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number); + + cInterface->endpoint_addrs[i - 1] = ((direction << 7 & LIBUSB_ENDPOINT_DIR_MASK) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); + } + + cInterface->num_endpoints = numep; + + return 0; +} + +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + io_service_t usbInterface = IO_OBJECT_NULL; + IOReturn kresult; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + + /* current interface */ + struct __darwin_interface *cInterface = &priv->interfaces[iface]; + + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* make sure we have an interface */ + if (!usbInterface && dpriv->first_config != 0) { + usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config); + + /* set the configuration */ + kresult = darwin_set_configuration (dev_handle, dpriv->first_config); + if (kresult != LIBUSB_SUCCESS) { + usbi_err (HANDLE_CTX (dev_handle), "could not set configuration"); + return kresult; + } + + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + } + + if (!usbInterface) { + usbi_err (HANDLE_CTX (dev_handle), "interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* get an interface to the device's interface */ + kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, &score); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + if (!plugInInterface) { + usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* ignore release error */ + (void)IOObjectRelease (usbInterface); + + /* Do the actual claim */ + kresult = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID)&cInterface->interface); + if (kresult || !cInterface->interface) { + usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* We no longer need the intermediate plug-in */ + (*plugInInterface)->Release(plugInInterface); + + /* claim the interface */ + kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* update list of endpoints */ + kresult = get_endpoints (dev_handle, iface); + if (kresult) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + return kresult; + } + + cInterface->cfSource = NULL; + + /* create async event source */ + kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "could not create async event source"); + + /* can't continue without an async event source */ + (void)darwin_release_interface (dev_handle, iface); + + return darwin_to_libusb (kresult); + } + + /* add the cfSource to the async thread's run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + + usbi_info (HANDLE_CTX (dev_handle), "interface opened"); + + return 0; +} + +static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + IOReturn kresult; + + /* current interface */ + struct __darwin_interface *cInterface = &priv->interfaces[iface]; + + /* Check to see if an interface is open */ + if (!cInterface->interface) + return LIBUSB_SUCCESS; + + /* clean up endpoint data */ + cInterface->num_endpoints = 0; + + /* delete the interface's async event source */ + if (cInterface->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + CFRelease (cInterface->cfSource); + } + + kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); + if (kresult) + usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); + + kresult = (*(cInterface->interface))->Release(cInterface->interface); + if (kresult != kIOReturnSuccess) + usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); + + cInterface->interface = IO_OBJECT_NULL; + + return darwin_to_libusb (kresult); +} + +static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + IOReturn kresult; + + /* current interface */ + struct __darwin_interface *cInterface = &priv->interfaces[iface]; + + if (!cInterface->interface) + return LIBUSB_ERROR_NO_DEVICE; + + kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting); + if (kresult != kIOReturnSuccess) + darwin_reset_device (dev_handle); + + /* update list of endpoints */ + kresult = get_endpoints (dev_handle, iface); + if (kresult) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + return kresult; + } + + return darwin_to_libusb (kresult); +} + +static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + + /* current interface */ + struct __darwin_interface *cInterface; + uint8_t pipeRef, iface; + IOReturn kresult; + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, &iface) != 0) { + usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + cInterface = &priv->interfaces[iface]; + +#if (InterfaceVersion < 190) + kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef); +#else + /* newer versions of darwin support clearing additional bits on the device's endpoint */ + kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); +#endif + if (kresult) + usbi_err (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_reset_device(struct libusb_device_handle *dev_handle) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + IOReturn kresult; + + kresult = (*(dpriv->device))->ResetDevice (dpriv->device); + if (kresult) + usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + io_service_t usbInterface; + CFTypeRef driver; + IOReturn kresult; + + kresult = darwin_get_interface (dpriv->device, interface, &usbInterface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); + } + + driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0); + IOObjectRelease (usbInterface); + + if (driver) { + CFRelease (driver); + + return 1; + } + + /* no driver */ + return 0; +} + +/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */ +static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void darwin_destroy_device(struct libusb_device *dev) { +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; + + IOReturn ret; + uint8_t is_read; /* 0 = we're reading, 1 = we're writing */ + uint8_t transferType; + /* None of the values below are used in libusb for bulk transfers */ + uint8_t direction, number, interval, pipeRef, iface; + uint16_t maxPacketSize; + + struct __darwin_interface *cInterface; + + /* are we reading or writing? */ + is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + cInterface = &priv->interfaces[iface]; + + (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, + &transferType, &maxPacketSize, &interval); + + /* submit the request */ + /* timeouts are unavailable on interrupt endpoints */ + if (transferType == kUSBInterrupt) { + if (is_read) + ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, darwin_async_io_callback, itransfer); + else + ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, darwin_async_io_callback, itransfer); + } else { + if (is_read) + ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); + else + ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); + } + + if (ret) + usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", is_read ? "In" : "Out", + darwin_error_str(ret), ret); + + return darwin_to_libusb (ret); +} + +static int submit_iso_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; + + IOReturn kresult; + uint8_t is_read; /* 0 = we're writing, 1 = we're reading */ + uint8_t pipeRef, iface; + UInt64 frame; + AbsoluteTime atTime; + int i; + + struct __darwin_interface *cInterface; + + /* are we reading or writing? */ + is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + /* construct an array of IOUSBIsocFrames */ + tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame)); + if (!tpriv->isoc_framelist) + return LIBUSB_ERROR_NO_MEM; + + /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ + for (i = 0 ; i < transfer->num_iso_packets ; i++) + tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length; + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + cInterface = &priv->interfaces[iface]; + + /* Last but not least we need the bus frame number */ + kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); + if (kresult) { + usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult); + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + + return darwin_to_libusb (kresult); + } + + /* schedule for a frame a little in the future */ + frame += 2; + + /* submit the request */ + if (is_read) + kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + else + kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + + if (kresult != kIOReturnSuccess) { + usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", is_read ? "In" : "Out", + darwin_error_str(kresult)); + free (tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } + + return darwin_to_libusb (kresult); +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer; + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv; + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + IOReturn kresult; + + bzero(&tpriv->req, sizeof(tpriv->req)); + + /* IOUSBDeviceInterface expects the request in cpu endianess */ + tpriv->req.bmRequestType = setup->bmRequestType; + tpriv->req.bRequest = setup->bRequest; + /* these values should be in bus order from libusb_fill_control_setup */ + tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue); + tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex); + tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength); + /* data is stored after the libusb control block */ + tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + tpriv->req.completionTimeout = transfer->timeout; + tpriv->req.noDataTimeout = transfer->timeout; + + /* all transfers in libusb-1.0 are async */ + kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); + + if (kresult != kIOReturnSuccess) + usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_submit_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int cancel_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv; + IOReturn kresult; + + usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions control pipe"); + + kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device); + + return darwin_to_libusb (kresult); +} + +static int darwin_abort_transfers (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; + struct __darwin_interface *cInterface; + uint8_t pipeRef, iface; + IOReturn kresult; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + cInterface = &priv->interfaces[iface]; + + usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef); + + /* abort transactions */ + (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef); + + usbi_info (ITRANSFER_CTX (itransfer), "calling clear pipe stall to clear the data toggle bit"); + + /* clear the data toggle bit */ +#if (InterfaceVersion < 190) + kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef); +#else + /* newer versions of darwin support clearing additional bits on the device's endpoint */ + kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); +#endif + + return darwin_to_libusb (kresult); +} + +static int darwin_cancel_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return cancel_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return darwin_abort_transfers (itransfer); + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) { + free (tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } +} + +static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) { + struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon; + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; + UInt32 message; + + usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed"); + + /* send a completion message to the device's file descriptor */ + message = MESSAGE_ASYNC_IO_COMPLETE; + write (priv->fds[1], &message, sizeof (message)); + write (priv->fds[1], &itransfer, sizeof (itransfer)); + write (priv->fds[1], &result, sizeof (IOReturn)); + write (priv->fds[1], &arg0, sizeof (UInt32)); +} + +static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) { + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_TRANSFER_COMPLETED; + case kIOReturnAborted: + return LIBUSB_TRANSFER_CANCELLED; + case kIOUSBPipeStalled: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: pipe is stalled"); + return LIBUSB_TRANSFER_STALL; + case kIOReturnOverrun: + usbi_err (ITRANSFER_CTX (itransfer), "transfer error: data overrun"); + return LIBUSB_TRANSFER_OVERFLOW; + case kIOUSBTransactionTimeout: + usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out"); + return LIBUSB_TRANSFER_TIMED_OUT; + default: + usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); + return LIBUSB_TRANSFER_ERROR; + } +} + +static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; + int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type; + int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type; + int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type; + int i; + + if (!isIsoc && !isBulk && !isControl && !isInterrupt) { + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return; + } + + usbi_info (ITRANSFER_CTX (itransfer), "handling %s completion with kernel status %d", + isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result); + + if (kIOReturnSuccess == result || kIOReturnUnderrun == result) { + if (isIsoc && tpriv->isoc_framelist) { + /* copy isochronous results back */ + + for (i = 0; i < transfer->num_iso_packets ; i++) { + struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; + lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus); + lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; + } + } else if (!isIsoc) + itransfer->transferred += io_size; + } + + /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */ + usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, result)); +} + +static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds, int num_ready) { + struct usbi_transfer *itransfer; + UInt32 io_size; + IOReturn kresult; + int i = 0, ret; + UInt32 message; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + struct pollfd *pollfd = &fds[i]; + struct libusb_device_handle *handle; + struct darwin_device_handle_priv *hpriv = NULL; + + usbi_info (ctx, "checking fd %i with revents = %x", fds[i], pollfd->revents); + + if (!pollfd->revents) + continue; + + num_ready--; + list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { + hpriv = (struct darwin_device_handle_priv *)handle->os_priv; + if (hpriv->fds[0] == pollfd->fd) + break; + } + + if (!(pollfd->revents & POLLERR)) { + ret = read (hpriv->fds[0], &message, sizeof (message)); + if (ret < sizeof (message)) + continue; + } else + /* could not poll the device-- response is to delete the device (this seems a little heavy-handed) */ + message = MESSAGE_DEVICE_GONE; + + switch (message) { + case MESSAGE_DEVICE_GONE: + /* remove the device's async port from the runloop */ + if (hpriv->cfSource) { + if (libusb_darwin_acfl) + CFRunLoopRemoveSource (libusb_darwin_acfl, hpriv->cfSource, kCFRunLoopDefaultMode); + CFRelease (hpriv->cfSource); + hpriv->cfSource = NULL; + } + + usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fds[0]); + usbi_handle_disconnect(handle); + + /* done with this device */ + continue; + case MESSAGE_ASYNC_IO_COMPLETE: + read (hpriv->fds[0], &itransfer, sizeof (itransfer)); + read (hpriv->fds[0], &kresult, sizeof (IOReturn)); + read (hpriv->fds[0], &io_size, sizeof (UInt32)); + + darwin_handle_callback (itransfer, kresult, io_size); + break; + default: + usbi_err (ctx, "unknown message received from device pipe"); + } + } + + usbi_mutex_unlock(&ctx->open_devs_lock); + + return 0; +} + +static int darwin_clock_gettime(int clk_id, struct timespec *tp) { + mach_timespec_t sys_time; + clock_serv_t clock_ref; + + switch (clk_id) { + case USBI_CLOCK_REALTIME: + /* CLOCK_REALTIME represents time since the epoch */ + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock_ref); + break; + case USBI_CLOCK_MONOTONIC: + /* use system boot time as reference for the monotonic clock */ + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_ref); + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + clock_get_time (clock_ref, &sys_time); + + tp->tv_sec = sys_time.tv_sec; + tp->tv_nsec = sys_time.tv_nsec; + + return 0; +} + +const struct usbi_os_backend darwin_backend = { + .name = "Darwin", + .init = darwin_init, + .exit = darwin_exit, + .get_device_list = darwin_get_device_list, + .get_device_descriptor = darwin_get_device_descriptor, + .get_active_config_descriptor = darwin_get_active_config_descriptor, + .get_config_descriptor = darwin_get_config_descriptor, + + .open = darwin_open, + .close = darwin_close, + .get_configuration = darwin_get_configuration, + .set_configuration = darwin_set_configuration, + .claim_interface = darwin_claim_interface, + .release_interface = darwin_release_interface, + + .set_interface_altsetting = darwin_set_interface_altsetting, + .clear_halt = darwin_clear_halt, + .reset_device = darwin_reset_device, + + .kernel_driver_active = darwin_kernel_driver_active, + .detach_kernel_driver = darwin_detach_kernel_driver, + .attach_kernel_driver = darwin_attach_kernel_driver, + + .destroy_device = darwin_destroy_device, + + .submit_transfer = darwin_submit_transfer, + .cancel_transfer = darwin_cancel_transfer, + .clear_transfer_priv = darwin_clear_transfer_priv, + + .handle_events = op_handle_events, + + .clock_gettime = darwin_clock_gettime, + + .device_priv_size = sizeof(struct darwin_device_priv), + .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), + .transfer_priv_size = sizeof(struct darwin_transfer_priv), + .add_iso_packet_size = 0, +}; + diff --git a/include/libusb-1.0/os/darwin_usb.h b/include/libusb-1.0/os/darwin_usb.h new file mode 100644 index 0000000..a71d464 --- /dev/null +++ b/include/libusb-1.0/os/darwin_usb.h @@ -0,0 +1,167 @@ +/* + * darwin backend for libusb 1.0 + * Copyright (C) 2008-2009 Nathan Hjelm <hjelmn@users.sourceforge.net> + * + * 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 + */ + +#if !defined(LIBUSB_DARWIN_H) +#define LIBUSB_DARWIN_H + +#include "libusbi.h" + +#include <IOKit/IOTypes.h> +#include <IOKit/IOCFBundle.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/IOCFPlugIn.h> + +/* IOUSBInterfaceInferface */ +#if defined (kIOUSBInterfaceInterfaceID300) + +#define usb_interface_t IOUSBInterfaceInterface300 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 +#define InterfaceVersion 300 + +#elif defined (kIOUSBInterfaceInterfaceID245) + +#define usb_interface_t IOUSBInterfaceInterface245 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 +#define InterfaceVersion 245 + +#elif defined (kIOUSBInterfaceInterfaceID220) + +#define usb_interface_t IOUSBInterfaceInterface220 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 +#define InterfaceVersion 220 + +#elif defined (kIOUSBInterfaceInterfaceID197) + +#define usb_interface_t IOUSBInterfaceInterface197 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 +#define InterfaceVersion 197 + +#elif defined (kIOUSBInterfaceInterfaceID190) + +#define usb_interface_t IOUSBInterfaceInterface190 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 +#define InterfaceVersion 190 + +#elif defined (kIOUSBInterfaceInterfaceID182) + +#define usb_interface_t IOUSBInterfaceInterface182 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 +#define InterfaceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +/* IOUSBDeviceInterface */ +#if defined (kIOUSBDeviceInterfaceID320) + +#define usb_device_t IOUSBDeviceInterface320 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 +#define DeviceVersion 320 + +#elif defined (kIOUSBDeviceInterfaceID300) + +#define usb_device_t IOUSBDeviceInterface300 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 +#define DeviceVersion 300 + +#elif defined (kIOUSBDeviceInterfaceID245) + +#define usb_device_t IOUSBDeviceInterface245 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 +#define DeviceVersion 245 + +#elif defined (kIOUSBDeviceInterfaceID197) + +#define usb_device_t IOUSBDeviceInterface197 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 +#define DeviceVersion 197 + +#elif defined (kIOUSBDeviceInterfaceID187) + +#define usb_device_t IOUSBDeviceInterface187 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 +#define DeviceVersion 187 + +#elif defined (kIOUSBDeviceInterfaceID182) + +#define usb_device_t IOUSBDeviceInterface182 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 +#define DeviceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +#if !defined(IO_OBJECT_NULL) +#define IO_OBJECT_NULL ((io_object_t) 0) +#endif + +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; +typedef IONotificationPortRef io_notification_port_t; + +/* private structures */ +struct darwin_device_priv { + IOUSBDeviceDescriptor dev_descriptor; + UInt32 location; + char sys_path[21]; + usb_device_t **device; + int open_count; + UInt8 first_config, active_config; +}; + +struct darwin_device_handle_priv { + int is_open; + CFRunLoopSourceRef cfSource; + int fds[2]; + + struct __darwin_interface { + usb_interface_t **interface; + uint8_t num_endpoints; + CFRunLoopSourceRef cfSource; + uint8_t endpoint_addrs[USB_MAXENDPOINTS]; + } interfaces[USB_MAXINTERFACES]; +}; + +struct darwin_transfer_priv { + /* Isoc */ + IOUSBIsocFrame *isoc_framelist; + + /* Control */ +#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) + IOUSBDevRequestTO req; +#else + IOUSBDevRequest req; +#endif + + /* Bulk */ +}; + +enum { + MESSAGE_DEVICE_GONE, + MESSAGE_ASYNC_IO_COMPLETE +}; + + + +#endif diff --git a/include/libusb-1.0/os/libusb.h b/include/libusb-1.0/os/libusb.h new file mode 100644 index 0000000..8dc3362 --- /dev/null +++ b/include/libusb-1.0/os/libusb.h @@ -0,0 +1,1322 @@ +/* + * Public libusb header file + * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 __LIBUSB_H__ +#define __LIBUSB_H__ + +/* MSVC doesn't like inline, but does accept __inline ?? */ +#ifdef _MSC_VER +#define inline __inline +#endif + +#include <stdint.h> +#include <sys/types.h> +#include <time.h> +#include <limits.h> + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) +#include <sys/time.h> +#endif + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * 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__) +#include <windows.h> +#if defined(interface) +#undef interface +#endif +#endif + +/** \def LIBUSB_CALL + * \ingroup misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not <em>one true calling + * convention</em> (calling convention: the manner in which parameters are + * passed to funcions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the <tt>stdcall</tt> convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** \def libusb_cpu_to_le16 + * \ingroup misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = x >> 8; + _tmp.b8[0] = x & 0xff; + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Printer dclass */ + LIBUSB_CLASS_PRINTER = 7, + + /** Picture transfer protocol class */ + LIBUSB_CLASS_PTP = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03 +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2 +}; + +/** \ingroup desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully opreation. Expressed in units + * of 2 mA. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_device_ref() and + * libusb_device_unref(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can use libusb_strerror() to retrieve a short string description of + * a libusb_error enumeration value. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /** Other error */ + LIBUSB_ERROR_OTHER = -99 + + /* IMPORTANT: when adding new values to this enum, remember to + update the libusb_strerror() function implementation! */ +}; + +/** \ingroup asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW +}; + +/** \ingroup asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer() */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2 +}; + +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, + int interface_number); + +/* async I/O */ + +/** \ingroup asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +/** \ingroup asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * <tt>buffer + (packet_size * packet)</tt> + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + (uint16_t) length, 1000); +} + +/** \ingroup desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from <poll.h>. POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libusb-1.0/os/libusbi.h b/include/libusb-1.0/os/libusbi.h new file mode 100644 index 0000000..9b2fa70 --- /dev/null +++ b/include/libusb-1.0/os/libusbi.h @@ -0,0 +1,872 @@ +/* + * Internal header for libusb + * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 __LIBUSBI_H__ +#define __LIBUSBI_H__ + +#include <config.h> + +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +#include <libusb.h> + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY + +#define DEVICE_DESC_LENGTH 18 + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) + +enum usbi_log_level { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, +}; + +void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, ...); + +#if !defined(_MSC_VER) || _MSC_VER > 1200 + +#ifdef ENABLE_LOGGING +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) +#else +#define _usbi_log(ctx, level, ...) +#endif + +#ifdef ENABLE_DEBUG_LOGGING +#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) +#else +#define usbi_dbg(...) +#endif + +#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) + +#else /* !defined(_MSC_VER) || _MSC_VER > 1200 */ + +void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, va_list args); + +#ifdef ENABLE_LOGGING +#define LOG_BODY(ctxt, level) \ +{ \ + va_list args; \ + va_start (args, format); \ + usbi_log_v(ctxt, level, "", format, args); \ + va_end(args); \ +} +#else +#define LOG_BODY(ctxt, level) { } +#endif + +static inline void usbi_info(struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_INFO) +static inline void usbi_warn(struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_WARNING) +static inline void usbi_err( struct libusb_context *ctx, const char *format, + ...) + LOG_BODY(ctx,LOG_LEVEL_ERROR) + +static inline void usbi_dbg(const char *format, ...) +#ifdef ENABLE_DEBUG_LOGGING + LOG_BODY(NULL,LOG_LEVEL_DEBUG) +#else +{ } +#endif + +#endif /* !defined(_MSC_VER) || _MSC_VER > 1200 */ + +#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) \ + (TRANSFER_CTX(__USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +/* Internal abstractions for thread synchronization and poll */ +#if defined(THREADS_POSIX) +#include <os/threads_posix.h> +#elif defined(OS_WINDOWS) +#include <os/threads_windows.h> +#endif + +#if defined(OS_LINUX) || defined(OS_DARWIN) +#include <os/poll_posix.h> +#elif defined(OS_WINDOWS) +#include <os/poll_windows.h> +#endif + +extern struct libusb_context *usbi_default_context; + +struct libusb_context { + int debug; + int debug_fixed; + + /* internal control pipe, used for interrupting event handling when + * something needs to modify poll fds. */ + int ctrl_pipe[2]; + + struct list_head usb_devs; + usbi_mutex_t usb_devs_lock; + + /* A list of open handles. Backends are free to traverse this if required. + */ + struct list_head open_devs; + usbi_mutex_t open_devs_lock; + + /* this is a list of in-flight transfer handles, sorted by timeout + * expiration. URBs to timeout the soonest are placed at the beginning of + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + usbi_mutex_t flying_transfers_lock; + + /* list of poll fds */ + struct list_head pollfds; + usbi_mutex_t pollfds_lock; + + /* a counter that is set when we want to interrupt event handling, in order + * to modify the poll fd set. and a lock to protect it. */ + unsigned int pollfd_modify; + usbi_mutex_t pollfd_modify_lock; + + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + +#ifdef USBI_TIMERFD_AVAILABLE + /* used for timeout handling, if supported by OS. + * this timerfd is maintained to trigger on the next pending timeout */ + int timerfd; +#endif +}; + +#ifdef USBI_TIMERFD_AVAILABLE +#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) +#else +#define usbi_using_timerfd(ctx) (0) +#endif + +struct libusb_device { + /* lock protects refcnt, everything else is finalized at initialization + * time */ + usbi_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t device_address; + uint8_t num_configurations; + + struct list_head list; + unsigned long session_data; + unsigned char os_priv[0]; +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + unsigned char os_priv[0]; +}; + +#define USBI_TRANSFER_TIMED_OUT (1<<0) + +enum { + USBI_CLOCK_MONOTONIC, + USBI_CLOCK_REALTIME +}; + +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ + +struct usbi_transfer { + int num_iso_packets; + struct list_head list; + struct timeval timeout; + int transferred; + uint8_t flags; + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). */ + usbi_mutex_t lock; +}; + +#define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + +/* bus structures */ + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* shared data and functions */ + +int usbi_io_init(struct libusb_context *ctx); +void usbi_io_exit(struct libusb_context *ctx); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_device_handle *handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); + +int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, + int host_endian); +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx); + +/* polling */ + +struct usbi_pollfd { + /* must come first */ + struct libusb_pollfd pollfd; + + struct list_head list; +}; + +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); +void usbi_remove_pollfd(struct libusb_context *ctx, int fd); +void usbi_fd_notification(struct libusb_context *ctx); + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices[0]; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * prior to use. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * that was set up by init. + * + * This function is called when the user deinitializes the library. + */ + void (*exit)(void); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a libusb_device + * structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, we can now continue. + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_pollfd() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_pollfd() to inform + * libusb of any file descriptors associated with this device that should + * no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *handle); + + /* Retrieve the device descriptor from a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. Alternatively, you may be able + * to retrieve it from a kernel interface (some Linux setups can do this) + * still without generating bus I/O. + * + * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into + * buffer, which is guaranteed to be big enough. + * + * This function is called when sanity-checking a device before adding + * it to the list of discovered devices, and also when the user requests + * to read the device descriptor. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_device_descriptor)(struct libusb_device *device, + unsigned char *buffer, int *host_endian); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *handle, int *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applicatiosn can use the interface because + * we now "own" it. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *handle, + int interface_number, int altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. + * + * If possible, the handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the resetted + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *handle); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *handle, + int interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events. This involves monitoring any active + * transfers and processing their completion or cancellation. + * + * The function is passed an array of pollfd structures (size nfds) + * as a result of the poll() system call. The num_ready parameter + * indicates the number of file descriptors that have reported events + * (i.e. the poll() return value). This should be enough information + * for you to determine which actions need to be taken on the currently + * active transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + struct pollfd *fds, nfds_t nfds, int num_ready); + + /* Get time from specified clock. At least two clocks must be implemented + by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. + + Description of clocks: + USBI_CLOCK_REALTIME : clock returns time since system epoch. + USBI_CLOCK_MONOTONIC: clock returns time since unspecified start + time (usually boot). + */ + int (*clock_gettime)(int clkid, struct timespec *tp); + +#ifdef USBI_TIMERFD_AVAILABLE + /* clock ID of the clock that should be used for timerfd */ + clockid_t (*get_timerfd_clockid)(void); +#endif + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + */ + size_t transfer_priv_size; + + /* Mumber of additional bytes for os_priv for each iso packet. + * Can your backend use this? */ + /* FIXME: linux can't use this any more. if other OS's cannot either, + * then remove this */ + size_t add_iso_packet_size; +}; + +extern const struct usbi_os_backend * const usbi_backend; + +extern const struct usbi_os_backend linux_usbfs_backend; +extern const struct usbi_os_backend darwin_backend; +extern const struct usbi_os_backend windows_backend; + +#endif + diff --git a/include/libusb-1.0/os/linux_usbfs.c b/include/libusb-1.0/os/linux_usbfs.c new file mode 100644 index 0000000..a44688d --- /dev/null +++ b/include/libusb-1.0/os/linux_usbfs.c @@ -0,0 +1,2220 @@ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 <config.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> + +#include "libusb.h" +#include "libusbi.h" +#include "linux_usbfs.h" + +/* sysfs vs usbfs: + * opening a usbfs node causes the device to be resumed, so we attempt to + * avoid this during enumeration. + * + * sysfs allows us to read the kernel's in-memory copies of device descriptors + * and so forth, avoiding the need to open the device: + * - The binary "descriptors" file was added in 2.6.23. + * - The "busnum" file was added in 2.6.22 + * - The "devnum" file has been present since pre-2.6.18 + * - the "bConfigurationValue" file has been present since pre-2.6.18 + * + * If we have bConfigurationValue, busnum, and devnum, then we can determine + * the active configuration without having to open the usbfs node in RDWR mode. + * We assume this is the case if we see the busnum file (indicates 2.6.22+). + * The busnum file is important as that is the only way we can relate sysfs + * devices to usbfs nodes. + * + * If we also have descriptors, we can obtain the device descriptor and active + * configuration without touching usbfs at all. + * + * The descriptors file originally only contained the active configuration + * descriptor alongside the device descriptor, but all configurations are + * included as of Linux 2.6.26. + */ + +/* endianness for multi-byte fields: + * + * Descriptors exposed by usbfs have the multi-byte fields in the device + * descriptor as host endian. Multi-byte fields in the other descriptors are + * bus-endian. The kernel documentation says otherwise, but it is wrong. + */ + +static const char *usbfs_path = NULL; + +/* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically + * allows us to mark URBs as being part of a specific logical transfer when + * we submit them to the kernel. then, on any error error except a + * cancellation, all URBs within that transfer will be cancelled with the + * endpoint is disabled, meaning that no more data can creep in during the + * time it takes to cancel the remaining URBs. + * + * The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer + * (in either direction) except the first. + * For IN transfers, we must also set SHORT_NOT_OK on all the URBs. + * For OUT transfers, SHORT_NOT_OK must not be set. The effective behaviour + * (where an OUT transfer does not complete, the rest of the URBs in the + * transfer get cancelled) is already in effect, and setting this flag is + * disallowed (a kernel with USB debugging enabled will reject such URBs). + */ +static int supports_flag_bulk_continuation = -1; + +/* clock ID for monotonic clock, as not all clock sources are available on all + * systems. appropriate choice made at initialization time. */ +static clockid_t monotonic_clkid = -1; + +/* do we have a busnum to relate devices? this also implies that we can read + * the active configuration through bConfigurationValue */ +static int sysfs_can_relate_devices = -1; + +/* do we have a descriptors file? */ +static int sysfs_has_descriptors = -1; + +struct linux_device_priv { + char *sysfs_dir; + unsigned char *dev_descriptor; + unsigned char *config_descriptor; +}; + +struct linux_device_handle_priv { + int fd; +}; + +enum reap_action { + NORMAL = 0, + /* submission failed after the first URB, so await cancellation/completion + * of all the others */ + SUBMIT_FAILED, + + /* cancelled by user or timeout */ + CANCELLED, + + /* completed multi-URB transfer in non-final URB */ + COMPLETED_EARLY, + + /* one or more urbs encountered a low-level error */ + ERROR, +}; + +struct linux_transfer_priv { + union { + struct usbfs_urb *urbs; + struct usbfs_urb **iso_urbs; + }; + + enum reap_action reap_action; + int num_urbs; + unsigned int num_retired; + enum libusb_transfer_status reap_status; + + /* next iso packet in user-supplied transfer to be populated */ + int iso_packet_offset; +}; + +static void __get_usbfs_path(struct libusb_device *dev, char *path) +{ + snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, dev->bus_number, + dev->device_address); +} + +static struct linux_device_priv *__device_priv(struct libusb_device *dev) +{ + return (struct linux_device_priv *) dev->os_priv; +} + +static struct linux_device_handle_priv *__device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct linux_device_handle_priv *) handle->os_priv; +} + +static int check_usb_vfs(const char *dirname) +{ + DIR *dir; + struct dirent *entry; + int found = 0; + + dir = opendir(dirname); + if (!dir) + return 0; + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') + continue; + + /* We assume if we find any files that it must be the right place */ + found = 1; + break; + } + + closedir(dir); + return found; +} + +static const char *find_usbfs_path(void) +{ + const char *path = "/dev/bus/usb"; + const char *ret = NULL; + + if (check_usb_vfs(path)) { + ret = path; + } else { + path = "/proc/bus/usb"; + if (check_usb_vfs(path)) + ret = path; + } + + usbi_dbg("found usbfs at %s", ret); + return ret; +} + +/* the monotonic clock is not usable on all systems (e.g. embedded ones often + * seem to lack it). fall back to REALTIME if we have to. */ +static clockid_t find_monotonic_clock(void) +{ + struct timespec ts; + int r; + +#ifdef CLOCK_MONOTONIC + /* Linux 2.6.28 adds CLOCK_MONOTONIC_RAW but we don't use it + * because it's not available through timerfd */ + r = clock_gettime(CLOCK_MONOTONIC, &ts); + if (r == 0) + return CLOCK_MONOTONIC; + usbi_dbg("monotonic clock doesn't work, errno %d", errno); +#endif + + return CLOCK_REALTIME; +} + +/* bulk continuation URB flag available from Linux 2.6.32 */ +static int check_flag_bulk_continuation(void) +{ + struct utsname uts; + int sublevel; + + if (uname(&uts) < 0) + return -1; + if (strlen(uts.release) < 4) + return 0; + if (strncmp(uts.release, "2.6.", 4) != 0) + return 0; + + sublevel = atoi(uts.release + 4); + return sublevel >= 32; +} + +static int op_init(struct libusb_context *ctx) +{ + struct stat statbuf; + int r; + + usbfs_path = find_usbfs_path(); + if (!usbfs_path) { + usbi_err(ctx, "could not find usbfs"); + return LIBUSB_ERROR_OTHER; + } + + if (monotonic_clkid == -1) + monotonic_clkid = find_monotonic_clock(); + + if (supports_flag_bulk_continuation == -1) { + supports_flag_bulk_continuation = check_flag_bulk_continuation(); + if (supports_flag_bulk_continuation == -1) { + usbi_err(ctx, "error checking for bulk continuation support"); + return LIBUSB_ERROR_OTHER; + } + } + + if (supports_flag_bulk_continuation) + usbi_dbg("bulk continuation flag supported"); + + r = stat(SYSFS_DEVICE_PATH, &statbuf); + if (r == 0 && S_ISDIR(statbuf.st_mode)) { + usbi_dbg("found usb devices in sysfs"); + } else { + usbi_dbg("sysfs usb info not available"); + sysfs_has_descriptors = 0; + sysfs_can_relate_devices = 0; + } + + return 0; +} + +static int usbfs_get_device_descriptor(struct libusb_device *dev, + unsigned char *buffer) +{ + struct linux_device_priv *priv = __device_priv(dev); + + /* return cached copy */ + memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH); + return 0; +} + +static int __open_sysfs_attr(struct libusb_device *dev, const char *attr) +{ + struct linux_device_priv *priv = __device_priv(dev); + char filename[PATH_MAX]; + int fd; + + snprintf(filename, PATH_MAX, "%s/%s/%s", + SYSFS_DEVICE_PATH, priv->sysfs_dir, attr); + fd = open(filename, O_RDONLY); + if (fd < 0) { + usbi_err(DEVICE_CTX(dev), + "open %s failed ret=%d errno=%d", filename, fd, errno); + return LIBUSB_ERROR_IO; + } + + return fd; +} + +static int sysfs_get_device_descriptor(struct libusb_device *dev, + unsigned char *buffer) +{ + int fd; + ssize_t r; + + /* sysfs provides access to an in-memory copy of the device descriptor, + * so we use that rather than keeping our own copy */ + + fd = __open_sysfs_attr(dev, "descriptors"); + if (fd < 0) + return fd; + + r = read(fd, buffer, DEVICE_DESC_LENGTH);; + close(fd); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno); + return LIBUSB_ERROR_IO; + } else if (r < DEVICE_DESC_LENGTH) { + usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, DEVICE_DESC_LENGTH); + return LIBUSB_ERROR_IO; + } + + return 0; +} + +static int op_get_device_descriptor(struct libusb_device *dev, + unsigned char *buffer, int *host_endian) +{ + if (sysfs_has_descriptors) { + return sysfs_get_device_descriptor(dev, buffer); + } else { + *host_endian = 1; + return usbfs_get_device_descriptor(dev, buffer); + } +} + +static int usbfs_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buffer, size_t len) +{ + struct linux_device_priv *priv = __device_priv(dev); + if (!priv->config_descriptor) + return LIBUSB_ERROR_NOT_FOUND; /* device is unconfigured */ + + /* retrieve cached copy */ + memcpy(buffer, priv->config_descriptor, len); + return 0; +} + +/* read the bConfigurationValue for a device */ +static int sysfs_get_active_config(struct libusb_device *dev, int *config) +{ + char *endptr; + char tmp[4] = {0, 0, 0, 0}; + long num; + int fd; + size_t r; + + fd = __open_sysfs_attr(dev, "bConfigurationValue"); + if (fd < 0) + return fd; + + r = read(fd, tmp, sizeof(tmp)); + close(fd); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "read bConfigurationValue failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_err(DEVICE_CTX(dev), "device unconfigured"); + *config = -1; + return 0; + } + + if (tmp[sizeof(tmp) - 1] != 0) { + usbi_err(DEVICE_CTX(dev), "not null-terminated?"); + return LIBUSB_ERROR_IO; + } else if (tmp[0] == 0) { + usbi_err(DEVICE_CTX(dev), "no configuration value?"); + return LIBUSB_ERROR_IO; + } + + num = strtol(tmp, &endptr, 10); + if (endptr == tmp) { + usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp); + return LIBUSB_ERROR_IO; + } + + *config = (int) num; + return 0; +} + +/* takes a usbfs/descriptors fd seeked to the start of a configuration, and + * seeks to the next one. */ +static int seek_to_next_config(struct libusb_context *ctx, int fd, + int host_endian) +{ + struct libusb_config_descriptor config; + unsigned char tmp[6]; + off_t off; + int r; + + /* read first 6 bytes of descriptor */ + r = read(fd, tmp, sizeof(tmp)); + if (r < 0) { + usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } else if (r < sizeof(tmp)) { + usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp)); + return LIBUSB_ERROR_IO; + } + + /* seek forward to end of config */ + usbi_parse_descriptor(tmp, "bbwbb", &config, host_endian); + off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR); + if (off < 0) { + usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); + return LIBUSB_ERROR_IO; + } + + return 0; +} + +static int sysfs_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buffer, size_t len) +{ + int fd; + ssize_t r; + off_t off; + int to_copy; + int config; + unsigned char tmp[6]; + + r = sysfs_get_active_config(dev, &config); + if (r < 0) + return r; + if (config == -1) + return LIBUSB_ERROR_NOT_FOUND; + + usbi_dbg("active configuration %d", config); + + /* sysfs provides access to an in-memory copy of the device descriptor, + * so we use that rather than keeping our own copy */ + + fd = __open_sysfs_attr(dev, "descriptors"); + if (fd < 0) + return fd; + + /* device might have been unconfigured since we read bConfigurationValue, + * so first check that there is any config descriptor data at all... */ + off = lseek(fd, 0, SEEK_END); + if (off < 1) { + usbi_err(DEVICE_CTX(dev), "end seek failed, ret=%d errno=%d", + off, errno); + close(fd); + return LIBUSB_ERROR_IO; + } else if (off == DEVICE_DESC_LENGTH) { + close(fd); + return LIBUSB_ERROR_NOT_FOUND; + } + + off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); + if (off < 0) { + usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno); + close(fd); + return LIBUSB_ERROR_IO; + } + + /* unbounded loop: we expect the descriptor to be present under all + * circumstances */ + while (1) { + r = read(fd, tmp, sizeof(tmp)); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", + fd, errno); + return LIBUSB_ERROR_IO; + } else if (r < sizeof(tmp)) { + usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp)); + return LIBUSB_ERROR_IO; + } + + /* check bConfigurationValue */ + if (tmp[5] == config) + break; + + /* try the next descriptor */ + off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR); + if (off < 0) + return LIBUSB_ERROR_IO; + + r = seek_to_next_config(DEVICE_CTX(dev), fd, 1); + if (r < 0) + return r; + } + + to_copy = (len < sizeof(tmp)) ? len : sizeof(tmp); + memcpy(buffer, tmp, to_copy); + if (len > sizeof(tmp)) { + r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp)); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", + fd, errno); + r = LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_dbg("device is unconfigured"); + r = LIBUSB_ERROR_NOT_FOUND; + } else if (r < len - sizeof(tmp)) { + usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len); + r = LIBUSB_ERROR_IO; + } + } else { + r = 0; + } + + close(fd); + return r; +} + +static int op_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buffer, size_t len, int *host_endian) +{ + if (sysfs_has_descriptors) { + return sysfs_get_active_config_descriptor(dev, buffer, len); + } else { + return usbfs_get_active_config_descriptor(dev, buffer, len); + } +} + +/* takes a usbfs fd, attempts to find the requested config and copy a certain + * amount of it into an output buffer. */ +static int get_config_descriptor(struct libusb_context *ctx, int fd, + uint8_t config_index, unsigned char *buffer, size_t len) +{ + off_t off; + ssize_t r; + + off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); + if (off < 0) { + usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); + return LIBUSB_ERROR_IO; + } + + /* might need to skip some configuration descriptors to reach the + * requested configuration */ + while (config_index > 0) { + r = seek_to_next_config(ctx, fd, 0); + if (r < 0) + return r; + config_index--; + } + + /* read the rest of the descriptor */ + r = read(fd, buffer, len); + if (r < 0) { + usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } else if (r < len) { + usbi_err(ctx, "short output read %d/%d", r, len); + return LIBUSB_ERROR_IO; + } + + return 0; +} + +static int op_get_config_descriptor(struct libusb_device *dev, + uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + char filename[PATH_MAX]; + int fd; + int r; + + /* always read from usbfs: sysfs only has the active descriptor + * this will involve waking the device up, but oh well! */ + + /* FIXME: the above is no longer true, new kernels have all descriptors + * in the descriptors file. but its kinda hard to detect if the kernel + * is sufficiently new. */ + + __get_usbfs_path(dev, filename); + fd = open(filename, O_RDONLY); + if (fd < 0) { + usbi_err(DEVICE_CTX(dev), + "open '%s' failed, ret=%d errno=%d", filename, fd, errno); + return LIBUSB_ERROR_IO; + } + + r = get_config_descriptor(DEVICE_CTX(dev), fd, config_index, buffer, len); + close(fd); + return r; +} + +/* cache the active config descriptor in memory. a value of -1 means that + * we aren't sure which one is active, so just assume the first one. + * only for usbfs. */ +static int cache_active_config(struct libusb_device *dev, int fd, + int active_config) +{ + struct linux_device_priv *priv = __device_priv(dev); + struct libusb_config_descriptor config; + unsigned char tmp[8]; + unsigned char *buf; + int idx; + int r; + + if (active_config == -1) { + idx = 0; + } else { + r = usbi_get_config_index_by_value(dev, active_config, &idx); + if (r < 0) + return r; + if (idx == -1) + return LIBUSB_ERROR_NOT_FOUND; + } + + r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, tmp, sizeof(tmp)); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), "first read error %d", r); + return r; + } + + usbi_parse_descriptor(tmp, "bbw", &config, 0); + buf = malloc(config.wTotalLength); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, buf, + config.wTotalLength); + if (r < 0) { + free(buf); + return r; + } + + if (priv->config_descriptor) + free(priv->config_descriptor); + priv->config_descriptor = buf; + return 0; +} + +/* send a control message to retrieve active configuration */ +static int usbfs_get_active_config(struct libusb_device *dev, int fd) +{ + unsigned char active_config = 0; + int r; + + struct usbfs_ctrltransfer ctrl = { + .bmRequestType = LIBUSB_ENDPOINT_IN, + .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION, + .wValue = 0, + .wIndex = 0, + .wLength = 1, + .timeout = 1000, + .data = &active_config + }; + + r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl); + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + /* we hit this error path frequently with buggy devices :( */ + usbi_warn(DEVICE_CTX(dev), + "get_configuration failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + + return active_config; +} + +static int initialize_device(struct libusb_device *dev, uint8_t busnum, + uint8_t devaddr, const char *sysfs_dir) +{ + struct linux_device_priv *priv = __device_priv(dev); + unsigned char *dev_buf; + char path[PATH_MAX]; + int fd; + int active_config = 0; + int device_configured = 1; + ssize_t r; + + dev->bus_number = busnum; + dev->device_address = devaddr; + + if (sysfs_dir) { + priv->sysfs_dir = malloc(strlen(sysfs_dir) + 1); + if (!priv->sysfs_dir) + return LIBUSB_ERROR_NO_MEM; + strcpy(priv->sysfs_dir, sysfs_dir); + } + + if (sysfs_has_descriptors) + return 0; + + /* cache device descriptor in memory so that we can retrieve it later + * without waking the device up (op_get_device_descriptor) */ + + priv->dev_descriptor = NULL; + priv->config_descriptor = NULL; + + if (sysfs_can_relate_devices) { + int tmp = sysfs_get_active_config(dev, &active_config); + if (tmp < 0) + return tmp; + if (active_config == -1) + device_configured = 0; + } + + __get_usbfs_path(dev, path); + fd = open(path, O_RDWR); + if (fd < 0 && errno == EACCES) { + fd = open(path, O_RDONLY); + /* if we only have read-only access to the device, we cannot + * send a control message to determine the active config. just + * assume the first one is active. */ + active_config = -1; + } + + if (fd < 0) { + usbi_err(DEVICE_CTX(dev), "open failed, ret=%d errno=%d", fd, errno); + return LIBUSB_ERROR_IO; + } + + if (!sysfs_can_relate_devices) { + if (active_config == -1) { + /* if we only have read-only access to the device, we cannot + * send a control message to determine the active config. just + * assume the first one is active. */ + usbi_warn(DEVICE_CTX(dev), "access to %s is read-only; cannot " + "determine active configuration descriptor", path); + } else { + active_config = usbfs_get_active_config(dev, fd); + if (active_config == LIBUSB_ERROR_IO) { + /* buggy devices sometimes fail to report their active config. + * assume unconfigured and continue the probing */ + usbi_warn(DEVICE_CTX(dev), "couldn't query active " + "configuration, assumung unconfigured"); + device_configured = 0; + } else if (active_config < 0) { + close(fd); + return active_config; + } else if (active_config == 0) { + /* some buggy devices have a configuration 0, but we're + * reaching into the corner of a corner case here, so let's + * not support buggy devices in these circumstances. + * stick to the specs: a configuration value of 0 means + * unconfigured. */ + usbi_dbg("active cfg 0? assuming unconfigured device"); + device_configured = 0; + } + } + } + + dev_buf = malloc(DEVICE_DESC_LENGTH); + if (!dev_buf) { + close(fd); + return LIBUSB_ERROR_NO_MEM; + } + + r = read(fd, dev_buf, DEVICE_DESC_LENGTH); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "read descriptor failed ret=%d errno=%d", fd, errno); + free(dev_buf); + close(fd); + return LIBUSB_ERROR_IO; + } else if (r < DEVICE_DESC_LENGTH) { + usbi_err(DEVICE_CTX(dev), "short descriptor read (%d)", r); + free(dev_buf); + close(fd); + return LIBUSB_ERROR_IO; + } + + /* bit of a hack: set num_configurations now because cache_active_config() + * calls usbi_get_config_index_by_value() which uses it */ + dev->num_configurations = dev_buf[DEVICE_DESC_LENGTH - 1]; + + if (device_configured) { + r = cache_active_config(dev, fd, active_config); + if (r < 0) { + close(fd); + free(dev_buf); + return r; + } + } + + close(fd); + priv->dev_descriptor = dev_buf; + return 0; +} + +static int enumerate_device(struct libusb_context *ctx, + struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr, + const char *sysfs_dir) +{ + struct discovered_devs *discdevs; + unsigned long session_id; + int need_unref = 0; + struct libusb_device *dev; + int r = 0; + + /* FIXME: session ID is not guaranteed unique as addresses can wrap and + * will be reused. instead we should add a simple sysfs attribute with + * a session ID. */ + session_id = busnum << 8 | devaddr; + usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, + session_id); + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_dbg("using existing device for %d/%d (session %ld)", + busnum, devaddr, session_id); + } else { + usbi_dbg("allocating new device for %d/%d (session %ld)", + busnum, devaddr, session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) + return LIBUSB_ERROR_NO_MEM; + need_unref = 1; + r = initialize_device(dev, busnum, devaddr, sysfs_dir); + if (r < 0) + goto out; + r = usbi_sanitize_device(dev); + if (r < 0) + goto out; + } + + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) + r = LIBUSB_ERROR_NO_MEM; + else + *_discdevs = discdevs; + +out: + if (need_unref) + libusb_unref_device(dev); + return r; +} + +/* open a bus directory and adds all discovered devices to discdevs. on + * failure (non-zero return) the pre-existing discdevs should be destroyed + * (and devices freed). on success, the new discdevs pointer should be used + * as it may have been moved. */ +static int usbfs_scan_busdir(struct libusb_context *ctx, + struct discovered_devs **_discdevs, uint8_t busnum) +{ + DIR *dir; + char dirpath[PATH_MAX]; + struct dirent *entry; + struct discovered_devs *discdevs = *_discdevs; + int r = 0; + + snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum); + usbi_dbg("%s", dirpath); + dir = opendir(dirpath); + if (!dir) { + usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno); + /* FIXME: should handle valid race conditions like hub unplugged + * during directory iteration - this is not an error */ + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(dir))) { + int devaddr; + + if (entry->d_name[0] == '.') + continue; + + devaddr = atoi(entry->d_name); + if (devaddr == 0) { + usbi_dbg("unknown dir entry %s", entry->d_name); + continue; + } + + r = enumerate_device(ctx, &discdevs, busnum, (uint8_t) devaddr, NULL); + if (r < 0) + goto out; + } + + *_discdevs = discdevs; +out: + closedir(dir); + return r; +} + +static int usbfs_get_device_list(struct libusb_context *ctx, + struct discovered_devs **_discdevs) +{ + struct dirent *entry; + DIR *buses = opendir(usbfs_path); + struct discovered_devs *discdevs = *_discdevs; + int r = 0; + + if (!buses) { + usbi_err(ctx, "opendir buses failed errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(buses))) { + struct discovered_devs *discdevs_new = discdevs; + int busnum; + + if (entry->d_name[0] == '.') + continue; + + busnum = atoi(entry->d_name); + if (busnum == 0) { + usbi_dbg("unknown dir entry %s", entry->d_name); + continue; + } + + r = usbfs_scan_busdir(ctx, &discdevs_new, busnum); + if (r < 0) + goto out; + discdevs = discdevs_new; + } + +out: + closedir(buses); + *_discdevs = discdevs; + return r; + +} + +static int sysfs_scan_device(struct libusb_context *ctx, + struct discovered_devs **_discdevs, const char *devname, + int *usbfs_fallback) +{ + int r; + FILE *fd; + char filename[PATH_MAX]; + int busnum; + int devaddr; + + usbi_dbg("scan %s", devname); + + /* determine descriptors presence ahead of time, we need to know this + * when we reach initialize_device */ + if (sysfs_has_descriptors == -1) { + struct stat statbuf; + + snprintf(filename, PATH_MAX, "%s/%s/descriptors", SYSFS_DEVICE_PATH, + devname); + r = stat(filename, &statbuf); + if (r == 0 && S_ISREG(statbuf.st_mode)) { + usbi_dbg("sysfs descriptors available"); + sysfs_has_descriptors = 1; + } else { + usbi_dbg("sysfs descriptors not available"); + sysfs_has_descriptors = 0; + } + } + + snprintf(filename, PATH_MAX, "%s/%s/busnum", SYSFS_DEVICE_PATH, devname); + fd = fopen(filename, "r"); + if (!fd) { + if (errno == ENOENT) { + usbi_dbg("busnum not found, cannot relate sysfs to usbfs, " + "falling back on pure usbfs"); + sysfs_can_relate_devices = 0; + *usbfs_fallback = 1; + return LIBUSB_ERROR_OTHER; + } + usbi_err(ctx, "open busnum failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + sysfs_can_relate_devices = 1; + + r = fscanf(fd, "%d", &busnum); + fclose(fd); + if (r != 1) { + usbi_err(ctx, "fscanf busnum returned %d, errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + + snprintf(filename, PATH_MAX, "%s/%s/devnum", SYSFS_DEVICE_PATH, devname); + fd = fopen(filename, "r"); + if (!fd) { + usbi_err(ctx, "open devnum failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + r = fscanf(fd, "%d", &devaddr); + fclose(fd); + if (r != 1) { + usbi_err(ctx, "fscanf devnum returned %d, errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + + usbi_dbg("bus=%d dev=%d", busnum, devaddr); + if (busnum > 255 || devaddr > 255) + return LIBUSB_ERROR_INVALID_PARAM; + + return enumerate_device(ctx, _discdevs, busnum & 0xff, devaddr & 0xff, + devname); +} + +static int sysfs_get_device_list(struct libusb_context *ctx, + struct discovered_devs **_discdevs, int *usbfs_fallback) +{ + struct discovered_devs *discdevs = *_discdevs; + DIR *devices = opendir(SYSFS_DEVICE_PATH); + struct dirent *entry; + int r = 0; + + if (!devices) { + usbi_err(ctx, "opendir devices failed errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(devices))) { + struct discovered_devs *discdevs_new = discdevs; + + if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3)) + || strchr(entry->d_name, ':')) + continue; + + r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name, + usbfs_fallback); + if (r < 0) + goto out; + discdevs = discdevs_new; + } + +out: + closedir(devices); + *_discdevs = discdevs; + return r; +} + +static int op_get_device_list(struct libusb_context *ctx, + struct discovered_devs **_discdevs) +{ + /* we can retrieve device list and descriptors from sysfs or usbfs. + * sysfs is preferable, because if we use usbfs we end up resuming + * any autosuspended USB devices. however, sysfs is not available + * everywhere, so we need a usbfs fallback too. + * + * as described in the "sysfs vs usbfs" comment, sometimes we have + * sysfs but not enough information to relate sysfs devices to usbfs + * nodes. the usbfs_fallback variable is used to indicate that we should + * fall back on usbfs. + */ + if (sysfs_can_relate_devices != 0) { + int usbfs_fallback = 0; + int r = sysfs_get_device_list(ctx, _discdevs, &usbfs_fallback); + if (!usbfs_fallback) + return r; + } + + return usbfs_get_device_list(ctx, _discdevs); +} + +static int op_open(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = __device_handle_priv(handle); + char filename[PATH_MAX]; + + __get_usbfs_path(handle->dev, filename); + hpriv->fd = open(filename, O_RDWR); + if (hpriv->fd < 0) { + if (errno == EACCES) { + usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: " + "Permission denied.", filename); + usbi_err(HANDLE_CTX(handle), + "libusb requires write access to USB device nodes."); + return LIBUSB_ERROR_ACCESS; + } else if (errno == ENOENT) { + return LIBUSB_ERROR_NO_DEVICE; + } else { + usbi_err(HANDLE_CTX(handle), + "open failed, code %d errno %d", hpriv->fd, errno); + return LIBUSB_ERROR_IO; + } + } + + return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT); +} + +static void op_close(struct libusb_device_handle *dev_handle) +{ + int fd = __device_handle_priv(dev_handle)->fd; + usbi_remove_pollfd(HANDLE_CTX(dev_handle), fd); + close(fd); +} + +static int op_get_configuration(struct libusb_device_handle *handle, + int *config) +{ + int r; + if (sysfs_can_relate_devices != 1) + return LIBUSB_ERROR_NOT_SUPPORTED; + + r = sysfs_get_active_config(handle->dev, config); + if (*config == -1) + *config = 0; + + return 0; +} + +static int op_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct linux_device_priv *priv = __device_priv(handle->dev); + int fd = __device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_SETCONFIG, &config); + if (r) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + if (!sysfs_has_descriptors) { + /* update our cached active config descriptor */ + if (config == -1) { + if (priv->config_descriptor) { + free(priv->config_descriptor); + priv->config_descriptor = NULL; + } + } else { + r = cache_active_config(handle->dev, fd, config); + if (r < 0) + usbi_warn(HANDLE_CTX(handle), + "failed to update cached config descriptor, error %d", r); + } + } + + return 0; +} + +static int op_claim_interface(struct libusb_device_handle *handle, int iface) +{ + int fd = __device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface); + if (r) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "claim interface failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int op_release_interface(struct libusb_device_handle *handle, int iface) +{ + int fd = __device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface); + if (r) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "release interface failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int op_set_interface(struct libusb_device_handle *handle, int iface, + int altsetting) +{ + int fd = __device_handle_priv(handle)->fd; + struct usbfs_setinterface setintf; + int r; + + setintf.interface = iface; + setintf.altsetting = altsetting; + r = ioctl(fd, IOCTL_USBFS_SETINTF, &setintf); + if (r) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "setintf failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_clear_halt(struct libusb_device_handle *handle, + unsigned char endpoint) +{ + int fd = __device_handle_priv(handle)->fd; + unsigned int _endpoint = endpoint; + int r = ioctl(fd, IOCTL_USBFS_CLEAR_HALT, &_endpoint); + if (r) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "clear_halt failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_reset_device(struct libusb_device_handle *handle) +{ + int fd = __device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_RESET, NULL); + if (r) { + if (errno == ENODEV) + return LIBUSB_ERROR_NOT_FOUND; + + usbi_err(HANDLE_CTX(handle), + "reset failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_kernel_driver_active(struct libusb_device_handle *handle, + int interface) +{ + int fd = __device_handle_priv(handle)->fd; + struct usbfs_getdriver getdrv; + int r; + + getdrv.interface = interface; + r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); + if (r) { + if (errno == ENODATA) + return 0; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "get driver failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 1; +} + +static int op_detach_kernel_driver(struct libusb_device_handle *handle, + int interface) +{ + int fd = __device_handle_priv(handle)->fd; + struct usbfs_ioctl command; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_DISCONNECT; + command.data = NULL; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "detach failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_attach_kernel_driver(struct libusb_device_handle *handle, + int interface) +{ + int fd = __device_handle_priv(handle)->fd; + struct usbfs_ioctl command; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_CONNECT; + command.data = NULL; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r < 0) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + + usbi_err(HANDLE_CTX(handle), + "attach failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } else if (r == 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + return 0; +} + +static void op_destroy_device(struct libusb_device *dev) +{ + struct linux_device_priv *priv = __device_priv(dev); + if (!sysfs_has_descriptors) { + if (priv->dev_descriptor) + free(priv->dev_descriptor); + if (priv->config_descriptor) + free(priv->config_descriptor); + } + if (priv->sysfs_dir) + free(priv->sysfs_dir); +} + +static void free_iso_urbs(struct linux_transfer_priv *tpriv) +{ + int i; + for (i = 0; i < tpriv->num_urbs; i++) { + struct usbfs_urb *urb = tpriv->iso_urbs[i]; + if (!urb) + break; + free(urb); + } + + free(tpriv->iso_urbs); + tpriv->iso_urbs = NULL; +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer, + unsigned char urb_type) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urbs; + int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int r; + int i; + size_t alloc_size; + + if (tpriv->urbs) + return LIBUSB_ERROR_BUSY; + + /* usbfs places a 16kb limit on bulk URBs. we divide up larger requests + * into smaller units to meet such restriction, then fire off all the + * units at once. it would be simpler if we just fired one unit at a time, + * but there is a big performance gain through doing it this way. */ + int num_urbs = transfer->length / MAX_BULK_BUFFER_LENGTH; + int last_urb_partial = 0; + + if (transfer->length == 0) { + num_urbs = 1; + } else if ((transfer->length % MAX_BULK_BUFFER_LENGTH) > 0) { + last_urb_partial = 1; + num_urbs++; + } + usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, + transfer->length); + alloc_size = num_urbs * sizeof(struct usbfs_urb); + urbs = malloc(alloc_size); + if (!urbs) + return LIBUSB_ERROR_NO_MEM; + memset(urbs, 0, alloc_size); + tpriv->urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED; + + for (i = 0; i < num_urbs; i++) { + struct usbfs_urb *urb = &urbs[i]; + urb->usercontext = itransfer; + urb->type = urb_type; + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH); + if (supports_flag_bulk_continuation && !is_out) + urb->flags = USBFS_URB_SHORT_NOT_OK; + if (i == num_urbs - 1 && last_urb_partial) + urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH; + else if (transfer->length == 0) + urb->buffer_length = 0; + else + urb->buffer_length = MAX_BULK_BUFFER_LENGTH; + + if (i > 0 && supports_flag_bulk_continuation) + urb->flags |= USBFS_URB_BULK_CONTINUATION; + + r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r < 0) { + int j; + + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else { + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg("first URB failed, easy peasy"); + free(urbs); + tpriv->urbs = NULL; + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we may need to discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * - this URB failing may be no error; EREMOTEIO means that + * this transfer simply didn't need all the URBs we submitted + * so, we report that the transfer was submitted successfully and + * in case of error we discard all previous URBs. later when + * the final reap completes we can report error to the user, + * or success if an earlier URB was completed successfully. + */ + tpriv->reap_action = EREMOTEIO == errno ? COMPLETED_EARLY : SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired += num_urbs - i; + + /* If we completed short then don't try to discard. */ + if (COMPLETED_EARLY == tpriv->reap_action) + return 0; + + for (j = 0; j < i; j++) { + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &urbs[j]); + if (tmp && errno != EINVAL) + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + } + + usbi_dbg("reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + } + + return 0; +} + +static int submit_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + struct usbfs_urb **urbs; + size_t alloc_size; + int num_packets = transfer->num_iso_packets; + int i; + int this_urb_len = 0; + int num_urbs = 1; + int packet_offset = 0; + unsigned int packet_len; + unsigned char *urb_buffer = transfer->buffer; + + if (tpriv->iso_urbs) + return LIBUSB_ERROR_BUSY; + + /* usbfs places a 32kb limit on iso URBs. we divide up larger requests + * into smaller units to meet such restriction, then fire off all the + * units at once. it would be simpler if we just fired one unit at a time, + * but there is a big performance gain through doing it this way. */ + + /* calculate how many URBs we need */ + for (i = 0; i < num_packets; i++) { + int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; + packet_len = transfer->iso_packet_desc[i].length; + + if (packet_len > space_remaining) { + num_urbs++; + this_urb_len = packet_len; + } else { + this_urb_len += packet_len; + } + } + usbi_dbg("need %d 32k URBs for transfer", num_urbs); + + alloc_size = num_urbs * sizeof(*urbs); + urbs = malloc(alloc_size); + if (!urbs) + return LIBUSB_ERROR_NO_MEM; + memset(urbs, 0, alloc_size); + + tpriv->iso_urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->iso_packet_offset = 0; + + /* allocate + initialize each URB with the correct number of packets */ + for (i = 0; i < num_urbs; i++) { + struct usbfs_urb *urb; + int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; + int urb_packet_offset = 0; + unsigned char *urb_buffer_orig = urb_buffer; + int j; + int k; + + /* swallow up all the packets we can fit into this URB */ + while (packet_offset < transfer->num_iso_packets) { + packet_len = transfer->iso_packet_desc[packet_offset].length; + if (packet_len <= space_remaining_in_urb) { + /* throw it in */ + urb_packet_offset++; + packet_offset++; + space_remaining_in_urb -= packet_len; + urb_buffer += packet_len; + } else { + /* it can't fit, save it for the next URB */ + break; + } + } + + alloc_size = sizeof(*urb) + + (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc)); + urb = malloc(alloc_size); + if (!urb) { + free_iso_urbs(tpriv); + return LIBUSB_ERROR_NO_MEM; + } + memset(urb, 0, alloc_size); + urbs[i] = urb; + + /* populate packet lengths */ + for (j = 0, k = packet_offset - urb_packet_offset; + k < packet_offset; k++, j++) { + packet_len = transfer->iso_packet_desc[k].length; + urb->iso_frame_desc[j].length = packet_len; + } + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_ISO; + /* FIXME: interface for non-ASAP data? */ + urb->flags = USBFS_URB_ISO_ASAP; + urb->endpoint = transfer->endpoint; + urb->number_of_packets = urb_packet_offset; + urb->buffer = urb_buffer_orig; + } + + /* submit URBs */ + for (i = 0; i < num_urbs; i++) { + int r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]); + if (r < 0) { + int j; + + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else { + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg("first URB failed, easy peasy"); + free_iso_urbs(tpriv); + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we must discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * so, in this case we discard all the previous URBs BUT we report + * that the transfer was submitted successfully. then later when + * the final discard completes we can report error to the user. + */ + tpriv->reap_action = SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired = num_urbs - i; + for (j = 0; j < i; j++) { + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urbs[j]); + if (tmp && errno != EINVAL) + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + } + + usbi_dbg("reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + } + + return 0; +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urb; + int r; + + if (tpriv->urbs) + return LIBUSB_ERROR_BUSY; + + if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + urb = malloc(sizeof(struct usbfs_urb)); + if (!urb) + return LIBUSB_ERROR_NO_MEM; + memset(urb, 0, sizeof(struct usbfs_urb)); + tpriv->urbs = urb; + tpriv->reap_action = NORMAL; + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_CONTROL; + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer; + urb->buffer_length = transfer->length; + + r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r < 0) { + free(urb); + tpriv->urbs = NULL; + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + return 0; +} + +static int op_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + return submit_bulk_transfer(itransfer, USBFS_URB_TYPE_BULK); + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer, USBFS_URB_TYPE_INTERRUPT); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), + "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int cancel_control_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + int r; + + if (!tpriv->urbs) + return LIBUSB_ERROR_NOT_FOUND; + + tpriv->reap_action = CANCELLED; + r = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->urbs); + if(r) { + if (errno == EINVAL) { + usbi_dbg("URB not found --> assuming ready to be reaped"); + return 0; + } else { + usbi_err(TRANSFER_CTX(transfer), + "unrecognised DISCARD code %d", errno); + return LIBUSB_ERROR_OTHER; + } + } + + return 0; +} + +static int cancel_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + int i; + + if (!tpriv->urbs) + return LIBUSB_ERROR_NOT_FOUND; + + if (tpriv->reap_action != ERROR) + tpriv->reap_action = CANCELLED; + + for (i = 0; i < tpriv->num_urbs; i++) { + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[i]); + if (tmp && errno != EINVAL) + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + } + return 0; +} + +static int cancel_iso_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = + __device_handle_priv(transfer->dev_handle); + int i; + + if (!tpriv->iso_urbs) + return LIBUSB_ERROR_NOT_FOUND; + + tpriv->reap_action = CANCELLED; + for (i = 0; i < tpriv->num_urbs; i++) { + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, tpriv->iso_urbs[i]); + if (tmp && errno != EINVAL) + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + } + return 0; +} + +static int op_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return cancel_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return cancel_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return cancel_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), + "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void op_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + free(tpriv->urbs); + tpriv->urbs = NULL; + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + free_iso_urbs(tpriv); + break; + default: + usbi_err(TRANSFER_CTX(transfer), + "unknown endpoint type %d", transfer->type); + } +} + +static int handle_bulk_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = __device_handle_priv(transfer->dev_handle); + int urb_idx = urb - tpriv->urbs; + + usbi_mutex_lock(&itransfer->lock); + usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status, + urb_idx + 1, tpriv->num_urbs); + + tpriv->num_retired++; + + if (tpriv->reap_action != NORMAL) { + /* cancelled, submit_fail, or completed early */ + usbi_dbg("abnormal reap: urb status %d", urb->status); + + /* even though we're in the process of cancelling, it's possible that + * we may receive some data in these URBs that we don't want to lose. + * examples: + * 1. while the kernel is cancelling all the packets that make up an + * URB, a few of them might complete. so we get back a successful + * cancellation *and* some data. + * 2. we receive a short URB which marks the early completion condition, + * so we start cancelling the remaining URBs. however, we're too + * slow and another URB completes (or at least completes partially). + * + * When this happens, our objectives are not to lose any "surplus" data, + * and also to stick it at the end of the previously-received data + * (closing any holes), so that libusb reports the total amount of + * transferred data and presents it in a contiguous chunk. + */ + if (urb->actual_length > 0) { + unsigned char *target = transfer->buffer + itransfer->transferred; + usbi_dbg("received %d bytes of surplus data", urb->actual_length); + if (urb->buffer != target) { + usbi_dbg("moving surplus data from offset %d to offset %d", + (unsigned char *) urb->buffer - transfer->buffer, + target - transfer->buffer); + memmove(target, urb->buffer, urb->actual_length); + } + itransfer->transferred += urb->actual_length; + } + + if (tpriv->num_retired == tpriv->num_urbs) { + usbi_dbg("abnormal reap: last URB handled, reporting"); + if (tpriv->reap_action != COMPLETED_EARLY && + tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + goto completed; + } + goto out_unlock; + } + + if (urb->status == 0 || urb->status == -EREMOTEIO || + (urb->status == -EOVERFLOW && urb->actual_length > 0)) + itransfer->transferred += urb->actual_length; + + + switch (urb->status) { + case 0: + break; + case -EREMOTEIO: /* short transfer */ + break; + case -EPIPE: + usbi_dbg("detected endpoint stall"); + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_STALL; + goto cancel_remaining; + case -EOVERFLOW: + /* overflow can only ever occur in the last urb */ + usbi_dbg("overflow, actual_length=%d", urb->actual_length); + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_OVERFLOW; + goto completed; + case -ETIME: + case -EPROTO: + case -EILSEQ: + /* These can happen on *any* urb of a multi-urb transfer, so + * save a status and tear down rest of the transfer */ + usbi_dbg("low level error %d", urb->status); + tpriv->reap_action = ERROR; + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + goto cancel_remaining; + default: + usbi_warn(ITRANSFER_CTX(itransfer), + "unrecognised urb status %d", urb->status); + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + goto cancel_remaining; + } + + /* if we're the last urb or we got less data than requested then we're + * done */ + if (urb_idx == tpriv->num_urbs - 1) { + usbi_dbg("last URB in transfer --> complete!"); + goto completed; + } else if (urb->actual_length < urb->buffer_length) { + usbi_dbg("short transfer %d/%d --> complete!", + urb->actual_length, urb->buffer_length); + if (tpriv->reap_action == NORMAL) + tpriv->reap_action = COMPLETED_EARLY; + } else + goto out_unlock; + +cancel_remaining: + if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */ + goto completed; + + /* cancel remaining urbs and wait for their completion before + * reporting results */ + while (++urb_idx < tpriv->num_urbs) { + /* remaining URBs with continuation flag are + * automatically cancelled by the kernel */ + if (tpriv->urbs[urb_idx].flags & USBFS_URB_BULK_CONTINUATION) + continue; + int tmp = ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, &tpriv->urbs[urb_idx]); + if (tmp && errno != EINVAL) + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + } + +out_unlock: + usbi_mutex_unlock(&itransfer->lock); + return 0; + +completed: + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return CANCELLED == tpriv->reap_action ? + usbi_handle_transfer_cancellation(itransfer) : + usbi_handle_transfer_completion(itransfer, tpriv->reap_status); +} + +static int handle_iso_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int num_urbs = tpriv->num_urbs; + int urb_idx = 0; + int i; + + usbi_mutex_lock(&itransfer->lock); + for (i = 0; i < num_urbs; i++) { + if (urb == tpriv->iso_urbs[i]) { + urb_idx = i + 1; + break; + } + } + if (urb_idx == 0) { + usbi_err(TRANSFER_CTX(transfer), "could not locate urb!"); + usbi_mutex_unlock(&itransfer->lock); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status, + urb_idx, num_urbs); + + if (urb->status == 0) { + /* copy isochronous results back in */ + + for (i = 0; i < urb->number_of_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[tpriv->iso_packet_offset++]; + lib_desc->status = urb_desc->status; + lib_desc->actual_length = urb_desc->actual_length; + } + } + + tpriv->num_retired++; + + if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */ + usbi_dbg("CANCEL: urb status %d", urb->status); + + if (tpriv->num_retired == num_urbs) { + usbi_dbg("CANCEL: last URB handled, reporting"); + free_iso_urbs(tpriv); + if (tpriv->reap_action == CANCELLED) { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } else { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, + LIBUSB_TRANSFER_ERROR); + } + } + goto out; + } + + switch (urb->status) { + case 0: + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + usbi_dbg("low-level USB error %d", urb->status); + break; + default: + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised urb status %d", urb->status); + break; + } + + /* if we're the last urb or we got less data than requested then we're + * done */ + if (urb_idx == num_urbs) { + usbi_dbg("last URB in transfer --> complete!"); + free_iso_urbs(tpriv); + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); + } + +out: + usbi_mutex_unlock(&itransfer->lock); + return 0; +} + +static int handle_control_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int status; + + usbi_mutex_lock(&itransfer->lock); + usbi_dbg("handling completion status %d", urb->status); + + if (urb->status == 0) + itransfer->transferred += urb->actual_length; + + if (tpriv->reap_action == CANCELLED) { + if (urb->status != 0 && urb->status != -ENOENT) + usbi_warn(ITRANSFER_CTX(itransfer), + "cancel: unrecognised urb status %d", urb->status); + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } + + switch (urb->status) { + case 0: + itransfer->transferred = urb->actual_length; + status = LIBUSB_TRANSFER_COMPLETED; + break; + case -EPIPE: + usbi_dbg("unsupported control request"); + status = LIBUSB_TRANSFER_STALL; + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + usbi_dbg("low-level bus error occurred"); + status = LIBUSB_TRANSFER_ERROR; + break; + default: + usbi_warn(ITRANSFER_CTX(itransfer), + "unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); +} + +static int reap_for_handle(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = __device_handle_priv(handle); + int r; + struct usbfs_urb *urb; + struct usbi_transfer *itransfer; + struct libusb_transfer *transfer; + + r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb); + if (r == -1 && errno == EAGAIN) + return 1; + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "reap failed error %d errno=%d", + r, errno); + return LIBUSB_ERROR_IO; + } + + itransfer = urb->usercontext; + transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status, + urb->actual_length); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return handle_iso_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return handle_bulk_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_CONTROL: + return handle_control_completion(itransfer, urb); + default: + usbi_err(HANDLE_CTX(handle), "unrecognised endpoint type %x", + transfer->type); + return LIBUSB_ERROR_OTHER; + } +} + +static int op_handle_events(struct libusb_context *ctx, + struct pollfd *fds, nfds_t nfds, int num_ready) +{ + int r; + int i = 0; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + struct pollfd *pollfd = &fds[i]; + struct libusb_device_handle *handle; + struct linux_device_handle_priv *hpriv = NULL; + + if (!pollfd->revents) + continue; + + num_ready--; + list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { + hpriv = __device_handle_priv(handle); + if (hpriv->fd == pollfd->fd) + break; + } + + if (pollfd->revents & POLLERR) { + usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd); + usbi_handle_disconnect(handle); + continue; + } + + r = reap_for_handle(handle); + if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE) + continue; + else if (r < 0) + goto out; + } + + r = 0; +out: + usbi_mutex_unlock(&ctx->open_devs_lock); + return r; +} + +static int op_clock_gettime(int clk_id, struct timespec *tp) +{ + switch (clk_id) { + case USBI_CLOCK_MONOTONIC: + return clock_gettime(monotonic_clkid, tp); + case USBI_CLOCK_REALTIME: + return clock_gettime(CLOCK_REALTIME, tp); + default: + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +#ifdef USBI_TIMERFD_AVAILABLE +static clockid_t op_get_timerfd_clockid(void) +{ + return monotonic_clkid; + +} +#endif + +const struct usbi_os_backend linux_usbfs_backend = { + .name = "Linux usbfs", + .init = op_init, + .exit = NULL, + .get_device_list = op_get_device_list, + .get_device_descriptor = op_get_device_descriptor, + .get_active_config_descriptor = op_get_active_config_descriptor, + .get_config_descriptor = op_get_config_descriptor, + + .open = op_open, + .close = op_close, + .get_configuration = op_get_configuration, + .set_configuration = op_set_configuration, + .claim_interface = op_claim_interface, + .release_interface = op_release_interface, + + .set_interface_altsetting = op_set_interface, + .clear_halt = op_clear_halt, + .reset_device = op_reset_device, + + .kernel_driver_active = op_kernel_driver_active, + .detach_kernel_driver = op_detach_kernel_driver, + .attach_kernel_driver = op_attach_kernel_driver, + + .destroy_device = op_destroy_device, + + .submit_transfer = op_submit_transfer, + .cancel_transfer = op_cancel_transfer, + .clear_transfer_priv = op_clear_transfer_priv, + + .handle_events = op_handle_events, + + .clock_gettime = op_clock_gettime, + +#ifdef USBI_TIMERFD_AVAILABLE + .get_timerfd_clockid = op_get_timerfd_clockid, +#endif + + .device_priv_size = sizeof(struct linux_device_priv), + .device_handle_priv_size = sizeof(struct linux_device_handle_priv), + .transfer_priv_size = sizeof(struct linux_transfer_priv), + .add_iso_packet_size = 0, +}; + diff --git a/include/libusb-1.0/os/linux_usbfs.h b/include/libusb-1.0/os/linux_usbfs.h new file mode 100644 index 0000000..bd02edc --- /dev/null +++ b/include/libusb-1.0/os/linux_usbfs.h @@ -0,0 +1,138 @@ +/* + * usbfs header structures + * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * 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 __LIBUSB_USBFS_H__ +#define __LIBUSB_USBFS_H__ + +#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" + +struct usbfs_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + + uint32_t timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_bulktransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USBFS_MAXDRIVERNAME 255 + +struct usbfs_getdriver { + unsigned int interface; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 + +enum usbfs_urb_type { + USBFS_URB_TYPE_ISO = 0, + USBFS_URB_TYPE_INTERRUPT = 1, + USBFS_URB_TYPE_CONTROL = 2, + USBFS_URB_TYPE_BULK = 3, +}; + +struct usbfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +#define MAX_ISO_BUFFER_LENGTH 32768 +#define MAX_BULK_BUFFER_LENGTH 16384 +#define MAX_CTRL_BUFFER_LENGTH 4096 + +struct usbfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; + void *usercontext; + struct usbfs_iso_packet_desc iso_frame_desc[0]; +}; + +struct usbfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usbfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in <asm/ioctl.h> give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +struct usbfs_hub_portinfo { + unsigned char numports; + unsigned char port[127]; /* port to device num mapping */ +}; + +#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) +#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) +#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) +#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) +#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) +#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) +#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) +#define IOCTL_USBFS_DISCARDURB _IO('U', 11) +#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) +#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) +#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) +#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) +#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) +#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) +#define IOCTL_USBFS_RESET _IO('U', 20) +#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) + +#endif diff --git a/include/libusb-1.0/os/poll_posix.h b/include/libusb-1.0/os/poll_posix.h new file mode 100644 index 0000000..17298a5 --- /dev/null +++ b/include/libusb-1.0/os/poll_posix.h @@ -0,0 +1,12 @@ +#ifndef __LIBUSB_POLL_POSIX_H__ +#define __LIBUSB_POLL_POSIX_H__ + +#include <unistd.h> +#include <poll.h> +#define usbi_write write +#define usbi_read read +#define usbi_close close +#define usbi_pipe pipe +#define usbi_poll poll + +#endif /* __LIBUSB_POLL_POSIX_H__ */ diff --git a/include/libusb-1.0/os/poll_windows.c b/include/libusb-1.0/os/poll_windows.c new file mode 100644 index 0000000..9ae9895 --- /dev/null +++ b/include/libusb-1.0/os/poll_windows.c @@ -0,0 +1,860 @@ +/* + * poll_windows: poll compatibility wrapper for Windows + * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com> + * 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <io.h> + +#include <libusbi.h> + +// 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 _close(int fd); +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern int cygwin_attach_handle_to_fd(char *name, int fd, HANDLE handle, int bin, int access_mode); +// _open_osfhandle() is not available on cygwin, but we can emulate +// it for our needs with cygwin_attach_handle_to_fd() +static inline int _open_osfhandle(intptr_t osfhandle, int flags) +{ + int access_mode; + switch (flags) { + case _O_RDONLY: + access_mode = GENERIC_READ; + break; + case _O_WRONLY: + access_mode = GENERIC_WRITE; + break; + case _O_RDWR: + access_mode = GENERIC_READ|GENERIC_WRITE; + break; + default: + usbi_err(NULL, "unsupported access mode"); + return -1; + } + return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access_mode); +} +#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, 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; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (!is_polling_set) { + pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) + GetProcAddress(GetModuleHandle("KERNEL32"), "CancelIoEx"); + usbi_dbg("Will use CancelIo%s for I/O cancellation", + CancelIoEx_Available?"Ex":""); + for (i=0; i<MAX_FDS; i++) { + poll_fd[i] = INVALID_WINFD; + _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; + _poll_fd[i].thread_id = 0; + InitializeCriticalSection(&_poll_fd[i].mutex); + } +#if defined(DYNAMIC_FDS) + // We need to create an update event so that poll is warned when there + // are new/deleted fds during a timeout wait operation + fd_update = CreateEvent(NULL, TRUE, FALSE, NULL); + if (fd_update == NULL) { + usbi_err(NULL, "unable to create update event"); + } + usbi_mutex_init(&new_fd_mutex, NULL); + nb_new_fds = 0; +#endif + is_polling_set = TRUE; + } + compat_spinlock = 0; +} + +// Internal function to retrieve the table index (and lock the fd mutex) +int _fd_to_index_and_lock(int fd) +{ + int i; + + if (fd <= 0) + return -1; + + for (i=0; i<MAX_FDS; i++) { + if (poll_fd[i].fd == fd) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have changed before we got to critical + if (poll_fd[i].fd != fd) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + return i; + } + } + return -1; +} + +OVERLAPPED *create_overlapped(void) +{ + OVERLAPPED *overlapped = calloc(1, sizeof(OVERLAPPED)); + if (overlapped == NULL) { + return NULL; + } + overlapped->hEvent = 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; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (is_polling_set) { + is_polling_set = FALSE; + + for (i=0; i<MAX_FDS; i++) { + // Cancel any async I/O (handle can be invalid) + cancel_io(i); + // If anything was pending on that I/O, it should be + // terminating, and we should be able to access the fd + // mutex lock before too long + EnterCriticalSection(&_poll_fd[i].mutex); + if ( (poll_fd[i].fd > 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; + HANDLE handle; + 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" + handle = CreateFileA("NUL", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_err(NULL, "could not create pipe: errcode %d", (int)GetLastError()); + goto out1; + } + filedes[0] = _open_osfhandle((intptr_t)handle, _O_RDONLY); + // 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<MAX_FDS; i++) { + if (poll_fd[i].fd < 0) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been allocated before we got to critical + if (poll_fd[i].fd >= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + + poll_fd[i].fd = filedes[0]; + poll_fd[i].handle = 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: + CloseHandle(handle); +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 + fd = _open_osfhandle((intptr_t)CreateFileA("NUL", 0, 0, + NULL, OPEN_EXISTING, 0, NULL), _O_RDWR); + if (fd < 0) { + return INVALID_WINFD; + } + + overlapped = create_overlapped(); + if(overlapped == NULL) { + _close(fd); + return INVALID_WINFD; + } + + for (i=0; i<MAX_FDS; i++) { + if (poll_fd[i].fd < 0) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been removed before we got to critical + if (poll_fd[i].fd >= 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<MAX_FDS; i++) { + if (poll_fd[i].fd == fd) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].fd != fd) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); + return wfd; + } + } + return INVALID_WINFD; +} + +struct winfd handle_to_winfd(HANDLE handle) +{ + int i; + struct winfd wfd; + + CHECK_INIT_POLLING; + + if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) + return INVALID_WINFD; + + for (i=0; i<MAX_FDS; i++) { + if (poll_fd[i].handle == handle) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].handle != handle) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); + return wfd; + } + } + return INVALID_WINFD; +} + +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped) +{ + int i; + struct winfd wfd; + + CHECK_INIT_POLLING; + + if (overlapped == NULL) + return INVALID_WINFD; + + for (i=0; i<MAX_FDS; i++) { + if (poll_fd[i].overlapped == overlapped) { + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].overlapped != overlapped) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); + return wfd; + } + } + return INVALID_WINFD; +} + +/* + * POSIX poll equivalent, using Windows OVERLAPPED + * Currently, this function only accepts one of POLLIN or POLLOUT per fd + * (but you can create multiple fds from the same handle for read and write) + */ +int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout) +{ + unsigned i; + int index, object_index, triggered; + HANDLE *handles_to_wait_on; + int *handle_to_index; + DWORD nb_handles_to_wait_on = 0; + DWORD ret; + +#if defined(DYNAMIC_FDS) + DWORD nb_extra_handles = 0; + unsigned j; + + // To address the possibility of missing new fds between the time the new + // pollable fd set is assembled, and the ResetEvent() call below, an + // additional new_fd[] HANDLE table is used for any new fd that was created + // since the last call to poll (see below) + ResetEvent(fd_update); + + // At this stage, any new fd creation will be detected through the fd_update + // event notification, and any previous creation that we may have missed + // will be picked up through the existing new_fd[] table. +#endif + + CHECK_INIT_POLLING; + + triggered = 0; + handles_to_wait_on = malloc((nfds+1)*sizeof(HANDLE)); // +1 for fd_update + handle_to_index = malloc(nfds*sizeof(int)); + if ((handles_to_wait_on == NULL) || (handle_to_index == NULL)) { + errno = ENOMEM; + triggered = -1; + goto poll_exit; + } + + for (i = 0; i < nfds; ++i) { + fds[i].revents = 0; + + // Only one of POLLIN or POLLOUT can be selected with this version of poll (not both) + if ((fds[i].events & ~POLLIN) && (!(fds[i].events & POLLOUT))) { + fds[i].revents |= POLLERR; + errno = EACCES; + usbi_warn(NULL, "unsupported set of events"); + triggered = -1; + goto poll_exit; + } + + index = _fd_to_index_and_lock(fds[i].fd); + poll_dbg("fd[%d]=%d: (overlapped=%p) got events %04X", i, poll_fd[index].fd, poll_fd[index].overlapped, fds[i].events); + + if ( (index < 0) || (poll_fd[index].handle == INVALID_HANDLE_VALUE) + || (poll_fd[index].handle == 0) || (poll_fd[index].overlapped == NULL)) { + fds[i].revents |= POLLNVAL | POLLERR; + errno = EBADF; + if (index >= 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<nb_new_fds; j++) { + if (handles_to_wait_on[nb_handles_to_wait_on] == new_fd[j]) { + new_fd[j] = INVALID_HANDLE_VALUE; + break; + } + } + usbi_mutex_unlock(&new_fd_mutex); +#endif + nb_handles_to_wait_on++; + } + LeaveCriticalSection(&_poll_fd[index].mutex); + } +#if defined(DYNAMIC_FDS) + // At this stage, new_fd[] should only contain events from fds that + // have been added since the last call to poll, but are not (yet) part + // of the pollable fd set. Typically, these would be from fds that have + // been created between the construction of the fd set and the calling + // of poll. + // Event if we won't be able to return usable poll data on these events, + // make sure we monitor them to return an EINTR code + usbi_mutex_lock(&new_fd_mutex); // We could probably do without + for (i=0; i<nb_new_fds; i++) { + if (new_fd[i] != INVALID_HANDLE_VALUE) { + handles_to_wait_on[nb_handles_to_wait_on++] = new_fd[i]; + nb_extra_handles++; + } + } + usbi_mutex_unlock(&new_fd_mutex); + poll_dbg("dynamic_fds: added %d extra handles", nb_extra_handles); +#endif + + // If nothing was triggered, wait on all fds that require it + if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) { +#if defined(DYNAMIC_FDS) + // Register for fd update notifications + handles_to_wait_on[nb_handles_to_wait_on++] = fd_update; + nb_extra_handles++; +#endif + 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)) { +#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); + } + if (CloseHandle(poll_fd[index].handle) == 0) { + errno = EIO; + } else { + r = 0; + } + 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; +} diff --git a/include/libusb-1.0/os/poll_windows.h b/include/libusb-1.0/os/poll_windows.h new file mode 100644 index 0000000..a9e3e90 --- /dev/null +++ b/include/libusb-1.0/os/poll_windows.h @@ -0,0 +1,120 @@ +/* + * Windows compat: POSIX compatibility wrapper + * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com> + * 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 + * + */ +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#endif + +// Uncomment to have poll return with EINTR as soon as a new transfer (fd) is added +// This should result in a LIBUSB_ERROR_INTERRUPTED being returned by libusb calls, +// which should give the app an opportunity to resubmit a new fd set. +//#define DYNAMIC_FDS + +// Handle synchronous completion through the overlapped structure +#if !defined(STATUS_REPARSE) // reuse the REPARSE status code +#define STATUS_REPARSE ((LONG)0x00000104L) +#endif +#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE +#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) + +enum windows_version { + WINDOWS_UNSUPPORTED, + WINDOWS_XP, + WINDOWS_2003, // also includes XP 64 + WINDOWS_VISTA_AND_LATER, +}; +extern enum windows_version windows_version; + +#define MAX_FDS 256 + +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +typedef unsigned int nfds_t; + +// access modes +enum rw_type { + RW_NONE, + RW_READ, + RW_WRITE, +}; + +// fd struct that can be used for polling on Windows +struct winfd { + int fd; // what's exposed to libusb core + HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it + OVERLAPPED* overlapped; // what will report our I/O status + enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) +}; +extern const struct winfd INVALID_WINFD; + +int usbi_pipe(int pipefd[2]); +int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); +ssize_t usbi_write(int fd, const void *buf, size_t count); +ssize_t usbi_read(int fd, void *buf, size_t count); +int usbi_close(int fd); + +void init_polling(void); +void exit_polling(void); +struct winfd usbi_create_fd(HANDLE handle, int access_mode); +void usbi_free_fd(int fd); +struct winfd fd_to_winfd(int fd); +struct winfd handle_to_winfd(HANDLE handle); +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); + +/* + * Timeval operations + */ +#if defined(DDKBUILD) +#include <winsock.h> // defines timeval functions on DDK +#endif + +#if !defined(TIMESPEC_TO_TIMEVAL) +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (long)(ts)->tv_sec; \ + (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ +} +#endif +#if !defined(timersub) +#define timersub(a, b, result) \ +do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + diff --git a/include/libusb-1.0/os/threads_posix.h b/include/libusb-1.0/os/threads_posix.h new file mode 100644 index 0000000..dc558d4 --- /dev/null +++ b/include/libusb-1.0/os/threads_posix.h @@ -0,0 +1,46 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright (C) 2010 Peter Stuge <peter@stuge.se> + * + * 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 __LIBUSB_THREADS_POSIX_H__ +#define __LIBUSB_THREADS_POSIX_H__ + +#include <pthread.h> + +#define usbi_mutex_static_t pthread_mutex_t +#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define usbi_mutex_static_lock pthread_mutex_lock +#define usbi_mutex_static_unlock pthread_mutex_unlock + +#define usbi_mutex_t pthread_mutex_t +#define usbi_mutex_init pthread_mutex_init +#define usbi_mutex_lock pthread_mutex_lock +#define usbi_mutex_unlock pthread_mutex_unlock +#define usbi_mutex_trylock pthread_mutex_trylock +#define usbi_mutex_destroy pthread_mutex_destroy + +#define usbi_cond_t pthread_cond_t +#define usbi_cond_init pthread_cond_init +#define usbi_cond_wait pthread_cond_wait +#define usbi_cond_timedwait pthread_cond_timedwait +#define usbi_cond_broadcast pthread_cond_broadcast +#define usbi_cond_destroy pthread_cond_destroy +#define usbi_cond_signal pthread_cond_signal + +#endif /* __LIBUSB_THREADS_POSIX_H__ */ diff --git a/include/libusb-1.0/os/threads_windows.c b/include/libusb-1.0/os/threads_windows.c new file mode 100644 index 0000000..8a29920 --- /dev/null +++ b/include/libusb-1.0/os/threads_windows.c @@ -0,0 +1,208 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com> + * + * 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 <config.h> +#include <objbase.h> +#include <errno.h> +#include <stdarg.h> + +#include "libusbi.h" + + +int usbi_mutex_init(usbi_mutex_t *mutex, + const usbi_mutexattr_t *attr) { + if(! mutex) return ((errno=EINVAL)); + *mutex = CreateMutex(NULL, FALSE, NULL); + if(!*mutex) return ((errno=ENOMEM)); + return 0; +} +int usbi_mutex_destroy(usbi_mutex_t *mutex) { + // It is not clear if CloseHandle failure is due to failure to unlock. + // If so, this should be errno=EBUSY. + if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL)); + *mutex = NULL; + return 0; +} +int usbi_mutex_trylock(usbi_mutex_t *mutex) { + DWORD result; + if(!mutex) return ((errno=EINVAL)); + result = WaitForSingleObject(*mutex, 0); + if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) + return 0; // acquired (ToDo: check that abandoned is ok) + if(result == WAIT_TIMEOUT) + return ((errno=EBUSY)); + return ((errno=EINVAL)); // don't know how this would happen + // so don't know proper errno +} +int usbi_mutex_lock(usbi_mutex_t *mutex) { + DWORD result; + if(!mutex) return ((errno=EINVAL)); + result = WaitForSingleObject(*mutex, INFINITE); + if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) + return 0; // acquired (ToDo: check that abandoned is ok) + return ((errno=EINVAL)); // don't know how this would happen + // so don't know proper errno +} +int usbi_mutex_unlock(usbi_mutex_t *mutex) { + if(!mutex) return ((errno=EINVAL)); + if(!ReleaseMutex(*mutex)) return ((errno=EPERM )); + return 0; +} + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) { + if(!mutex) return ((errno=EINVAL)); + while (InterlockedExchange((LONG *)mutex, 1) == 1) { + SleepEx(0, TRUE); + } + return 0; +} +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) { + if(!mutex) return ((errno=EINVAL)); + *mutex = 0; + return 0; +} + + + +int usbi_cond_init(usbi_cond_t *cond, + const usbi_condattr_t *attr) { + if(!cond) return ((errno=EINVAL)); + list_init(&cond->waiters ); + list_init(&cond->not_waiting); + return 0; +} +int usbi_cond_destroy(usbi_cond_t *cond) { + // This assumes no one is using this anymore. The check MAY NOT BE safe. + struct usbi_cond_perthread *pos, *prev_pos = NULL; + if(!cond) return ((errno=EINVAL)); + if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!) + list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) { + free(prev_pos); + list_del(&pos->list); + prev_pos = pos; + } + free(prev_pos); + prev_pos = pos = NULL; + + return 0; +} + +int usbi_cond_broadcast(usbi_cond_t *cond) { + // Assumes mutex is locked; this is not in keeping with POSIX spec, but + // libusb does this anyway, so we simplify by not adding more sync + // primitives to the CV definition! + int fail = 0; + struct usbi_cond_perthread *pos; + if(!cond) return ((errno=EINVAL)); + list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) { + if(!SetEvent(pos->event)) + fail = 1; + } + // The wait function will remove its respective item from the list. + return fail ? ((errno=EINVAL)) : 0; +} +int usbi_cond_signal(usbi_cond_t *cond) { + // Assumes mutex is locked; this is not in keeping with POSIX spec, but + // libusb does this anyway, so we simplify by not adding more sync + // primitives to the CV definition! + struct usbi_cond_perthread *pos; + if(!cond) return ((errno=EINVAL)); + if(list_empty(&cond->waiters)) return 0; // no one to wakeup. + pos = list_entry(&cond->waiters.next, struct usbi_cond_perthread, list); + // The wait function will remove its respective item from the list. + return SetEvent(pos->event) ? 0 : ((errno=EINVAL)); +} +static int __inline usbi_cond_intwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, + DWORD timeout_ms) { + struct usbi_cond_perthread *pos; + int found = 0, r; + DWORD r2,tid = GetCurrentThreadId(); + if(!cond || !mutex) return ((errno=EINVAL)); + list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) { + if(tid == pos->tid) { + found = 1; + break; + } + } + if(!found) { + pos = malloc(sizeof(struct usbi_cond_perthread)); + if(!pos) return ((errno=ENOMEM)); // This errno is not POSIX-allowed. + pos->tid = tid; + pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset. + if(!pos->event) { + free(pos); + return ((errno=ENOMEM)); + } + list_add(&pos->list, &cond->not_waiting); + } + + list_del(&pos->list); // remove from not_waiting list. + list_add(&pos->list, &cond->waiters); + + r = usbi_mutex_unlock(mutex); + if(r) return r; + r2 = WaitForSingleObject(pos->event, timeout_ms); + r = usbi_mutex_lock(mutex); + if(r) return r; + + list_del(&pos->list); + list_add(&pos->list, &cond->not_waiting); + + if(r2 == WAIT_TIMEOUT) return ((errno=ETIMEDOUT)); + + return 0; +} +// N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot! +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) { + return usbi_cond_intwait(cond, mutex, INFINITE); +} +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, + const struct timespec *abstime) { + FILETIME filetime; + ULARGE_INTEGER rtime; + struct timeval targ_time, cur_time, delta_time; + struct timespec cur_time_ns; + DWORD millis; + extern const uint64_t epoch_time; + + GetSystemTimeAsFileTime(&filetime); + rtime.LowPart = filetime.dwLowDateTime; + rtime.HighPart = filetime.dwHighDateTime; + rtime.QuadPart -= epoch_time; + cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000); + cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100); + TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns); + + TIMESPEC_TO_TIMEVAL(&targ_time, abstime); + timersub(&targ_time, &cur_time, &delta_time); + if(delta_time.tv_sec < 0) // abstime already passed? + millis = 0; + else { + millis = delta_time.tv_usec/1000; + millis += delta_time.tv_sec *1000; + if (delta_time.tv_usec % 1000) // round up to next millisecond + millis++; + } + + return usbi_cond_intwait(cond, mutex, millis); +} + diff --git a/include/libusb-1.0/os/threads_windows.h b/include/libusb-1.0/os/threads_windows.h new file mode 100644 index 0000000..2cd1867 --- /dev/null +++ b/include/libusb-1.0/os/threads_windows.h @@ -0,0 +1,84 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com> + * + * 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 __LIBUSB_THREADS_WINDOWS_H__ +#define __LIBUSB_THREADS_WINDOWS_H__ + +#define usbi_mutex_static_t volatile LONG +#define USBI_MUTEX_INITIALIZER 0 + +#define usbi_mutex_t HANDLE + +struct usbi_cond_perthread { + struct list_head list; + DWORD tid; + HANDLE event; +}; +struct usbi_cond_t_ { + // Every time a thread touches the CV, it winds up in one of these lists. + // It stays there until the CV is destroyed, even if the thread + // terminates. + struct list_head waiters; + struct list_head not_waiting; +}; +typedef struct usbi_cond_t_ usbi_cond_t; + +// We *were* getting timespec from pthread.h: +#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) +#define HAVE_STRUCT_TIMESPEC 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ + +// We *were* getting ETIMEDOUT from pthread.h: +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#define usbi_mutexattr_t void +#define usbi_condattr_t void + + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); + + +int usbi_mutex_init(usbi_mutex_t *mutex, + const usbi_mutexattr_t *attr); +int usbi_mutex_lock(usbi_mutex_t *mutex); +int usbi_mutex_unlock(usbi_mutex_t *mutex); +int usbi_mutex_trylock(usbi_mutex_t *mutex); +int usbi_mutex_destroy(usbi_mutex_t *mutex); + +int usbi_cond_init(usbi_cond_t *cond, + const usbi_condattr_t *attr); +int usbi_cond_destroy(usbi_cond_t *cond); +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, + const struct timespec *abstime); +int usbi_cond_broadcast(usbi_cond_t *cond); +int usbi_cond_signal(usbi_cond_t *cond); + +#endif /* __LIBUSB_THREADS_WINDOWS_H__ */ + diff --git a/include/libusb-1.0/os/windows_usb.c b/include/libusb-1.0/os/windows_usb.c new file mode 100644 index 0000000..e37932a --- /dev/null +++ b/include/libusb-1.0/os/windows_usb.c @@ -0,0 +1,4185 @@ +/* + * windows backend for libusb 1.0 + * 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 + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * 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 + */ + +// COMPILATION OPTIONS: +// - Should libusb automatically claim (and release) the interfaces it requires? +#define AUTO_CLAIM +// - Forces instant overlapped completion on timeouts: can prevents extensive +// wait in poll, after a timeout, but might affect subsequent API calls. +// ***USE AT YOUR OWN RISKS*** +//#define FORCE_INSTANT_TIMEOUTS + +#include <config.h> +#include <windows.h> +#include <setupapi.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <process.h> +#include <stdio.h> +#include <inttypes.h> +#include <objbase.h> // for string to GUID conv. requires libole32.a +#include <winioctl.h> + +#include <libusbi.h> +#include "poll_windows.h" +#include "windows_usb.h" + +// The following prevents "banned API" errors when using the MS's WDK OACR/Prefast +#if defined(_PREFAST_) +#pragma warning(disable:28719) +#endif + +// The 2 macros below are used in conjunction with safe loops. +#define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; } +#define LOOP_BREAK(err) { r=err; continue; } + +extern void usbi_fd_notification(struct libusb_context *ctx); + +// Helper prototypes +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian); +static int windows_clock_gettime(int clk_id, struct timespec *tp); +unsigned __stdcall windows_clock_gettime_threaded(void* param); +// WinUSB API prototypes +static int winusb_init(struct libusb_context *ctx); +static int winusb_exit(void); +static int winusb_open(struct libusb_device_handle *dev_handle); +static void winusb_close(struct libusb_device_handle *dev_handle); +static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int winusb_submit_control_transfer(struct usbi_transfer *itransfer); +static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer); +static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int winusb_abort_transfers(struct usbi_transfer *itransfer); +static int winusb_abort_control(struct usbi_transfer *itransfer); +static int winusb_reset_device(struct libusb_device_handle *dev_handle); +static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +// HID API prototypes +static int hid_init(struct libusb_context *ctx); +static int hid_exit(void); +static int hid_open(struct libusb_device_handle *dev_handle); +static void hid_close(struct libusb_device_handle *dev_handle); +static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int hid_submit_control_transfer(struct usbi_transfer *itransfer); +static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer); +static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int hid_abort_transfers(struct usbi_transfer *itransfer); +static int hid_reset_device(struct libusb_device_handle *dev_handle); +static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +// Composite API prototypes +static int composite_init(struct libusb_context *ctx); +static int composite_exit(void); +static int composite_open(struct libusb_device_handle *dev_handle); +static void composite_close(struct libusb_device_handle *dev_handle); +static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int composite_submit_control_transfer(struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(struct usbi_transfer *itransfer); +static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_abort_transfers(struct usbi_transfer *itransfer); +static int composite_abort_control(struct usbi_transfer *itransfer); +static int composite_reset_device(struct libusb_device_handle *dev_handle); +static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); + + +// Global variables +struct windows_hcd_priv* hcd_root = NULL; +uint64_t hires_frequency, hires_ticks_to_ps; +const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime +enum windows_version windows_version = WINDOWS_UNSUPPORTED; +// Concurrency +static int concurrent_usage = -1; +#if defined(AUTO_CLAIM) +usbi_mutex_t autoclaim_lock; +#endif +// 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; +// API globals +bool api_winusb_available = false; +#define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) +bool api_hid_available = false; +#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0) + + +/* + * Converts a WCHAR string to UTF8 (allocate returned string) + * Returns NULL on error + */ +static char* wchar_to_utf8(LPCWSTR wstr) +{ + int size; + char* str; + + // Find out the size we need to allocate for our converted string + size = wchar_to_utf8_ms(wstr, NULL, 0); + if (size <= 1) // An empty string would be size 1 + return NULL; + + if ((str = malloc(size)) == NULL) + return NULL; + + if (wchar_to_utf8_ms(wstr, str, size) != size) { + safe_free(str); + return NULL; + } + + return str; +} + +static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { + if ((guid1 != NULL) && (guid2 != NULL)) { + return (memcmp(guid1, guid2, sizeof(GUID)) == 0); + } + return false; +} + +#if 0 +static char* guid_to_string(const GUID guid) +{ + static char guid_string[MAX_GUID_STRING_LENGTH]; + + sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned int)guid.Data1, guid.Data2, guid.Data3, + guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], + guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + return guid_string; +} +#endif + +/* + * Converts a windows error to human readable string + * uses retval as errorcode, or, if 0, use GetLastError() + */ +static char *windows_error_str(uint32_t retval) +{ +static char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + size_t i; + uint32_t error_code, format_error; + + error_code = retval?retval:GetLastError(); + + safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%d] ", error_code); + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err_string[safe_strlen(err_string)], + ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); + if (size == 0) { + format_error = GetLastError(); + if (format_error) + safe_sprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", error_code, format_error); + else + safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code); + } else { + // Remove CR/LF terminators + for (i=safe_strlen(err_string)-1; ((err_string[i]==0x0A) || (err_string[i]==0x0D)); i--) { + err_string[i] = 0; + } + } + return err_string; +} + +/* + * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes. + * Return an allocated sanitized string or NULL on error. + */ +static char* sanitize_path(const char* path) +{ + const char root_prefix[] = "\\\\.\\"; + size_t j, size, root_size; + char* ret_path = NULL; + size_t add_root = 0; + + if (path == NULL) + return NULL; + + size = safe_strlen(path)+1; + root_size = sizeof(root_prefix)-1; + + // Microsoft indiscriminatly uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes. + if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) || + ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) { + add_root = root_size; + size += add_root; + } + + if ((ret_path = (char*)calloc(size, 1)) == NULL) + return NULL; + + safe_strcpy(&ret_path[add_root], size-add_root, path); + + // Ensure consistancy with root prefix + for (j=0; j<root_size; j++) + ret_path[j] = root_prefix[j]; + + // Same goes for '\' and '#' after the root prefix. Ensure '#' is used + for(j=root_size; j<size; j++) { + ret_path[j] = (char)toupper((int)ret_path[j]); // Fix case too + if (ret_path[j] == '\\') + ret_path[j] = '#'; + } + + return ret_path; +} + +/* + * Cfgmgr32 API functions + */ +static int Cfgmgr32_init(void) +{ + DLL_LOAD(Cfgmgr32.dll, CM_Get_Parent, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Child, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Sibling, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDA, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDW, TRUE); + + return LIBUSB_SUCCESS; +} + +/* + * enumerate interfaces for a specific GUID + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * guid: the GUID for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +SP_DEVICE_INTERFACE_DETAIL_DATA *get_interface_details(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, GUID guid, unsigned index) +{ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + DWORD size; + + if (index <= 0) { + *dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + } + if (*dev_info == INVALID_HANDLE_VALUE) { + return NULL; + } + + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!SetupDiEnumDeviceInfo(*dev_info, index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + index, windows_error_str(0)); + } + SetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!SetupDiEnumDeviceInterfaces(*dev_info, NULL, &guid, index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + index, windows_error_str(0)); + } + SetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + + // Read interface data (dummy + actual) to access the device path + if (!SetupDiGetDeviceInterfaceDetail(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + index, windows_error_str(0)); + goto err_exit; + } + } + else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + + if ((dev_interface_details = malloc(size)) == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", index); + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if (!SetupDiGetDeviceInterfaceDetail(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + index, windows_error_str(0)); + } + + return dev_interface_details; + +err_exit: + SetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; +} + +/* + * Populate the endpoints addresses of the device_priv interface helper structs + */ +static int windows_assign_endpoints(struct libusb_device *dev, int iface, int altsetting) +{ + int i, r; + struct windows_device_priv *priv = __device_priv(dev); + struct libusb_config_descriptor *conf_desc; + const struct libusb_interface_descriptor *if_desc; + + r = libusb_get_config_descriptor(dev, 0, &conf_desc); + if (r != LIBUSB_SUCCESS) { + usbi_warn(NULL, "could not read config descriptor: error %d", r); + return r; + } + + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); + + if (if_desc->bNumEndpoints == 0) { + usbi_dbg("no endpoints found for interface %d", iface); + return LIBUSB_SUCCESS; + } + + priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints); + if (priv->usb_interface[iface].endpoint == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + + priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints; + for (i=0; i<if_desc->bNumEndpoints; i++) { + priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress; + usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface); + } + libusb_free_config_descriptor(conf_desc); + + return LIBUSB_SUCCESS; +} + +// Lookup for a match in the list of API driver names +bool is_api_driver(char* driver, uint8_t api) +{ + uint8_t i; + const char sep_str[2] = {LIST_SEPARATOR, 0}; + char *tok, *tmp_str; + size_t len = safe_strlen(driver); + + if (len == 0) return false; + tmp_str = calloc(len+1, 1); + if (tmp_str == NULL) return false; + memcpy(tmp_str, driver, len+1); + tok = strtok(tmp_str, sep_str); + while (tok != NULL) { + for (i=0; i<usb_api_backend[api].nb_driver_names; i++) { + if (safe_strcmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) { + free(tmp_str); + return true; + } + } + tok = strtok(NULL, sep_str); + } + free (tmp_str); + return false; +} + +/* + * auto-claiming and auto-release helper functions + */ +#if defined(AUTO_CLAIM) +static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type) +{ + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv( + transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int current_interface = *interface_number; + int r = LIBUSB_SUCCESS; + + switch(api_type) { + case USB_API_WINUSB: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_mutex_lock(&autoclaim_lock); + if (current_interface < 0) // No serviceable interface was found + { + for (current_interface=0; current_interface<USB_MAXINTERFACES; current_interface++) { + // Must claim an interface of the same API type + if ( (priv->usb_interface[current_interface].apib == &usb_api_backend[api_type]) + && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS) ) { + usbi_dbg("auto-claimed interface %d for control request", current_interface); + if (handle_priv->autoclaim_count[current_interface] != 0) { + usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero"); + } + handle_priv->autoclaim_count[current_interface]++; + break; + } + } + if (current_interface == USB_MAXINTERFACES) { + usbi_err(ctx, "could not auto-claim any interface"); + r = LIBUSB_ERROR_NOT_FOUND; + } + } else { + // If we have a valid interface that was autoclaimed, we must increment + // its autoclaim count so that we can prevent an early release. + if (handle_priv->autoclaim_count[current_interface] != 0) { + handle_priv->autoclaim_count[current_interface]++; + } + } + usbi_mutex_unlock(&autoclaim_lock); + + *interface_number = current_interface; + return r; + +} + +static void auto_release(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + libusb_device_handle *dev_handle = transfer->dev_handle; + struct windows_device_handle_priv* handle_priv = __device_handle_priv(dev_handle); + int r; + + usbi_mutex_lock(&autoclaim_lock); + if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) { + handle_priv->autoclaim_count[transfer_priv->interface_number]--; + if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) { + r = libusb_release_interface(dev_handle, transfer_priv->interface_number); + if (r == LIBUSB_SUCCESS) { + usbi_dbg("auto-released interface %d", transfer_priv->interface_number); + } else { + usbi_dbg("failed to auto-release interface %d (%s)", + transfer_priv->interface_number, libusb_strerror(r)); + } + } + } + usbi_mutex_unlock(&autoclaim_lock); +} +#endif + + +/* + * init: libusb backend init function + * + * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list + * In our implementation, we equate Windows' "HCD" to LibUSB's "bus". Note that bus is zero indexed. + * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?) + */ +static int windows_init(struct libusb_context *ctx) +{ + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + GUID guid; + libusb_bus_t bus; + int i, r = LIBUSB_ERROR_OTHER; + OSVERSIONINFO os_version; + HANDLE semaphore; + struct windows_hcd_priv** _hcd_cur; + TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) + + sprintf(sem_name, "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? + _hcd_cur = &hcd_root; + + // Detect OS version + memset(&os_version, 0, sizeof(OSVERSIONINFO)); + os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + windows_version = WINDOWS_UNSUPPORTED; + if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) { + if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) { + windows_version = WINDOWS_XP; + } else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 2)) { + windows_version = WINDOWS_2003; // also includes XP 64 + } else if (os_version.dwMajorVersion >= 6) { + windows_version = WINDOWS_VISTA_AND_LATER; + } + } + if (windows_version == WINDOWS_UNSUPPORTED) { + usbi_err(ctx, "This version of Windows is NOT supported"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + +#if defined(AUTO_CLAIM) + // We need a lock for proper auto-release + usbi_mutex_init(&autoclaim_lock, NULL); +#endif + + // Initialize pollable file descriptors + init_polling(); + + // Load missing CFGMGR32.DLL imports + if (Cfgmgr32_init() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve Cfgmgr32.dll functions"); + return LIBUSB_ERROR_NOT_FOUND; + } + + // Initialize the low level APIs (we don't care about errors at this stage) + for (i=0; i<USB_API_MAX; i++) { + usb_api_backend[i].init(ctx); + } + + // Because QueryPerformanceCounter might report different values when + // running on different cores, we create a separate thread for the timer + // calls, which we glue to the first core always to prevent timing discrepancies. + 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 = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, NULL, 0, NULL); + if (timer_thread == NULL) { + usbi_err(ctx, "Unable to create timer thread - aborting"); + goto init_exit; + } + SetThreadAffinityMask(timer_thread, 0); + + guid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + + r = LIBUSB_SUCCESS; + for (bus = 0; ; bus++) + { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_details); + safe_free(*_hcd_cur); + + dev_interface_details = get_interface_details(ctx, &dev_info, NULL, guid, bus); + // safe loop: end of loop condition + if ((dev_interface_details == NULL) || (r != LIBUSB_SUCCESS)) + break; + + // Will need to change storage and size of libusb_bus_t if this ever occurs + if (bus == LIBUSB_BUS_MAX) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); + continue; + } + + // Allocate and init a new priv structure to hold our data + if ((*_hcd_cur = malloc(sizeof(struct windows_hcd_priv))) == NULL) { + usbi_err(ctx, "could not allocate private structure for bus %u. aborting.", bus); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + windows_hcd_priv_init(*_hcd_cur); + (*_hcd_cur)->path = sanitize_path(dev_interface_details->DevicePath); + + _hcd_cur = &((*_hcd_cur)->next); + } + // TODO (2nd official release): thread for hotplug (see darwin source) + } + + if (hcd_root == NULL) + r = LIBUSB_ERROR_NO_DEVICE; + else + r = LIBUSB_SUCCESS; + +init_exit: // Holds semaphore here. + if(!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + 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; +} + +/* + * Initialize device structure, including active config + */ +static int initialize_device(struct libusb_device *dev, libusb_bus_t busnum, + libusb_devaddr_t devaddr, char *path, int connection_index, uint8_t active_config, + struct libusb_device *parent_dev) +{ + struct windows_device_priv *priv = __device_priv(dev); + + windows_device_priv_init(priv); + + dev->bus_number = busnum; + dev->device_address = devaddr; + priv->path = path; + priv->connection_index = connection_index; + priv->parent_dev = parent_dev; + + priv->active_config = active_config; + + if (priv->active_config != 0) { + usbi_dbg("active config: %d", priv->active_config); + } else { + // USB devices that don't have a config value are usually missing a driver + // TODO (after first official release): use this for automated driver installation + // NB: SetupDiGetDeviceRegistryProperty w/ SPDRP_INSTALL_STATE would tell us + // if the driver is properly installed, but driverless devices don't seem to + // be enumerable by SetupDi... + usbi_dbg("* This device has no driver => libusb will not be able to access it *"); + } + + return LIBUSB_SUCCESS; +} + +/* + * HCD (root) hubs need to have their device descriptor manually populated + * + * Note that we follow the Linux convention and use the "Linux Foundation root hub" + * vendor ID as well as the product ID to indicate the hub speed + */ +static int force_hcd_device_descriptor(struct libusb_device *dev, HANDLE handle) +{ + DWORD size; + USB_HUB_CAPABILITIES hub_caps; + USB_HUB_CAPABILITIES_EX hub_caps_ex; + struct windows_device_priv *priv = __device_priv(dev); + struct libusb_context *ctx = DEVICE_CTX(dev); + + priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); + priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; + dev->num_configurations = priv->dev_descriptor.bNumConfigurations = 1; + + // The following is used to set the VIS:PID of root HUBs similarly to what + // Linux does: 1d6b:0001 is for 1x root hubs, 1d6b:0002 for 2x + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + if (windows_version >= WINDOWS_VISTA_AND_LATER) { + size = sizeof(USB_HUB_CAPABILITIES_EX); + if (DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES_EX, &hub_caps_ex, + size, &hub_caps_ex, size, &size, NULL)) { + // Sanity check. HCD hub should always be root + if (!hub_caps_ex.CapabilityFlags.HubIsRoot) { + usbi_warn(ctx, "program assertion failed - HCD hub is not reported as root hub."); + } + priv->dev_descriptor.idProduct = hub_caps_ex.CapabilityFlags.HubIsHighSpeedCapable?2:1; + } + } else { + size = sizeof(USB_HUB_CAPABILITIES); + if (!DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES, &hub_caps, + size, &hub_caps, size, &size, NULL)) { + usbi_warn(ctx, "could not read hub capabilities (std) for hub %s: %s", + priv->path, windows_error_str(0)); + priv->dev_descriptor.idProduct = 1; // Indicate 1x speed + } else { + priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1; + } + } + + return LIBUSB_SUCCESS; +} + +/* + * fetch and cache all the config descriptors through I/O + */ +static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle) +{ + DWORD size, ret_size; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct windows_device_priv *priv = __device_priv(dev); + int r; + uint8_t i; + + USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request + PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request + PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL; + + if (dev->num_configurations == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + priv->config_descriptor = malloc(dev->num_configurations * sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NO_MEM; + for (i=0; i<dev->num_configurations; i++) + priv->config_descriptor[i] = NULL; + + for (i=0, r=LIBUSB_SUCCESS; ; i++) + { + // safe loop: release all dynamic resources + safe_free(cd_buf_actual); + + // safe loop: end of loop condition + if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS)) + break; + + size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT); + memset(&cd_buf_short, 0, size); + + cd_buf_short.req.ConnectionIndex = priv->connection_index; + cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_short.req.SetupPacket.wIndex = i; + cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + // Dummy call to get the required data size + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, + &cd_buf_short, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor (dummy): %s", windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { + usbi_err(ctx, "unexpected configuration descriptor size (dummy)."); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength; + if ((cd_buf_actual = (PUSB_DESCRIPTOR_REQUEST)malloc(size)) == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor buffer. aborting."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + memset(cd_buf_actual, 0, size); + + // Actual call + cd_buf_actual->ConnectionIndex = priv->connection_index; + cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_actual->SetupPacket.wIndex = i; + cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, + cd_buf_actual, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor (actual): %s", windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR*)cd_buf_actual+sizeof(USB_DESCRIPTOR_REQUEST)); + + if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) { + usbi_err(ctx, "unexpected configuration descriptor size (actual)."); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { + usbi_err(ctx, "not a configuration descriptor"); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + usbi_dbg("cached config descriptor #%d (%d bytes)", i+1, cd_data->wTotalLength); + + // Sanity check. Ensures that indexes for our list of config desc is in the right order + if (i != (cd_data->bConfigurationValue-1)) { + usbi_warn(ctx, "program assertion failed - config descriptors are being read out of order"); + continue; + } + + // Cache the descriptor + priv->config_descriptor[i] = malloc(cd_data->wTotalLength); + if (priv->config_descriptor[i] == NULL) + return LIBUSB_ERROR_NO_MEM; + + memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength); + } + return LIBUSB_SUCCESS; +} + +/* + * Recursively enumerates and finds all hubs & devices + */ +static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs **_discdevs, + HANDLE hub_handle, libusb_bus_t busnum, struct libusb_device *parent_dev, uint8_t nb_ports) +{ + struct discovered_devs *discdevs = *_discdevs; + struct libusb_device *dev = NULL; + DWORD size, size_initial, size_fixed, getname_ioctl; + HANDLE handle = INVALID_HANDLE_VALUE; + USB_HUB_NAME_FIXED s_hubname; + USB_NODE_CONNECTION_INFORMATION conn_info; + USB_NODE_INFORMATION hub_node; + bool is_hcd, need_unref = false; + int i, r; + LPCWSTR wstr; + char *tmp_str = NULL, *path_str = NULL; + unsigned long session_id; + libusb_devaddr_t devaddr = 0; + struct windows_device_priv *priv, *parent_priv; + + // obviously, root (HCD) hubs have no parent + is_hcd = (parent_dev == NULL); + if (is_hcd) + { + if (nb_ports != 1) { + usbi_warn(ctx, "program assertion failed - invalid number of ports for HCD."); + return LIBUSB_ERROR_INVALID_PARAM; + } + parent_priv = NULL; + size_initial = sizeof(USB_ROOT_HUB_NAME); + size_fixed = sizeof(USB_ROOT_HUB_NAME_FIXED); + getname_ioctl = IOCTL_USB_GET_ROOT_HUB_NAME; + } + else + { + parent_priv = __device_priv(parent_dev); + size_initial = sizeof(USB_NODE_CONNECTION_NAME); + size_fixed = sizeof(USB_NODE_CONNECTION_NAME_FIXED); + getname_ioctl = IOCTL_USB_GET_NODE_CONNECTION_NAME; + } + + // Loop through all the ports on this hub + for (i = 1, r = LIBUSB_SUCCESS; ; i++) + { + // safe loop: release all dynamic resources + if (need_unref) { + safe_unref_device(dev); + need_unref = false; + } + safe_free(tmp_str); + safe_free(path_str); + safe_closehandle(handle); + + // safe loop: end of loop condition + if ((i > nb_ports) || (r != LIBUSB_SUCCESS)) + break; + + memset(&conn_info, 0, sizeof(conn_info)); + // For non HCDs, check if the node on this port is a hub or a regular device + if (!is_hcd) { + size = sizeof(USB_NODE_CONNECTION_INFORMATION); + conn_info.ConnectionIndex = i; + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size, + &conn_info, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information: %s", windows_error_str(0)); + continue; + } + + if (conn_info.ConnectionStatus == NoDeviceConnected) { + continue; + } + + if (conn_info.DeviceAddress == LIBUSB_DEVADDR_MAX) { + usbi_warn(ctx, "program assertion failed - device address is %d " + "(conflicts with root hub), ignoring device", LIBUSB_DEVADDR_MAX); + continue; + } + + s_hubname.u.node.ConnectionIndex = i; // Only used for non HCDs (s_hubname is an union) + } + else + { + // HCDs have only 1 node, and it's always a hub + conn_info.DeviceAddress = LIBUSB_DEVADDR_MAX; // using 0 can conflict with driverless devices + conn_info.DeviceIsHub = true; + conn_info.CurrentConfigurationValue = 1; + } + + // If this node is a hub (HCD or not), open it + if (conn_info.DeviceIsHub) { + size = size_initial; + if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, + &s_hubname, size, &size, NULL)) { + usbi_warn(ctx, "could not get hub path (dummy): %s", windows_error_str(0)); + continue; + } + + size = is_hcd?s_hubname.u.root.ActualLength:s_hubname.u.node.ActualLength; + if (size > size_fixed) { + usbi_warn(ctx, "program assertion failed - hub path is too long"); + continue; + } + + if (!is_hcd) { + // previous call trashes some of the data + s_hubname.u.node.ConnectionIndex = i; + } + if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, + &s_hubname, size, &size, NULL)) { + usbi_warn(ctx, "could not get hub path (actual): %s", windows_error_str(0)); + continue; + } + + // Add prefix + wstr = is_hcd?s_hubname.u.root.RootHubName:s_hubname.u.node.NodeName; + tmp_str = wchar_to_utf8(wstr); + if (tmp_str == NULL) { + usbi_err(ctx, "could not convert hub path string."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + + path_str = sanitize_path(tmp_str); + if (path_str == NULL) { + usbi_err(ctx, "could not sanitize hub path string."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + + // Open Hub + handle = CreateFileA(path_str, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if(handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", path_str, windows_error_str(0)); + continue; + } + } + + // Generate a session ID + // Will need to change the session_id computation if this assertion fails + if (conn_info.DeviceAddress > LIBUSB_DEVADDR_MAX) { + usbi_warn(ctx, "program assertion failed - device address is greater than %d, ignoring device", + LIBUSB_DEVADDR_MAX); + continue; + } else { + devaddr = (uint8_t)conn_info.DeviceAddress; + } + // Same trick as linux for session_id, with same caveat + session_id = busnum << (sizeof(libusb_devaddr_t)*8) | devaddr; + usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, session_id); + + // Allocate device if needed + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_dbg("using existing device for session %ld", session_id); + priv = __device_priv(dev); + // Because we are rebuilding the list, there's no guarantee + // the parent device pointer is still the same. + // Other device data should still be reusable + priv->parent_dev = parent_dev; + } else { + usbi_dbg("allocating new device for session %ld", session_id); + if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + need_unref = true; + + LOOP_CHECK(initialize_device(dev, busnum, devaddr, path_str, i, + conn_info.CurrentConfigurationValue, parent_dev)); + priv = __device_priv(dev); + + path_str = NULL; // protect our path from being freed + + // Setup the cached descriptors. Note that only non HCDs can fetch descriptors + if (!is_hcd) { + // The device descriptor has been read with conn_info + memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); + dev->num_configurations = priv->dev_descriptor.bNumConfigurations; + // If we can't read the config descriptors, just set the number of confs to zero + if (cache_config_descriptors(dev, hub_handle) != LIBUSB_SUCCESS) { + dev->num_configurations = 0; + priv->dev_descriptor.bNumConfigurations = 0; + } + } else { + LOOP_CHECK(force_hcd_device_descriptor(dev, handle)); + } + LOOP_CHECK(usbi_sanitize_device(dev)); + } + + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + + *_discdevs = discdevs; + + // Finally, if device is a hub, recurse + if (conn_info.DeviceIsHub) { + // Find number of ports for this hub + size = sizeof(USB_NODE_INFORMATION); + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size, + &hub_node, size, &size, NULL)) { + usbi_warn(ctx, "could not retreive information for hub %s: %s", + priv->path, windows_error_str(0)); + continue; + } + + if (hub_node.NodeType != UsbHub) { + usbi_warn(ctx, "unexpected hub type (%d) for hub %s", hub_node.NodeType, priv->path); + continue; + } + + usbi_dbg("%d ports Hub: %s", hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts, priv->path); + + usb_enumerate_hub(ctx, _discdevs, handle, busnum, dev, + hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts); + } + } + + return r; +} + +/* + * Composite device interfaces are not enumerated using GUID_DEVINTERFACE_USB_DEVICE, + * but instead require a different lookup mechanism + */ +static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, struct windows_device_priv *priv) +{ +// indexes for additional interface GUIDs, not available from "USB" +// SetupDiGetClassDevs enumeration should go here. Typically, these are +// device interfaces that begin with something else than "\\?\usb\" +enum libusb_hid_report_type { + HID_DEVICE_INTERFACE_GUID_INDEX = 0, + MAX_DEVICE_INTERFACE_GUID_INDEX = 1 +}; + + DEVINST child_devinst, parent_devinst; + unsigned i, j, max_guids, nb_paths, interface_number; + uint8_t api; + bool found; + DWORD type, size; + CONFIGRET r; + HDEVINFO dev_info; + SP_DEVINFO_DATA dev_info_data; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + HKEY key; + WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; + GUID guid, class_guid; + GUID guid_table[MAX_USB_DEVICES]; + char* sanitized_path[MAX_USB_DEVICES]; + char* hid_path[MAX_USB_DEVICES]; // An extra path is needed for HID + uint8_t api_type[MAX_USB_DEVICES]; + char* sanitized_short = NULL; + char path[MAX_PATH_LENGTH]; + char driver[MAX_KEY_LENGTH]; + + dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); + if (dev_info == INVALID_HANDLE_VALUE) { + return LIBUSB_ERROR_NOT_FOUND; + } + + // Manually add the HID GUID as it cannot be read with DeviceInterfaceGUIDs reg key) + // NB the value returned by HidD_GetHidGuid, which is for interface class is different + // from GUID_HID, which is the device class GUID + HidD_GetHidGuid(&guid_table[HID_DEVICE_INTERFACE_GUID_INDEX]); + // NB: for other interface guids, SetupDiClassGuidsFromName can be used + max_guids = MAX_DEVICE_INTERFACE_GUID_INDEX; + + // First, retrieve all the device interface GUIDs + for (i = 0; ; i++) + { + dev_info_data.cbSize = sizeof(dev_info_data); + if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) { + break; + } + + key = SetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key == INVALID_HANDLE_VALUE) { + usbi_dbg("could not open registry key"); + continue; + } + + size = sizeof(guid_string_w); + r = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &type, + (BYTE*)guid_string_w, &size); + RegCloseKey(key); + if (r != ERROR_SUCCESS) { + continue; + } + CLSIDFromString(guid_string_w, &guid); + + // identical device interface GUIDs are not supposed to happen, but are a real possibility + // => check and ignore duplicates + found = false; + for (j=0; j<max_guids; j++) { + if (memcmp(&guid_table[j], &guid, sizeof(GUID)) == 0) { + found = true; + break; + } + } + if (!found) { + guid_table[max_guids++] = guid; + if (max_guids > MAX_USB_DEVICES) { + usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); + break; + } + } + } + SetupDiDestroyDeviceInfoList(dev_info); + + // Now let's find the device interface paths for all these devices + nb_paths = 0; + for (j=0; j<max_guids; j++) + { + guid = guid_table[j]; + + for (i = 0; ; i++) + { + safe_free(dev_interface_details); + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i); + if (dev_interface_details == NULL) + break; + + // HID devices (and possibly other classes) have an extra indirection + // for an USB path we can recognize + if (j == HID_DEVICE_INTERFACE_GUID_INDEX) { + if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve HID parent info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve HID parent's path for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + } + + // In case we can't read the driver string through SPDRP_SERVICE (which is + // the case for HID), we need the ClassGUID for comparison. + if(!SetupDiGetDeviceRegistryPropertyW(dev_info, &dev_info_data, SPDRP_CLASSGUID, + NULL, (BYTE*)guid_string_w, sizeof(guid_string_w), &size)) { + usbi_warn(ctx, "could not read class GUID for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + CLSIDFromString(guid_string_w, &class_guid); + + // Attempt to read the driver string + if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE, + NULL, (BYTE*)driver, MAX_KEY_LENGTH, &size)) { + driver[0] = 0; + } + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if ( (is_api_driver(driver, api)) + || (guid_eq(&class_guid, usb_api_backend[api].class_guid)) ) { + api_type[nb_paths] = api; + if (j == HID_DEVICE_INTERFACE_GUID_INDEX) { + hid_path[nb_paths] = sanitize_path(path); + } else { + hid_path[nb_paths] = NULL; + } + sanitized_path[nb_paths++] = sanitize_path(dev_interface_details->DevicePath); + if (nb_paths > MAX_USB_DEVICES) { + usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); + break; + } + } + } + } + } + + // Finally, match the interface paths with the interfaces. We do that + // by looking at the children of the composite device + // NB: if the interfaces are not found in their expected position, + // claim_interface will issue a warning + found = false; + memset(&child_devinst, 0, sizeof(DEVINST)); // prevents /W4 warning + for (i = 0; i<USB_MAXINTERFACES; i++) + { + if (i == 0) { + r = CM_Get_Child(&child_devinst, devinst, 0); + } else { + r = CM_Get_Sibling(&child_devinst, child_devinst, 0); + } + if (r == CR_NO_SUCH_DEVNODE) { // end of the siblings + break; + } else if (r != CR_SUCCESS) { + usbi_dbg("unable to find interface sibling #%d, error = %X", i, r); + break; + } + + r = CM_Get_Device_ID(child_devinst, path, MAX_PATH_LENGTH, 0); + if (r != CR_SUCCESS) { + usbi_err(ctx, "could not retrieve simple path for interface %d: CR error %d", + i, r); + continue; + } + sanitized_short = sanitize_path(path); + if (sanitized_short == NULL) { + usbi_err(ctx, "could not sanitize path for interface %d", i); + continue; + } + + // Because MI_## are not necessarily in sequential order (some composite HID + // devices will have only MI_00 & MI_03 for instance), we retrieve the actual + // interface number from the path's MI value + interface_number = i; + for (j=0; sanitized_short[j] != 0; ) { + if ( (sanitized_short[j++] == 'M') && (sanitized_short[j++] == 'I') + && (sanitized_short[j++] == '_') ) { + interface_number = (sanitized_short[j++] - '0')*10; + interface_number += sanitized_short[j] - '0'; + break; + } + } + if (sanitized_short[j] == 0) { + usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", + sanitized_short, interface_number); + } + + for (j=0; j<nb_paths; j++) { + if ( (safe_strncmp(sanitized_path[j], sanitized_short, safe_strlen(sanitized_short)) == 0) + || (safe_strcmp(hid_path[j], sanitized_short) == 0 ) ) { + // HID devices can have multiple collections (COL##) for each MI_## interface + if (priv->usb_interface[interface_number].path != NULL) { + usbi_dbg("interface_path[%d] already set - ignoring HID collection: %s", + interface_number, sanitized_path[j]); + if (api_type[j] != USB_API_HID) { + usbi_warn(ctx, "program assertion failed - not an HID collection"); + } + } else { + priv->usb_interface[interface_number].path = sanitized_path[j]; + priv->usb_interface[interface_number].apib = &usb_api_backend[api_type[j]]; + if ((api_type[j] == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + } + priv->composite_api_flags |= 1<<api_type[j]; + sanitized_path[j] = NULL; + } + } + } + safe_free(sanitized_short); + + if (priv->usb_interface[interface_number].path == NULL) { + usbi_warn(ctx, "interface_path[%d]: unhandled API - interface will be disabled", + interface_number); + continue; + } + usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path); + found = true; + } + + for (j=0; j<nb_paths; j++) { + safe_free(sanitized_path[j]); + safe_free(hid_path[j]); + } + + if (found == 0) { + usbi_warn(ctx, "composite device: no interfaces were found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return LIBUSB_SUCCESS; +} + +/* + * Likewise, HID device interfaces's path (\\.\HID\...) are not enumerated through the + * generic USB devices GUID, but are actually children of one such device + */ +static int set_hid_device(struct libusb_context *ctx, struct windows_device_priv *priv) + { + char path[MAX_PATH_LENGTH]; + char *sanitized_path = NULL; + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + SP_DEVINFO_DATA dev_info_data; + DEVINST parent_devinst; + GUID guid; + int r = LIBUSB_SUCCESS; + unsigned i, interface_number; + + interface_number = 0; + HidD_GetHidGuid(&guid); + for (i = 0; ; i++) + { + // safe loop: free up any (unprotected) dynamic resource + safe_free(dev_interface_details); + safe_free(sanitized_path); + + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i); + // safe loop: end of loop condition + if ( (dev_interface_details == NULL) + || (r != LIBUSB_SUCCESS) ) + break; + + // Retrieve parent's path using PnP Configuration Manager (CM) + if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + // Fix parent's path inconsistencies before attempting to compare + sanitized_path = sanitize_path(path); + if (sanitized_path == NULL) { + usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.", + dev_interface_details->DevicePath); + continue; + } + + // NB: we compare strings of different lengths below => strncmp + if (safe_strncmp(priv->path, sanitized_path, safe_strlen(sanitized_path)) == 0) { + priv->usb_interface[interface_number].path = sanitize_path(dev_interface_details->DevicePath); + priv->usb_interface[interface_number].apib = &usb_api_backend[USB_API_HID]; + usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path); + interface_number++; + } + } + + return LIBUSB_SUCCESS; +} + +/* + * This function retrieves and sets the paths of all non-hub devices + * NB: No I/O with device is required during this call + */ +static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *discdevs) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + struct windows_device_priv *priv; + struct windows_device_priv *parent_priv; + char path[MAX_PATH_LENGTH]; + char *sanitized_path = NULL; + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; + SP_DEVINFO_DATA dev_info_data; + DEVINST parent_devinst; + GUID guid; + DWORD size, reg_type, install_state, port_nr; + int r = LIBUSB_SUCCESS; + unsigned i, j, k, l; + uint8_t api; + bool found; + + // TODO (after first official release): MI_## automated driver installation + guid = GUID_DEVINTERFACE_USB_DEVICE; + for (i = 0; ; i++) + { + // safe loop: free up any (unprotected) dynamic resource + safe_free(dev_interface_details); + safe_free(sanitized_path); + + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i); + // safe loop: end of loop condition + if ( (dev_interface_details == NULL) + || (r != LIBUSB_SUCCESS) ) + break; + + // Check that the driver installation is OK + if ( (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + ®_type, (BYTE*)&install_state, 4, &size)) + && (size != 4) ){ + usbi_warn(ctx, "could not detect installation state of driver for %s: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device %s is reporting an issue (code: %d) - skipping", + dev_interface_details->DevicePath, install_state); + continue; + } + + // The SPDRP_ADDRESS for USB devices should be the device port number on the hub + if ( (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_ADDRESS, + ®_type, (BYTE*)&port_nr, 4, &size)) + && (size != 4) ){ + usbi_warn(ctx, "could not retrieve port number for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + // Retrieve parent's path using PnP Configuration Manager (CM) + if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s", + dev_interface_details->DevicePath, windows_error_str(0)); + continue; + } + + // Fix parent's path inconsistencies before attempting to compare + sanitized_path = sanitize_path(path); + if (sanitized_path == NULL) { + usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.", + dev_interface_details->DevicePath); + continue; + } + + // With the parent path and port number, we should be able to locate our device + // by comparing these values to the ones we got when enumerating hubs + found = false; + for (j=0; j<discdevs->len; j++) { + priv = __device_priv(discdevs->devices[j]); + + if (priv->parent_dev == NULL) { + continue; // ignore HCDs + } + + parent_priv = __device_priv(priv->parent_dev); + + // NB: we compare strings of different lengths below => strncmp + if ( (safe_strncmp(parent_priv->path, sanitized_path, safe_strlen(sanitized_path)) == 0) + && (port_nr == priv->connection_index) ) { + + priv->path = sanitize_path(dev_interface_details->DevicePath); + + usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number, + discdevs->devices[j]->device_address, priv->path); + + // Check the service & filter names to know the API we should use + for (k=0; k<3; k++) { + if (SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, lookup[k].reg_prop, + ®_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) { + // our buffers are MAX_KEY_LENGTH+1 so we can overflow if needed + lookup[k].list[safe_strlen(lookup[k].list)+1] = 0; + } + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsuported one does, it's not an issue + for (l=0; (lookup[k].list[l] != 0) || (lookup[k].list[l+1] != 0); l++) { + if (lookup[k].list[l] == 0) { + lookup[k].list[l] = LIST_SEPARATOR; + } + } + upperize(lookup[k].list); + usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); + found = true; + } else { + if (GetLastError() != ERROR_INVALID_DATA) { + usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); + } + lookup[k].list[0] = 0; + } + } + + for (api=0; api<USB_API_MAX; api++) { + for (k=0; k<3; k++) { + if (is_api_driver(lookup[k].list, api)) { + usbi_dbg("matched %s name against %s", lookup[k].designation, usb_api_backend[api].designation); + break; + } + } + if (k >= 3) continue; + priv->apib = &usb_api_backend[api]; + switch(api) { + case USB_API_COMPOSITE: + set_composite_device(ctx, dev_info_data.DevInst, priv); + break; + case USB_API_HID: + safe_free(priv->hid); + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) { + usbi_err(ctx, "could not allocate HID data for %s, skipping", + dev_interface_details->DevicePath); + priv->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + safe_free(priv->path); + } else { + set_hid_device(ctx, priv); + } + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = malloc(safe_strlen(priv->path)+1); + if (priv->usb_interface[0].path != NULL) { + safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path)+1, priv->path); + } + // The following is needed if we want to API calls to work for both simple + // and composite devices, as + for(k=0; k<USB_MAXINTERFACES; k++) { + priv->usb_interface[k].apib = &usb_api_backend[api]; + } + break; + } + } + break; + } + } + if (!found) { + usbi_warn(ctx, "could not match %s with a libusb device.", dev_interface_details->DevicePath); + continue; + } + } + + return LIBUSB_SUCCESS; +} + +/* + * get_device_list: libusb backend device enumeration function + */ +static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + struct windows_hcd_priv* hcd; + HANDLE handle = INVALID_HANDLE_VALUE; + int r = LIBUSB_SUCCESS; + libusb_bus_t bus; + + // Use the index of the HCD in the chained list as bus # + for (hcd = hcd_root, bus = 0; ; hcd = hcd->next, bus++) + { + safe_closehandle(handle); + + if ( (hcd == NULL) || (r != LIBUSB_SUCCESS) ) + break; + + if (bus == LIBUSB_BUS_MAX) { + usbi_warn(ctx, "program assertion failed - got more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); + continue; + } + + handle = CreateFileA(hcd->path, GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open bus %u, skipping: %s", bus, windows_error_str(0)); + continue; + } + + LOOP_CHECK(usb_enumerate_hub(ctx, _discdevs, handle, bus, NULL, 1)); + } + + // Set the interface path for non-hubs + r = set_device_paths(ctx, *_discdevs); + + return r; +} + +/* + * exit: libusb backend deinitialization function + */ +static void windows_exit(void) +{ + struct windows_hcd_priv* hcd_tmp; + int i; + HANDLE semaphore; + TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) + + sprintf(sem_name, "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 + while (hcd_root != NULL) + { + hcd_tmp = hcd_root; // Keep a copy for free + hcd_root = hcd_root->next; + windows_hcd_priv_release(hcd_tmp); + safe_free(hcd_tmp); + } + + for (i=0; i<USB_API_MAX; i++) { + usb_api_backend[i].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; + } + } + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); +} + +static int windows_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) +{ + struct windows_device_priv *priv = __device_priv(dev); + + memcpy(buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = __device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + // config index is zero based + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, priv->config_descriptor[config_index], size); + + return LIBUSB_SUCCESS; +} + +/* + * return the cached copy of the active config descriptor + */ +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = __device_priv(dev); + + if (priv->active_config == 0) + return LIBUSB_ERROR_NOT_FOUND; + + // config index is zero based + return windows_get_config_descriptor(dev, (uint8_t)(priv->active_config-1), buffer, len, host_endian); +} + +static int windows_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + if (priv->apib == NULL) { + usbi_err(ctx, "program assertion failed - device is not initialized"); + return LIBUSB_ERROR_NO_DEVICE; + } + + return priv->apib->open(dev_handle); +} + +static void windows_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + priv->apib->close(dev_handle); +} + +static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + if (priv->active_config == 0) { + *config = 0; + return LIBUSB_ERROR_NOT_FOUND; + } + + *config = priv->active_config; + return LIBUSB_SUCCESS; +} + +/* + * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver + * does not currently expose a service that allows higher-level drivers to set + * the configuration." + */ +static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + int r = LIBUSB_SUCCESS; + + if (config >= USB_MAXCONFIG) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config, + 0, NULL, 0, 1000); + + if (r == LIBUSB_SUCCESS) { + priv->active_config = (uint8_t)config; + } + return r; +} + +static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + if (iface >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->claim_interface(dev_handle, iface); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle->dev, iface, 0); + } + + return r; +} + +static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->set_interface_altsetting(dev_handle, iface, altsetting); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle->dev, iface, altsetting); + } + + return r; +} + +static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + windows_set_interface_altsetting(dev_handle, iface, 0); + return priv->apib->release_interface(dev_handle, iface); +} + +static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->apib->clear_halt(dev_handle, endpoint); +} + +static int windows_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->apib->reset_device(dev_handle); +} + +// The 3 functions below are unlikely to ever get supported on Windows +static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void windows_destroy_device(struct libusb_device *dev) +{ + struct windows_device_priv *priv = __device_priv(dev); + windows_device_priv_release(priv, dev->num_configurations); +} + +static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + + usbi_free_fd(transfer_priv->pollable_fd.fd); + safe_free(transfer_priv->hid_buffer); +#if defined(AUTO_CLAIM) + // When auto claim is in use, attempt to release the auto-claimed interface + auto_release(itransfer); +#endif +} + +static int submit_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 windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_bulk_transfer(itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)((transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT)); +#if !defined(DYNAMIC_FDS) + usbi_fd_notification(ctx); +#endif + + return LIBUSB_SUCCESS; +} + +static int submit_iso_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 windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_iso_transfer(itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)((transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT)); +#if !defined(DYNAMIC_FDS) + usbi_fd_notification(ctx); +#endif + + return LIBUSB_SUCCESS; +} + +static int submit_control_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 windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_control_transfer(itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); +#if !defined(DYNAMIC_FDS) + usbi_fd_notification(ctx); +#endif + + return LIBUSB_SUCCESS; + +} + +static int windows_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int windows_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_control(itransfer); +} + +static int windows_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_transfers(itransfer); +} + +static int windows_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); +#if defined(FORCE_INSTANT_TIMEOUTS) + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + + // Forces instant overlapped completion on timeouts - use at your own risks + if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) { + transfer_priv->pollable_fd.overlapped->Internal &= ~STATUS_PENDING; + } +#endif + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return windows_abort_control(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return windows_abort_transfers(itransfer); + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void windows_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 windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int status; + + usbi_dbg("handling I/O completion with errcode %d", io_result); + + switch(io_result) { + case NO_ERROR: + status = priv->apib->copy_transfer_data(itransfer, io_size); + break; + 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(0)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + windows_clear_transfer_priv(itransfer); // Cancel polling + usbi_handle_transfer_completion(itransfer, status); +} + +static void windows_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: + windows_transfer_callback (itransfer, io_result, io_size); + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + } +} + +static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds, int num_ready) +{ + struct windows_transfer_priv* transfer_priv = NULL; + nfds_t 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) { + // Handle async requests that completed synchronously first + if (HasOverlappedIoCompletedSync(transfer_priv->pollable_fd.overlapped)) { + io_result = NO_ERROR; + io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; + // Regular async overlapped + } else if (GetOverlappedResult(transfer_priv->pollable_fd.handle, + transfer_priv->pollable_fd.overlapped, &io_size, false)) { + io_result = NO_ERROR; + } else { + io_result = GetLastError(); + } + 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. + windows_handle_callback(transfer, io_result, io_size); + } 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 windows_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. + windows_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 windows_clock_gettime(int clk_id, struct timespec *tp) +{ + FILETIME filetime; + ULARGE_INTEGER rtime; + DWORD r; + 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) + GetSystemTimeAsFileTime(&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; + } +} + + +// NB: MSVC6 does not support named initializers. +const struct usbi_os_backend windows_backend = { + "Windows", + windows_init, + windows_exit, + + windows_get_device_list, + windows_open, + windows_close, + + windows_get_device_descriptor, + windows_get_active_config_descriptor, + windows_get_config_descriptor, + + windows_get_configuration, + windows_set_configuration, + windows_claim_interface, + windows_release_interface, + + windows_set_interface_altsetting, + windows_clear_halt, + windows_reset_device, + + windows_kernel_driver_active, + windows_detach_kernel_driver, + windows_attach_kernel_driver, + + windows_destroy_device, + + windows_submit_transfer, + windows_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + + windows_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct windows_device_priv), + sizeof(struct windows_device_handle_priv), + sizeof(struct windows_transfer_priv), + 0, +}; + + +/* + * USB API backends + */ +static int unsupported_init(struct libusb_context *ctx) { + return LIBUSB_SUCCESS; +} +static int unsupported_exit(void) { + return LIBUSB_SUCCESS; +} +static int unsupported_open(struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(open); +} +static void unsupported_close(struct libusb_device_handle *dev_handle) { + usbi_dbg("unsupported API call for 'close'"); +} +static int unsupported_claim_interface(struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(claim_interface); +} +static int unsupported_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) { + PRINT_UNSUPPORTED_API(set_interface_altsetting); +} +static int unsupported_release_interface(struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(release_interface); +} +static int unsupported_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) { + PRINT_UNSUPPORTED_API(clear_halt); +} +static int unsupported_reset_device(struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(reset_device); +} +static int unsupported_submit_bulk_transfer(struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_bulk_transfer); +} +static int unsupported_submit_iso_transfer(struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_iso_transfer); +} +static int unsupported_submit_control_transfer(struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_control_transfer); +} +static int unsupported_abort_control(struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_control); +} +static int unsupported_abort_transfers(struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_transfers); +} +static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) { + PRINT_UNSUPPORTED_API(copy_transfer_data); +} + +// These names must be uppercase +const char* composite_driver_names[] = {"USBCCGP"}; +const char* winusb_driver_names[] = {"WINUSB"}; +const char* hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + USB_API_UNSUPPORTED, + "Unsupported API", + &CLASS_GUID_UNSUPPORTED, + NULL, + 0, + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { + USB_API_COMPOSITE, + "Composite API", + &CLASS_GUID_COMPOSITE, + composite_driver_names, + sizeof(composite_driver_names)/sizeof(composite_driver_names[0]), + composite_init, + composite_exit, + composite_open, + composite_close, + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_abort_control, + composite_abort_transfers, + composite_copy_transfer_data, + }, { + USB_API_WINUSB, + "WinUSB API", + &CLASS_GUID_LIBUSB_WINUSB, + winusb_driver_names, + sizeof(winusb_driver_names)/sizeof(winusb_driver_names[0]), + winusb_init, + winusb_exit, + winusb_open, + winusb_close, + winusb_claim_interface, + winusb_set_interface_altsetting, + winusb_release_interface, + winusb_clear_halt, + winusb_reset_device, + winusb_submit_bulk_transfer, + unsupported_submit_iso_transfer, + winusb_submit_control_transfer, + winusb_abort_control, + winusb_abort_transfers, + winusb_copy_transfer_data, + }, { + USB_API_HID, + "HID API", + &CLASS_GUID_HID, + hid_driver_names, + sizeof(hid_driver_names)/sizeof(hid_driver_names[0]), + hid_init, + hid_exit, + hid_open, + hid_close, + hid_claim_interface, + hid_set_interface_altsetting, + hid_release_interface, + hid_clear_halt, + hid_reset_device, + hid_submit_bulk_transfer, + unsupported_submit_iso_transfer, + hid_submit_control_transfer, + hid_abort_transfers, + hid_abort_transfers, + hid_copy_transfer_data, + }, +}; + + +/* + * WinUSB API functions + */ +static int winusb_init(struct libusb_context *ctx) +{ + DLL_LOAD(winusb.dll, WinUsb_Initialize, TRUE); + DLL_LOAD(winusb.dll, WinUsb_Free, TRUE); + DLL_LOAD(winusb.dll, WinUsb_GetAssociatedInterface, TRUE); + DLL_LOAD(winusb.dll, WinUsb_GetDescriptor, TRUE); + DLL_LOAD(winusb.dll, WinUsb_QueryInterfaceSettings, TRUE); + DLL_LOAD(winusb.dll, WinUsb_QueryDeviceInformation, TRUE); + DLL_LOAD(winusb.dll, WinUsb_SetCurrentAlternateSetting, TRUE); + DLL_LOAD(winusb.dll, WinUsb_GetCurrentAlternateSetting, TRUE); + DLL_LOAD(winusb.dll, WinUsb_QueryPipe, TRUE); + DLL_LOAD(winusb.dll, WinUsb_SetPipePolicy, TRUE); + DLL_LOAD(winusb.dll, WinUsb_GetPipePolicy, TRUE); + DLL_LOAD(winusb.dll, WinUsb_ReadPipe, TRUE); + DLL_LOAD(winusb.dll, WinUsb_WritePipe, TRUE); + DLL_LOAD(winusb.dll, WinUsb_ControlTransfer, TRUE); + DLL_LOAD(winusb.dll, WinUsb_ResetPipe, TRUE); + DLL_LOAD(winusb.dll, WinUsb_AbortPipe, TRUE); + DLL_LOAD(winusb.dll, WinUsb_FlushPipe, TRUE); + + api_winusb_available = true; + return LIBUSB_SUCCESS; +} + +static int winusb_exit(void) +{ + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int winusb_open(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + + HANDLE file_handle; + int i; + + CHECK_WINUSB_AVAILABLE; + + // WinUSB requires a seperate handle for each interface + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSB) ) { + file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].dev_handle = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void winusb_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + HANDLE file_handle; + int i; + + if (!api_winusb_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_WINUSB) { + file_handle = handle_priv->interface_handle[i].dev_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + bool is_composite = (priv->apib->id == USB_API_COMPOSITE); + HANDLE file_handle, winusb_handle; + USB_INTERFACE_DESCRIPTOR if_desc; + UCHAR policy; + uint8_t endpoint_address; + int i; + + CHECK_WINUSB_AVAILABLE; + + // interfaces for composite devices are always independent, therefore + // "alt" interfaces are only found on non-composite + if ((!is_composite) && (iface != 0)) { + winusb_handle = handle_priv->interface_handle[0].api_handle; + // It is a requirement on Windows that to claim an interface >= 1 + // on a non-composite WinUSB device, you must first have claimed interface 0 + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { +#if defined(AUTO_CLAIM) + file_handle = handle_priv->interface_handle[0].dev_handle; + if (WinUsb_Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[0].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface); + } else { + usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB)", iface); + return LIBUSB_ERROR_ACCESS; + } +#else + usbi_warn(ctx, "you must claim interface 0 before you can claim %d with WinUSB", iface); + return LIBUSB_ERROR_ACCESS; +#endif + } + if (!WinUsb_GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1), + &handle_priv->interface_handle[iface].api_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + switch(GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + } else { + // composite device (independent interfaces) or interface 0 + winusb_handle = handle_priv->interface_handle[iface].api_handle; + file_handle = handle_priv->interface_handle[iface].dev_handle; + if ((file_handle == 0) || (file_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUsb_Initialize(file_handle, &winusb_handle)) { + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + switch(GetLastError()) { + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + handle_priv->interface_handle[iface].api_handle = winusb_handle; + } + if (!WinUsb_QueryInterfaceSettings(winusb_handle, 0, &if_desc)) { + usbi_err(ctx, "could not query interface settings for interface %d: %s", iface, windows_error_str(0)); + } else if (if_desc.bInterfaceNumber != iface) { + usbi_warn(ctx, "program assertion failed - WinUSB interface %d found at position %d", + if_desc.bInterfaceNumber, iface); + } + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + // With handle and enpoints set (in parent), we can setup the default + // pipe properties (copied from libusb-win32-v1) + // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx + for (i=0; i<priv->usb_interface[iface].nb_endpoints; i++) { + endpoint_address = priv->usb_interface[iface].endpoint[i]; + policy = false; + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address); + } + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address); + } + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); + } + policy = true; + if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address); + } + } + + return LIBUSB_SUCCESS; +} + +static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + HANDLE winusb_handle; + + CHECK_WINUSB_AVAILABLE; + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + WinUsb_Free(winusb_handle); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +/* + * Return the first valid interface (of the same API type), for control transfers + */ +static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + int i; + + if ((api_id < USB_API_WINUSB) || (api_id > USB_API_HID)) { + usbi_dbg("unsupported API ID"); + return -1; + } + + for (i=0; i<USB_MAXINTERFACES; i++) { + if ( (handle_priv->interface_handle[i].dev_handle != 0) + && (handle_priv->interface_handle[i].dev_handle != INVALID_HANDLE_VALUE) + && (handle_priv->interface_handle[i].api_handle != 0) + && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE) + && (priv->usb_interface[i].apib == &usb_api_backend[api_id]) ) { + return i; + } + } + return -1; +} + +/* + * Lookup interface by endpoint address. -1 if not found + */ +static int interface_by_endpoint(struct windows_device_priv *priv, + struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address) +{ + int i, j; + for (i=0; i<USB_MAXINTERFACES; i++) { + if (handle_priv->interface_handle[i].api_handle == INVALID_HANDLE_VALUE) + continue; + if (handle_priv->interface_handle[i].api_handle == 0) + continue; + if (priv->usb_interface[i].endpoint == NULL) + continue; + for (j=0; j<priv->usb_interface[i].nb_endpoints; j++) { + if (priv->usb_interface[i].endpoint[j] == endpoint_address) { + return i; + } + } + } + return -1; +} + +static int winusb_submit_control_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 windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = __device_handle_priv( + transfer->dev_handle); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + ULONG size; + HANDLE winusb_handle; + int current_interface; + struct winfd wfd; + + CHECK_WINUSB_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSB); + if (current_interface < 0) { +#if defined(AUTO_CLAIM) + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } +#else + usbi_warn(ctx, "no interface available for control transfer"); + return LIBUSB_ERROR_NOT_FOUND; +#endif + } + + usbi_dbg("will use interface %d", current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, _O_RDONLY); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(wfd.fd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)size; + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + HANDLE winusb_handle; + + CHECK_WINUSB_AVAILABLE; + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + usbi_err(ctx, "interface must be claimed first"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUsb_SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) { + usbi_err(ctx, "WinUsb_SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int winusb_submit_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 windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + bool direction_in, ret; + int current_interface; + struct winfd wfd; + + CHECK_WINUSB_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + if (direction_in) { + usbi_dbg("reading %d bytes", transfer->length); + ret = WinUsb_ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } else { + usbi_dbg("writing %d bytes", transfer->length); + ret = WinUsb_WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } + if (!ret) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "WinUsb_Pipe Transfer failed: %s", windows_error_str(0)); + usbi_free_fd(wfd.fd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)transfer->length; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSB_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUsb_ResetPipe(winusb_handle, endpoint)) { + usbi_err(ctx, "WinUsb_ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed + * through testing as well): + * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel + * the control transfer using CancelIo" + */ +static int winusb_abort_control(struct usbi_transfer *itransfer) +{ + // Cancelling of the I/O is done in the parent + return LIBUSB_SUCCESS; +} + +static int winusb_abort_transfers(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 windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSB_AVAILABLE; + + current_interface = transfer_priv->interface_number; + if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { + usbi_err(ctx, "program assertion failed: invalid interface_number"); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_dbg("will use interface %d", current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUsb_AbortPipe(winusb_handle, transfer->endpoint)) { + usbi_err(ctx, "WinUsb_AbortPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper + * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx): + * "WinUSB does not support host-initiated reset port and cycle port operations" and + * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the + * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is + * cycle the pipes (and even then, the control pipe can not be reset using WinUSB) + */ +// TODO (2nd official release): see if we can force eject the device and redetect it (reuse hotplug?) +static int winusb_reset_device(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct winfd wfd; + HANDLE winusb_handle; + int i, j; + + CHECK_WINUSB_AVAILABLE; + + // Reset any available pipe (except control) + for (i=0; i<USB_MAXINTERFACES; i++) { + winusb_handle = handle_priv->interface_handle[i].api_handle; + for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0;) + { + // Cancel any pollable I/O + usbi_remove_pollfd(ctx, wfd.fd); + usbi_free_fd(wfd.fd); + wfd = handle_to_winfd(winusb_handle); + } + + if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + for (j=0; j<priv->usb_interface[i].nb_endpoints; j++) { + usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]); + if (!WinUsb_AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "WinUsb_AbortPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + // FlushPipe seems to fail on OUT pipes + if ( (priv->usb_interface[i].endpoint[j] & LIBUSB_ENDPOINT_IN) + && (!WinUsb_FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) { + usbi_err(ctx, "WinUsb_FlushPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + if (!WinUsb_ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "WinUsb_ResetPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + } + } + } + + return LIBUSB_SUCCESS; +} + +static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + while (str[i] && (str[i] != 0x409)) { + i++; + } + return i; +} + +static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = 0x0200; /* 2.00 */ + d.bDeviceClass = 0; + d.bDeviceSubClass = 0; + d.bDeviceProtocol = 0; + d.bMaxPacketSize0 = 64; /* fix this! */ + d.idVendor = (uint16_t)dev->vid; + d.idProduct = (uint16_t)dev->pid; + d.bcdDevice = 0x0100; + d.iManufacturer = dev->string_index[0]; + d.iProduct = dev->string_index[1]; + d.iSerialNumber = dev->string_index[2]; + d.bNumConfigurations = 1; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (dev->input_report_size) + num_endpoints++; + if (dev->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t) config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(dev, hd, &tmp_size); + + if (dev->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->input_report_size - 1; + ed->bInterval = 10; + + ed++; + } + + if (dev->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv* dev, int index, + void *data, size_t *size) +{ + void *tmp = NULL; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = { + 0x09, + 0x04 + }; + + if ((*size < 2) || (*size > 255)) { + return LIBUSB_ERROR_OVERFLOW; + } + + if (index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid)+2; + } else { + for (i=0; i<3; i++) { + if (index == (dev->string_index[i])) { + tmp = dev->string[i]; + tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR); + break; + } + } + if (i == 3) { // not found + return LIBUSB_ERROR_INVALID_PARAM; + } + } + + if(!tmp_size) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (tmp_size < *size) { + *size = tmp_size; + } + // 2 byte header + ((uint8_t*)data)[0] = (uint8_t)*size; + ((uint8_t*)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t*)data+2, tmp, *size-2); + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(dev, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page (0xFFA0 == vendor defined) */ + d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF; + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (dev->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (dev->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (dev->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient, + int type, int index, void *data, size_t *size) +{ + switch(type) { + case LIBUSB_DT_DEVICE: + usbi_dbg("LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, data, size); + case LIBUSB_DT_CONFIG: + usbi_dbg("LIBUSB_DT_CONFIG"); + if (!index) + return _hid_get_config_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_STRING: + usbi_dbg("LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(dev, index, data, size); + case LIBUSB_DT_HID: + usbi_dbg("LIBUSB_DT_HID"); + if (!index) + return _hid_get_hid_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_REPORT: + usbi_dbg("LIBUSB_DT_REPORT"); + if (!index) + return _hid_get_report_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_PHYSICAL: + usbi_dbg("LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + usbi_dbg("unsupported"); + return LIBUSB_ERROR_INVALID_PARAM; +} + +static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // When report IDs are not in use, add an extra byte for the report ID + if (id==0) { + expected_size++; + } + + // Add a trailing byte to detect overflows + buf = (uint8_t*)calloc(expected_size+1, 1); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + buf[0] = (uint8_t)id; // Must be set always + usbi_dbg("report ID: 0x%02X", buf[0]); + + tp->hid_expected_size = expected_size; + + if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1, + buf, expected_size+1, &read_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously => copy and discard extra buffer + if (read_size == 0) { + usbi_dbg("program assertion failed - read completed synchronously, but no data was read"); + *size = 0; + } else { + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + if ((size_t)read_size > expected_size) { + r = LIBUSB_ERROR_OVERFLOW; + usbi_dbg("OVERFLOW!"); + } else { + r = LIBUSB_COMPLETED; + } + + if (id == 0) { + // Discard report ID + *size = MIN((size_t)read_size-1, *size); + memcpy(data, buf+1, *size); + } else { + *size = MIN((size_t)read_size, *size); + memcpy(data, buf, *size); + } + } + safe_free(buf); + return r; +} + +static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf = NULL; + DWORD ioctl_code, write_size= (DWORD)*size; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg("report ID: 0x%02X", id); + // When report IDs are not used (i.e. when id == 0), we must add + // a null report ID. Otherwise, we just use original data buffer + if (id == 0) { + write_size++; + } + buf = malloc(write_size); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, *size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, *size); + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + } + + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, &write_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously + if (write_size == 0) { + usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); + *size = 0; + } else { + *size = write_size - ((id == 0)?1:0); + } + safe_free(buf); + return LIBUSB_COMPLETED; +} + +static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type, + int request, int value, int index, void *data, struct windows_transfer_priv *tp, + size_t *size, OVERLAPPED* overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) ) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + + +/* + * HID API functions + */ +static int hid_init(struct libusb_context *ctx) +{ + DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE); + DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE); + DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE); + DLL_LOAD(hid.dll, HidD_GetProductString, TRUE); + DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE); + DLL_LOAD(hid.dll, HidP_GetCaps, TRUE); + DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD(hid.dll, HidD_SetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE); + DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE); + DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE); + DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(void) +{ + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + + HIDD_ATTRIBUTES hid_attributes; + PHIDP_PREPARSED_DATA preparsed_data = NULL; + HIDP_CAPS capabilities; + HIDP_VALUE_CAPS *value_caps; + + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + char* type[3] = {"input", "output", "feature"}; + int nb_ids[2]; // zero and nonzero report IDs + + CHECK_HID_AVAILABLE; + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - private HID structure is unitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID) ) { + hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2); + usbi_dbg("set maximum input buffer size to %d", i/2); + + // Get the maximum input and output report size + if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) { + usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j=0; j<3; j++) { + usbi_dbg("%d HID %s report value(s) found", size[j], type[j]); + priv->hid->uses_report_ids[j] = false; + if (size[j] > 0) { + value_caps = malloc(size[j] * sizeof(HIDP_VALUE_CAPS)); + if ( (value_caps != NULL) + && (HidP_GetValueCaps(j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1) ) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i=0; i<(int)size[j]; i++) { + usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) { + nb_ids[1]++; + } else { + nb_ids[0]++; + } + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) { + usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s", + type[j]); + } + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(ctx, " could not process %s report IDs", type[j]); + } + safe_free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Fetch string descriptors + priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) { + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], + sizeof(priv->hid->string[0])); + } else { + priv->hid->string[0][0] = 0; + } + priv->hid->string_index[1] = priv->dev_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) { + HidD_GetProductString(hid_handle, priv->hid->string[1], + sizeof(priv->hid->string[1])); + } else { + priv->hid->string[1][0] = 0; + } + priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) { + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], + sizeof(priv->hid->string[2])); + } else { + priv->hid->string[2][0] = 0; + } + } while(0); + + if (preparsed_data) { + HidD_FreePreparsedData(preparsed_data); + } + + return LIBUSB_SUCCESS; +} + +static void hid_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) { + return LIBUSB_ERROR_BUSY; // already claimed + } + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (altsetting != 0) { + usbi_err(ctx, "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + HANDLE hid_handle; + struct winfd wfd; + int current_interface, config; + size_t size; + int r = LIBUSB_ERROR_INVALID_PARAM; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID); + if (current_interface < 0) { +#if defined(AUTO_CLAIM) + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } +#else + usbi_warn(ctx, "no interface available for control transfer"); + return LIBUSB_ERROR_NOT_FOUND; +#endif + } + + usbi_dbg("will use interface %d", current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + // Always use the handle returned from usbi_create_fd (wfd.handle) + wfd = usbi_create_fd(hid_handle, _O_RDONLY); + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + switch(LIBUSB_REQ_TYPE(setup->request_type)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch(setup->request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type), + (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = windows_get_configuration(transfer->dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(ctx, "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_INVALID_PARAM; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(transfer->dev_handle, setup->index, setup->value); + if (r == LIBUSB_SUCCESS) { + r = LIBUSB_COMPLETED; + } + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value, + setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + &size, wfd.overlapped); + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx + // set InternalHigh to the number of bytes transferred + wfd.overlapped->InternalHigh = (DWORD)size; + r = LIBUSB_SUCCESS; + } + + if (r == LIBUSB_SUCCESS) { + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + } else { + usbi_free_fd(wfd.fd); + } + + return r; +} + +static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + struct winfd wfd; + HANDLE hid_handle; + bool direction_in, ret; + int current_interface, length; + DWORD size; + int r = LIBUSB_SUCCESS; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + transfer_priv->hid_dest = NULL; + safe_free(transfer_priv->hid_buffer); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(hid_handle, direction_in?_O_RDONLY:_O_WRONLY); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // If report IDs are not in use, an extra prefix byte must be added + if ( ((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) { + length = transfer->length+1; + } else { + length = transfer->length; + } + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1); + if (transfer_priv->hid_buffer == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg("reading %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped); + } else { + if (!priv->hid->uses_report_ids[1]) { + memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length); + } else { + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + } + usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped); + } + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0)); + usbi_free_fd(wfd.fd); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + } else { + // Only write operations that completed synchronously need to free up + // hid_buffer. For reads, copy_transfer_data() handles that process. + if (!direction_in) { + safe_free(transfer_priv->hid_buffer); + } + if (size == 0) { + usbi_err(ctx, "program assertion failed - no data was transferred"); + size = 1; + } + if (size > (size_t)length) { + usbi_err(ctx, "OVERFLOW!"); + r = LIBUSB_ERROR_OVERFLOW; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = size; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return r; +} + +static int hid_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = transfer_priv->interface_number; + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + CancelIo(hid_handle); + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) { + HidD_FlushQueue(hid_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + + // No endpoint selection with Microsoft's implementation, so we try to flush the + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) { + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + int r = LIBUSB_TRANSFER_COMPLETED; + uint32_t corrected_size = io_size; + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + // First, check for overflow + if (corrected_size > transfer_priv->hid_expected_size) { + usbi_err(ctx, "OVERFLOW!"); + corrected_size = (uint32_t)transfer_priv->hid_expected_size; + r = LIBUSB_TRANSFER_OVERFLOW; + } + + if (transfer_priv->hid_buffer[0] == 0) { + // Discard the 1 byte report ID prefix + corrected_size--; + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size); + } else { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size); + } + transfer_priv->hid_dest = NULL; + } + // For write, we just need to free the hid buffer + safe_free(transfer_priv->hid_buffer); + } + itransfer->transferred += corrected_size; + return r; +} + + +/* + * Composite API functions + */ +static int composite_init(struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int composite_exit(void) +{ + return LIBUSB_SUCCESS; +} + +static int composite_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + int r; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + r = usb_api_backend[api].open(dev_handle); + if (r != LIBUSB_SUCCESS) { + return r; + } + } + flag <<= 1; + } + return LIBUSB_SUCCESS; +} + +static void composite_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + usb_api_backend[api].close(dev_handle); + } + flag <<= 1; + } +} + +static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->claim_interface(dev_handle, iface); +} + +static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->set_interface_altsetting(dev_handle, iface, altsetting); +} + +static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib->release_interface(dev_handle, iface); +} + +static int composite_submit_control_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 windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int i, pass; + + // Interface shouldn't matter for control, but it does in practice, with Windows' + // restrictions with regards to accessing HID keyboards and mice. Try a 2 pass approach + for (pass = 0; pass < 2; pass++) { + for (i=0; i<USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].path != NULL) { + if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) { + usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i); + continue; + } + usbi_dbg("using interface %d", i); + return priv->usb_interface[i].apib->submit_control_transfer(itransfer); + } + } + } + + usbi_err(ctx, "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int composite_submit_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 windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->submit_bulk_transfer(itransfer); +} + +static int composite_submit_iso_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 windows_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->submit_iso_transfer(itransfer); +} + +static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle); + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib->clear_halt(dev_handle, endpoint); +} + +static int composite_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib->abort_control(itransfer); +} + +static int composite_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib->abort_transfers(itransfer); +} + +static int composite_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + unsigned api; + int r; + uint8_t flag = 1<<USB_API_WINUSB; + + for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { + if (priv->composite_api_flags & flag) { + r = usb_api_backend[api].reset_device(dev_handle); + if (r != LIBUSB_SUCCESS) { + return r; + } + } + flag <<= 1; + } + return LIBUSB_SUCCESS; +} + +static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +{ + struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib->copy_transfer_data(itransfer, io_size); +} diff --git a/include/libusb-1.0/os/windows_usb.h b/include/libusb-1.0/os/windows_usb.h new file mode 100644 index 0000000..dc90a64 --- /dev/null +++ b/include/libusb-1.0/os/windows_usb.h @@ -0,0 +1,784 @@ +/* + * Windows backend for libusb 1.0 + * 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 + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#endif + +// Windows API default is uppercase - ugh! +#if !defined(bool) +#define bool BOOL +#endif +#if !defined(true) +#define true TRUE +#endif +#if !defined(false) +#define false FALSE +#endif + +#if !defined(libusb_bus_t) +#define libusb_bus_t uint8_t +#define LIBUSB_BUS_MAX UINT8_MAX +#endif +#if !defined(libusb_devaddr_t) +#define libusb_devaddr_t uint8_t +#define LIBUSB_DEVADDR_MAX UINT8_MAX +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +#if defined(__CYGWIN__ ) +// cygwin produces a warning unless these prototypes are defined +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern char *_strdup(const char *strSource); +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) +#endif +#define safe_free(p) do {if (p != NULL) {free((void*)p); p = NULL;}} while(0) +#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) +#define safe_min(a, b) min((size_t)(a), (size_t)(b)) +#define safe_strcp(dst, dst_max, src, count) do {memcpy(dst, src, safe_min(count, dst_max)); \ + ((char*)dst)[safe_min(count, dst_max)-1] = 0;} while(0) +#define safe_strcpy(dst, dst_max, src) safe_strcp(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, safe_min(count, dst_max - safe_strlen(dst) - 1)) +#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2)) +#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2), count) +#define safe_strlen(str) ((str==NULL)?0:strlen(str)) +#define safe_sprintf _snprintf +#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) +#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) +inline void upperize(char* str) { + size_t i; + if (str == NULL) return; + for (i=0; i<safe_strlen(str); i++) + str[i] = (char)toupper((int)str[i]); +} + +#define MAX_CTRL_BUFFER_LENGTH 4096 +#define MAX_USB_DEVICES 256 +#define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 +#define MAX_GUID_STRING_LENGTH 40 +#define MAX_PATH_LENGTH 128 +#define MAX_KEY_LENGTH 256 +#define MAX_TIMER_SEMAPHORES 128 +#define TIMER_REQUEST_RETRY_MS 100 +#define ERR_BUFFER_SIZE 256 +#define LIST_SEPARATOR ';' + +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + +// http://msdn.microsoft.com/en-us/library/bb663109.aspx +// http://msdn.microsoft.com/en-us/library/bb663093.aspx +#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) +const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_DEVICE) +const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; +#endif + + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_COMPOSITE 1 +#define USB_API_WINUSB 2 +#define USB_API_HID 3 +#define USB_API_MAX 4 + +const GUID CLASS_GUID_UNSUPPORTED = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x57, 0xDA} }; +const GUID CLASS_GUID_HID = { 0x745A17A0, 0x74D3, 0x11D0, {0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA} }; +const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} }; +const GUID CLASS_GUID_COMPOSITE = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; + +struct windows_usb_api_backend { + const uint8_t id; + const char* designation; + const GUID *class_guid; // The Class GUID (for fallback in case the driver name cannot be read) + const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp" + const uint8_t nb_driver_names; + int (*init)(struct libusb_context *ctx); + int (*exit)(void); + int (*open)(struct libusb_device_handle *dev_handle); + void (*close)(struct libusb_device_handle *dev_handle); + int (*claim_interface)(struct libusb_device_handle *dev_handle, int iface); + int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, int iface, int altsetting); + int (*release_interface)(struct libusb_device_handle *dev_handle, int iface); + int (*clear_halt)(struct libusb_device_handle *dev_handle, unsigned char endpoint); + int (*reset_device)(struct libusb_device_handle *dev_handle); + int (*submit_bulk_transfer)(struct usbi_transfer *itransfer); + int (*submit_iso_transfer)(struct usbi_transfer *itransfer); + int (*submit_control_transfer)(struct usbi_transfer *itransfer); + int (*abort_control)(struct usbi_transfer *itransfer); + int (*abort_transfers)(struct usbi_transfer *itransfer); + int (*copy_transfer_data)(struct usbi_transfer *itransfer, uint32_t io_size); +}; + +extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; + +#define PRINT_UNSUPPORTED_API(fname) \ + usbi_dbg("unsupported API call for '" \ + #fname "' (unrecognized device driver)"); \ + return LIBUSB_ERROR_NOT_SUPPORTED; + +/* + * private structures definition + * with inline pseudo constructors/destructors + */ + +// HCDs +struct windows_hcd_priv { + char *path; + struct windows_hcd_priv *next; +}; + +static inline void windows_hcd_priv_init(struct windows_hcd_priv* p) { + p->path = NULL; + p->next = NULL; +} + +static inline void windows_hcd_priv_release(struct windows_hcd_priv* p) { + safe_free(p->path); +} + +// TODO (v2+): move hid desc to libusb.h? +struct libusb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bClassDescriptorType; + uint16_t wClassDescriptorLength; +}; +#define LIBUSB_DT_HID_SIZE 9 +#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \ + + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE) +#define HID_MAX_REPORT_SIZE 1024 +#define HID_IN_EP 0x81 +#define HID_OUT_EP 0x02 +#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F) +#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5)) +#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN) +#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) + +// The following are used for HID reports IOCTLs +#define HID_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS) +#define HID_BUFFER_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) +#define HID_IN_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define HID_OUT_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + +#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) +#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) +#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100) +#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101) + +enum libusb_hid_request_type { + HID_REQ_GET_REPORT = 0x01, + HID_REQ_GET_IDLE = 0x02, + HID_REQ_GET_PROTOCOL = 0x03, + HID_REQ_SET_REPORT = 0x09, + HID_REQ_SET_IDLE = 0x0A, + HID_REQ_SET_PROTOCOL = 0x0B +}; + +enum libusb_hid_report_type { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03 +}; + +struct hid_device_priv { + uint16_t vid; + uint16_t pid; + uint8_t config; + bool uses_report_ids[3]; // input, ouptput, feature + uint16_t input_report_size; + uint16_t output_report_size; + uint16_t feature_report_size; + WCHAR string[3][MAX_USB_STRING_LENGTH]; + uint8_t string_index[3]; // man, prod, ser +}; + +typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; +struct windows_device_priv { + struct libusb_device *parent_dev; // access to parent is required for usermode ops + ULONG connection_index; // also required for some usermode ops + char *path; // path used by Windows to reference the USB node + struct windows_usb_api_backend const *apib; + struct { + char *path; // each interface needs a Windows device interface path, + struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support), + int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) + uint8_t *endpoint; + bool restricted_functionality; // indicates if the interface functionality is restricted + // by Windows (eg. HID keyboards or mice cannot do R/W) + } usb_interface[USB_MAXINTERFACES]; + uint8_t composite_api_flags; // HID and composite devices require additional data + struct hid_device_priv *hid; + uint8_t active_config; + USB_DEVICE_DESCRIPTOR dev_descriptor; + unsigned char **config_descriptor; // list of pointers to the cached config descriptors +}; + +static inline void windows_device_priv_init(struct windows_device_priv* p) { + int i; + p->parent_dev = NULL; + p->connection_index = 0; + p->path = NULL; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->composite_api_flags = 0; + p->hid = NULL; + p->active_config = 0; + p->config_descriptor = NULL; + memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; i<USB_MAXINTERFACES; i++) { + p->usb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].nb_endpoints = 0; + p->usb_interface[i].endpoint = NULL; + p->usb_interface[i].restricted_functionality = false; + } +} + +static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) { + int i; + safe_free(p->path); + if ((num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < num_configurations; i++) + safe_free(p->config_descriptor[i]); + } + safe_free(p->config_descriptor); + safe_free(p->hid); + for (i=0; i<USB_MAXINTERFACES; i++) { + safe_free(p->usb_interface[i].path); + safe_free(p->usb_interface[i].endpoint); + } +} + +static inline struct windows_device_priv *__device_priv(struct libusb_device *dev) { + return (struct windows_device_priv *)dev->os_priv; +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; +#if defined(AUTO_CLAIM) + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +#endif +}; + +static inline struct windows_device_handle_priv *__device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *) handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; + uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID + uint8_t *hid_dest; // transfer buffer destination, required for HID + size_t hid_expected_size; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retreive list + const char* designation; // internal designation (for debug output) +}; + +/* + * API macros - from libusb-win32 1.x + */ +#define DLL_DECLARE(api, ret, name, args) \ + typedef ret (api * __dll_##name##_t)args; __dll_##name##_t name = NULL + +#define DLL_LOAD(dll, name, ret_on_failure) \ + do { \ + HMODULE h = GetModuleHandle(#dll); \ + if (!h) \ + h = LoadLibrary(#dll); \ + if (!h) { \ + if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\ + else { break; } \ + } \ + name = (__dll_##name##_t)GetProcAddress(h, #name); \ + if (name) break; \ + name = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ + if (name) break; \ + name = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ + if (name) break; \ + if(ret_on_failure) \ + return LIBUSB_ERROR_NOT_FOUND; \ + } while(0) + + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define HCD_GET_ROOT_HUB_NAME 258 +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_NODE_CONNECTION_INFORMATION 259 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access)( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum _USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum _USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDW, (DEVINST, PWCHAR, ULONG, ULONG)); + +#ifdef UNICODE +#define CM_Get_Device_ID CM_Get_Device_IDW +#else +#define CM_Get_Device_ID CM_Get_Device_IDA +#endif /* UNICODE */ + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct _USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct _USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct _USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct _USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct _USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct _USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct _USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct _USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct _USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct _USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct _USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + + +typedef struct _USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct _USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct _USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct _USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct _USB_NODE_CONNECTION_INFORMATION { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + BOOLEAN LowSpeed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION, *PUSB_NODE_CONNECTION_INFORMATION; + +typedef struct _USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct _USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable : 1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct _USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum _USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); + +/* hid.dll interface */ + +#define HIDP_STATUS_SUCCESS 0x110000 +typedef void* PHIDP_PREPARSED_DATA; + +#pragma pack(1) +typedef struct { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; +#pragma pack() + +typedef USHORT USAGE; +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + } u; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); +DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); diff --git a/src/irecovery.c b/irecovery.c index 0e981cd..39ac425 100644 --- a/src/irecovery.c +++ b/irecovery.c @@ -1,19 +1,20 @@ /** - * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode - * Copyright (C) 2008 - 2009 westbaer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * GreenPois0n iRecovery - irecovery.c + * Copyright (C) 2010 Chronic-Dev Team + * Copyright (C) 2010 Joshua Hill + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include <stdio.h> @@ -63,7 +64,7 @@ void parse_command(irecv_client_t client, unsigned char* command, unsigned int s char* filename = strtok(NULL, " "); debug("Uploading files %s\n", filename); if (filename != NULL) { - irecv_send_file(client, filename); + irecv_send_file(client, filename, 0); } } else @@ -71,7 +72,7 @@ void parse_command(irecv_client_t client, unsigned char* command, unsigned int s char* filename = strtok(NULL, " "); debug("Sending exploit %s\n", filename); if (filename != NULL) { - irecv_send_file(client, filename); + irecv_send_file(client, filename, 0); } irecv_send_exploit(client); } else @@ -225,7 +226,7 @@ void print_usage() { exit(1); } -int main(int argc, char** argv) { +int main(int argc, char* argv[]) { int i = 0; int opt = 0; int action = 0; @@ -299,7 +300,7 @@ int main(int argc, char** argv) { case kSendFile: irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); - error = irecv_send_file(client, argument); + error = irecv_send_file(client, argument, 0); debug("%s\n", irecv_strerror(error)); break; @@ -311,7 +312,7 @@ int main(int argc, char** argv) { case kSendExploit: if (argument != NULL) { irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); - error = irecv_send_file(client, argument); + error = irecv_send_file(client, argument, 0); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; diff --git a/libirecovery.c b/libirecovery.c new file mode 100644 index 0000000..f3d6f28 --- /dev/null +++ b/libirecovery.c @@ -0,0 +1,1234 @@ +/** + * GreenPois0n iRecovery - libirecovery.c + * Copyright (C) 2010 Chronic-Dev Team + * Copyright (C) 2010 Joshua Hill + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef WIN32 +#include <libusb-1.0/libusb.h> +#else +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <setupapi.h> +#endif + +#include "libirecovery.h" + +#define BUFFER_SIZE 0x1000 +#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) + +static int libirecovery_debug = 0; +#ifndef WIN32 +static libusb_context* libirecovery_context = NULL; +#endif + +int irecv_write_file(const char* filename, const void* data, size_t size); +int irecv_read_file(const char* filename, char** data, uint32_t* size); + +#ifdef WIN32 +static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; +static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; + +typedef struct usb_control_request { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + + char data[]; +} usb_control_request; + +irecv_error_t mobiledevice_openpipes(irecv_client_t client); +void mobiledevice_closepipes(irecv_client_t client); + +irecv_error_t mobiledevice_connect(irecv_client_t* client) { + irecv_error_t ret; + + SP_DEVICE_INTERFACE_DATA currentInterface; + HDEVINFO usbDevices; + DWORD i; + LPSTR path; + irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client)); + memset(_client, 0, sizeof(struct irecv_client)); + + // Get DFU paths + usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(!usbDevices) { + return IRECV_E_UNABLE_TO_CONNECT; + } + currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) { + DWORD requiredSize = 0; + PSP_DEVICE_INTERFACE_DETAIL_DATA details; + SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); + details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { + irecv_close(_client); + free(details); + SetupDiDestroyDeviceInfoList(usbDevices); + return IRECV_E_UNABLE_TO_CONNECT; + } else { + LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); + memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); + free(details); + path = (LPSTR) malloc(requiredSize - sizeof(DWORD)); + memcpy((void*) path, (void*) result, requiredSize - sizeof(DWORD)); + TCHAR* pathEnd = strstr(path, "#{"); + *pathEnd = '\0'; + _client->DfuPath = result; + break; + } + } + SetupDiDestroyDeviceInfoList(usbDevices); + // Get iBoot path + usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(!usbDevices) { + irecv_close(_client); + return IRECV_E_UNABLE_TO_CONNECT; + } + currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) { + DWORD requiredSize = 0; + PSP_DEVICE_INTERFACE_DETAIL_DATA details; + SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); + details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { + irecv_close(_client); + free(details); + SetupDiDestroyDeviceInfoList(usbDevices); + return IRECV_E_UNABLE_TO_CONNECT; + } else { + LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); + memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); + free(details); + + if(strstr(result, path) == NULL) { + free(result); + continue; + } + + _client->iBootPath = result; + break; + } + } + SetupDiDestroyDeviceInfoList(usbDevices); + free(path); + + ret = mobiledevice_openpipes(_client); + if (ret != IRECV_E_SUCCESS) return ret; + + *client = _client; + return IRECV_E_SUCCESS; +} + +irecv_error_t mobiledevice_openpipes(irecv_client_t client) { + if (client->iBootPath && !(client->hIB = CreateFile(client->iBootPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { + irecv_close(client); + return IRECV_E_UNABLE_TO_CONNECT; + } + if (client->DfuPath && !(client->hDFU = CreateFile(client->DfuPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { + irecv_close(client); + return IRECV_E_UNABLE_TO_CONNECT; + } + + if (client->iBootPath == NULL) { + client->mode = kDfuMode; + client->handle = client->hDFU; + } else { + client->mode = kRecoveryMode2; + client->handle = client->hIB; + } + + return IRECV_E_SUCCESS; +} + +void mobiledevice_closepipes(irecv_client_t client) { + if (client->hDFU!=NULL) { + CloseHandle(client->hDFU); + client->hDFU = NULL; + } + if (client->hIB!=NULL) { + CloseHandle(client->hIB); + client->hIB = NULL; + } +} +#endif + +int check_context(irecv_client_t client) { + if (client == NULL || client->handle == NULL) { + return IRECV_E_NO_DEVICE; + } + + return IRECV_E_SUCCESS; +} + +void irecv_init() { +#ifndef WIN32 + libusb_init(&libirecovery_context); +#endif +} + +void irecv_exit() { +#ifndef WIN32 + if (libirecovery_context != NULL) { + libusb_exit(libirecovery_context); + libirecovery_context = NULL; + } +#endif +} + +#ifdef __APPLE__ + void dummy_callback() { } +#endif + +int irecv_control_transfer( irecv_client_t client, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout) { +#ifndef WIN32 + return libusb_control_transfer(client->handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); +#else + DWORD count = 0; + DWORD ret; + BOOL bRet; + OVERLAPPED overlapped; + + if (data == NULL) wLength = 0; + + usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + wLength); + packet->bmRequestType = bmRequestType; + packet->bRequest = bRequest; + packet->wValue = wValue; + packet->wIndex = wIndex; + packet->wLength = wLength; + + if (bmRequestType < 0x80 && wLength > 0) { + memcpy(packet->data, data, wLength); + } + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + wLength, packet, sizeof(usb_control_request) + wLength, NULL, &overlapped); + ret = WaitForSingleObject(overlapped.hEvent, timeout); + bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE); + CloseHandle(overlapped.hEvent); + if (!bRet) { + CancelIo(client->handle); + free(packet); + return -1; + } + + count -= sizeof(usb_control_request); + if (count > 0) { + if (bmRequestType >= 0x80) { + memcpy(data, packet->data, count); + } + } + free(packet); + return count; +#endif +} + +int irecv_bulk_transfer(irecv_client_t client, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) { +#ifndef WIN32 + return libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout); +#else + int ret; + if (endpoint==0x4) { + ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL); + } else { + ret = 0; + } + return ret==0?-1:0; +#endif +} + +int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { +#ifndef WIN32 + return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size); +#else + irecv_error_t ret; + unsigned short langid = 0; + unsigned char data[255]; + int di, si; + memset(data, 0, sizeof(data)); + memset(buffer, 0, size); + + ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), 1000); + + if (ret < 0) return ret; + if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; + if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR; + + for (di = 0, si = 2; si < data[0]; si += 2) { + if (di >= (size - 1)) break; + if (data[si + 1]) { + /* high byte */ + buffer[di++] = '?'; + } else { + buffer[di++] = data[si]; + } + } + buffer[di] = 0; + + return di; +#endif +} + +irecv_error_t irecv_open(irecv_client_t* pclient) { +#ifndef WIN32 + int i = 0; + struct libusb_device* usb_device = NULL; + struct libusb_device** usb_device_list = NULL; + struct libusb_device_handle* usb_handle = NULL; + struct libusb_device_descriptor usb_descriptor; + + *pclient = NULL; + if(libirecovery_debug) { + irecv_set_debug_level(libirecovery_debug); + } + + irecv_error_t error = IRECV_E_SUCCESS; + int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); + for (i = 0; i < usb_device_count; i++) { + usb_device = usb_device_list[i]; + libusb_get_device_descriptor(usb_device, &usb_descriptor); + if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { + /* verify this device is in a mode we understand */ + if (usb_descriptor.idProduct == kRecoveryMode1 || + usb_descriptor.idProduct == kRecoveryMode2 || + usb_descriptor.idProduct == kRecoveryMode3 || + usb_descriptor.idProduct == kRecoveryMode4 || + usb_descriptor.idProduct == kDfuMode) { + + debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); + + libusb_open(usb_device, &usb_handle); + if (usb_handle == NULL) { + libusb_free_device_list(usb_device_list, 1); + libusb_close(usb_handle); + libusb_exit(libirecovery_context); + return IRECV_E_UNABLE_TO_CONNECT; + } + libusb_free_device_list(usb_device_list, 1); + + irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); + if (client == NULL) { + libusb_close(usb_handle); + libusb_exit(libirecovery_context); + return IRECV_E_OUT_OF_MEMORY; + } + + memset(client, '\0', sizeof(struct irecv_client)); + client->interface = 0; + client->handle = usb_handle; + client->mode = usb_descriptor.idProduct; + if (client->mode != kDfuMode) { + error = irecv_set_configuration(client, 1); + if (error != IRECV_E_SUCCESS) { + return error; + } + + error = irecv_set_interface(client, 0, 0); + if (error != IRECV_E_SUCCESS) { + return error; + } + } + + /* cache usb serial */ + irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*) client->serial, 255); + + *pclient = client; + return IRECV_E_SUCCESS; + } + } + } + + return IRECV_E_UNABLE_TO_CONNECT; +#else + int ret = mobiledevice_connect(pclient); + if (ret == IRECV_E_SUCCESS) { + irecv_get_string_descriptor_ascii(*pclient, 3, (unsigned char*) (*pclient)->serial, 255); + } + return ret; +#endif +} + +irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + +#ifndef WIN32 + debug("Setting to configuration %d\n", configuration); + + int current = 0; + libusb_get_configuration(client->handle, ¤t); + if (current != configuration) { + if (libusb_set_configuration(client->handle, configuration) < 0) { + return IRECV_E_USB_CONFIGURATION; + } + } + + client->config = configuration; +#endif + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + +#ifndef WIN32 + libusb_release_interface(client->handle, client->interface); + + debug("Setting to interface %d:%d\n", interface, alt_interface); + if (libusb_claim_interface(client->handle, interface) < 0) { + return IRECV_E_USB_INTERFACE; + } + + if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { + return IRECV_E_USB_INTERFACE; + } + + client->interface = interface; + client->alt_interface = alt_interface; +#endif + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_reset(irecv_client_t client) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + +#ifndef WIN32 + libusb_reset_device(client->handle); +#else + int ret; + DWORD count; + ret = DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL); +#endif + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts) { + int i; + + for (i = 0; i < attempts; i++) { + if (irecv_open(pclient) != IRECV_E_SUCCESS) { + debug("Connection failed. Waiting 1 sec before retry.\n"); + sleep(1); + } else { + return IRECV_E_SUCCESS; + } + } + + return IRECV_E_UNABLE_TO_CONNECT; +} + +irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { + switch(type) { + case IRECV_RECEIVED: + client->received_callback = callback; + break; + + case IRECV_PROGRESS: + client->progress_callback = callback; + + case IRECV_CONNECTED: + client->connected_callback = callback; + + case IRECV_PRECOMMAND: + client->precommand_callback = callback; + break; + + case IRECV_POSTCOMMAND: + client->postcommand_callback = callback; + break; + + case IRECV_DISCONNECTED: + client->disconnected_callback = callback; + + default: + return IRECV_E_UNKNOWN_ERROR; + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { + switch(type) { + case IRECV_RECEIVED: + client->received_callback = NULL; + break; + + case IRECV_PROGRESS: + client->progress_callback = NULL; + + case IRECV_CONNECTED: + client->connected_callback = NULL; + + case IRECV_PRECOMMAND: + client->precommand_callback = NULL; + break; + + case IRECV_POSTCOMMAND: + client->postcommand_callback = NULL; + break; + + case IRECV_DISCONNECTED: + client->disconnected_callback = NULL; + + default: + return IRECV_E_UNKNOWN_ERROR; + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_close(irecv_client_t client) { + if (client != NULL) { + if(client->disconnected_callback != NULL) { + irecv_event_t event; + event.size = 0; + event.data = NULL; + event.progress = 0; + event.type = IRECV_DISCONNECTED; + client->disconnected_callback(client, &event); + } +#ifndef WIN32 + if (client->handle != NULL) { + if (client->mode != kDfuMode) { + libusb_release_interface(client->handle, client->interface); + } + libusb_close(client->handle); + client->handle = NULL; + } +#else + if (client->iBootPath!=NULL) { + free(client->iBootPath); + client->iBootPath = NULL; + } + if (client->DfuPath!=NULL) { + free(client->DfuPath); + client->DfuPath = NULL; + } + mobiledevice_closepipes(client); +#endif + free(client); + client = NULL; + } + + return IRECV_E_SUCCESS; +} + +void irecv_set_debug_level(int level) { + libirecovery_debug = level; +#ifndef WIN32 + if(libirecovery_context) { + libusb_set_debug(libirecovery_context, libirecovery_debug); + } +#endif +} + +static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) { + unsigned int length = strlen(command); + if (length >= 0x100) { + length = 0xFF; + } + + if (length > 0) { + int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, 1000); + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_send_command(irecv_client_t client, char* command) { + irecv_error_t error = 0; + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + unsigned int length = strlen(command); + if (length >= 0x100) { + length = 0xFF; + } + + irecv_event_t event; + if(client->precommand_callback != NULL) { + event.size = length; + event.data = command; + event.type = IRECV_PRECOMMAND; + if(client->precommand_callback(client, &event)) { + return IRECV_E_SUCCESS; + } + } + + error = irecv_send_command_raw(client, command); + if (error != IRECV_E_SUCCESS) { + debug("Failed to send command %s\n", command); + if (error != IRECV_E_PIPE) + return error; + } + + if(client->postcommand_callback != NULL) { + event.size = length; + event.data = command; + event.type = IRECV_POSTCOMMAND; + if(client->postcommand_callback(client, &event)) { + return IRECV_E_SUCCESS; + } + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + FILE* file = fopen(filename, "rb"); + if (file == NULL) { + return IRECV_E_FILE_NOT_FOUND; + } + + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + + char* buffer = (char*) malloc(length); + if (buffer == NULL) { + fclose(file); + return IRECV_E_OUT_OF_MEMORY; + } + + long bytes = fread(buffer, 1, length, file); + fclose(file); + + if (bytes != length) { + free(buffer); + return IRECV_E_UNKNOWN_ERROR; + } + + irecv_error_t error = irecv_send_buffer(client, buffer, length, dfuNotifyFinished); + free(buffer); + return error; +} + +irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { + if (check_context(client) != IRECV_E_SUCCESS) { + *status = 0; + return IRECV_E_NO_DEVICE; + } + + unsigned char buffer[6]; + memset(buffer, '\0', 6); + if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { + *status = 0; + return IRECV_E_USB_STATUS; + } + + *status = (unsigned int) buffer[4]; + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) { + irecv_error_t error = 0; + int recovery_mode = (client->mode != kDfuMode); + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + int packet_size = 0x800; + int last = length % packet_size; + int packets = length / packet_size; + if (last != 0) { + packets++; + } else { + last = packet_size; + } + + /* initiate transfer */ + if (recovery_mode) { + error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, 1000); + if (error != IRECV_E_SUCCESS) { + return error; + } + } + + int i = 0; + double progress = 0; + unsigned long count = 0; + unsigned int status = 0; + int bytes = 0; + for (i = 0; i < packets; i++) { + int size = (i + 1) < packets ? packet_size : last; + + /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ + if (recovery_mode) { + error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, 1000); + } else { + bytes = irecv_control_transfer(client, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); + } + + if (bytes != size) { + return IRECV_E_USB_UPLOAD; + } + + if (!recovery_mode) { + error = irecv_get_status(client, &status); + } + + if (error != IRECV_E_SUCCESS) { + return error; + } + + if (!recovery_mode && status != 5) { + return IRECV_E_USB_UPLOAD; + } + + count += size; + if(client->progress_callback != NULL) { + irecv_event_t event; + event.progress = ((double) count/ (double) length) * 100.0; + event.type = IRECV_PROGRESS; + event.data = "Uploading"; + event.size = count; + client->progress_callback(client, &event); + } else { + debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); + } + } + + if (dfuNotifyFinished && !recovery_mode) { + irecv_control_transfer(client, 0x21, 1, 0, 0, (unsigned char*) buffer, 0, 1000); + + for (i = 0; i < 3; i++) { + error = irecv_get_status(client, &status); + if (error != IRECV_E_SUCCESS) { + return error; + } + } + irecv_reset(client); + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_receive(irecv_client_t client) { + char buffer[BUFFER_SIZE]; + memset(buffer, '\0', BUFFER_SIZE); + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + int bytes = 0; + + while (irecv_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 1000) == 0) { + if (bytes > 0) { + if (client->received_callback != NULL) { + irecv_event_t event; + event.size = bytes; + event.data = buffer; + event.type = IRECV_RECEIVED; + if (client->received_callback(client, &event) != 0) { + return IRECV_E_SUCCESS; + } + } + } else break; + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { + int ret = 0; + char command[256]; + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + *value = NULL; + + if(variable == NULL) { + return IRECV_E_UNKNOWN_ERROR; + } + + memset(command, '\0', sizeof(command)); + snprintf(command, sizeof(command)-1, "getenv %s", variable); + irecv_error_t error = irecv_send_command_raw(client, command); + if(error == IRECV_E_PIPE) { + return IRECV_E_SUCCESS; + } + if(error != IRECV_E_SUCCESS) { + return error; + } + + char* response = (char*) malloc(256); + if (response == NULL) { + return IRECV_E_OUT_OF_MEMORY; + } + + memset(response, '\0', 256); + ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000); + + *value = response; + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) { + int ret = 0; + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + *value = NULL; + + char* response = (char*) malloc(256); + if (response == NULL) { + return IRECV_E_OUT_OF_MEMORY; + } + + memset(response, '\0', 256); + ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000); + + *value = response; + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + char* cpid_string = strstr(client->serial, "CPID:"); + if (cpid_string == NULL) { + *cpid = 0; + return IRECV_E_UNKNOWN_ERROR; + } + sscanf(cpid_string, "CPID:%d", cpid); + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + char* bdid_string = strstr(client->serial, "BDID:"); + if (bdid_string == NULL) { + *bdid = 0; + return IRECV_E_UNKNOWN_ERROR; + } + sscanf(bdid_string, "BDID:%d", bdid); + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + char* ecid_string = strstr(client->serial, "ECID:"); + if (ecid_string == NULL) { + *ecid = 0; + return IRECV_E_UNKNOWN_ERROR; + } + sscanf(ecid_string, "ECID:%qX", ecid); + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_send_exploit(irecv_client_t client) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, 1000); + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { + irecv_error_t error = IRECV_E_SUCCESS; + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + char* file_data = NULL; + unsigned int file_size = 0; + if(irecv_read_file(filename, &file_data, &file_size) < 0) { + return IRECV_E_FILE_NOT_FOUND; + } + + char* line = strtok(file_data, "\n"); + while(line != NULL) { + if(line[0] != '#') { + error = irecv_send_command(client, line); + if(error != IRECV_E_SUCCESS) { + return error; + } + + error = irecv_receive(client); + if(error != IRECV_E_SUCCESS) { + return error; + } + } + line = strtok(NULL, "\n"); + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_saveenv(irecv_client_t client) { + irecv_error_t error = irecv_send_command_raw(client, "saveenv"); + if(error != IRECV_E_SUCCESS) { + return error; + } + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { + char command[256]; + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + if(variable == NULL || value == NULL) { + return IRECV_E_UNKNOWN_ERROR; + } + + memset(command, '\0', sizeof(command)); + snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); + irecv_error_t error = irecv_send_command_raw(client, command); + if(error != IRECV_E_SUCCESS) { + return error; + } + + return IRECV_E_SUCCESS; +} + +const char* irecv_strerror(irecv_error_t error) { + switch (error) { + case IRECV_E_SUCCESS: + return "Command completed successfully"; + + case IRECV_E_NO_DEVICE: + return "Unable to find device"; + + case IRECV_E_OUT_OF_MEMORY: + return "Out of memory"; + + case IRECV_E_UNABLE_TO_CONNECT: + return "Unable to connect to device"; + + case IRECV_E_INVALID_INPUT: + return "Invalid input"; + + case IRECV_E_FILE_NOT_FOUND: + return "File not found"; + + case IRECV_E_USB_UPLOAD: + return "Unable to upload data to device"; + + case IRECV_E_USB_STATUS: + return "Unable to get device status"; + + case IRECV_E_USB_INTERFACE: + return "Unable to set device interface"; + + case IRECV_E_USB_CONFIGURATION: + return "Unable to set device configuration"; + + case IRECV_E_PIPE: + return "Broken pipe"; + + case IRECV_E_TIMEOUT: + return "Timeout talking to device"; + + default: + return "Unknown error"; + } + + return NULL; +} + +int irecv_write_file(const char* filename, const void* data, size_t size) { + size_t bytes = 0; + FILE* file = NULL; + + debug("Writing data to %s\n", filename); + file = fopen(filename, "wb"); + if (file == NULL) { + //error("read_file: Unable to open file %s\n", filename); + return -1; + } + + bytes = fwrite(data, 1, size, file); + fclose(file); + + if (bytes != size) { + //error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); + return -1; + } + + return size; +} + +int irecv_read_file(const char* filename, char** data, uint32_t* size) { + size_t bytes = 0; + size_t length = 0; + FILE* file = NULL; + char* buffer = NULL; + debug("Reading data from %s\n", filename); + + *size = 0; + *data = NULL; + + file = fopen(filename, "rb"); + if (file == NULL) { + //error("read_file: File %s not found\n", filename); + return -1; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + rewind(file); + + buffer = (char*) malloc(length); + if(buffer == NULL) { + //error("ERROR: Out of memory\n"); + fclose(file); + return -1; + } + bytes = fread(buffer, 1, length, file); + fclose(file); + + if(bytes != length) { + //error("ERROR: Unable to read entire file\n"); + free(buffer); + return -1; + } + + *size = length; + *data = buffer; + return 0; +} + +irecv_error_t irecv_reset_counters(irecv_client_t client) { + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + if (client->mode == kDfuMode) { + irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000); + } + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) { + irecv_error_t error = 0; + int recovery_mode = (client->mode != kDfuMode); + + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + int packet_size = recovery_mode ? 0x2000: 0x800; + int last = length % packet_size; + int packets = length / packet_size; + if (last != 0) { + packets++; + } else { + last = packet_size; + } + + int i = 0; + int bytes = 0; + double progress = 0; + unsigned long count = 0; + unsigned int status = 0; + for (i = 0; i < packets; i++) { + unsigned short size = (i+1) < packets ? packet_size : last; + bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, &buffer[i * packet_size], size, 1000); + + if (bytes != size) { + return IRECV_E_USB_UPLOAD; + } + + count += size; + if(client->progress_callback != NULL) { + irecv_event_t event; + event.progress = ((double) count/ (double) length) * 100.0; + event.type = IRECV_PROGRESS; + event.data = "Downloading"; + event.size = count; + client->progress_callback(client, &event); + } else { + debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); + } + } + + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_finish_transfer(irecv_client_t client) { + int i = 0; + unsigned int status = 0; + + if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + + irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, 1000); + + for(i = 0; i < 3; i++){ + irecv_get_status(client, &status); + } + irecv_reset(client); + return IRECV_E_SUCCESS; +} + +irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device) { + int device_id = DEVICE_UNKNOWN; + uint32_t bdid = 0; + uint32_t cpid = 0; + + if (irecv_get_cpid(client, &cpid) < 0) { + return IRECV_E_UNKNOWN_ERROR; + } + + switch (cpid) { + case CPID_IPHONE2G: + // iPhone1,1 iPhone1,2 and iPod1,1 all share the same ChipID + // so we need to check the BoardID + if (irecv_get_bdid(client, &bdid) < 0) { + break; + } + + switch (bdid) { + case BDID_IPHONE2G: + device_id = DEVICE_IPHONE2G; + break; + + case BDID_IPHONE3G: + device_id = DEVICE_IPHONE3G; + break; + + case BDID_IPOD1G: + device_id = DEVICE_IPOD1G; + break; + + default: + device_id = DEVICE_UNKNOWN; + break; + } + break; + + case CPID_IPHONE3GS: + device_id = DEVICE_IPHONE3GS; + break; + + case CPID_IPOD2G: + device_id = DEVICE_IPOD2G; + break; + + case CPID_IPOD3G: + device_id = DEVICE_IPOD3G; + break; + + case CPID_IPAD1G: + // iPhone3,1 iPad4,1 and iPad1,1 all share the same ChipID + // so we need to check the BoardID + if (irecv_get_bdid(client, &bdid) < 0) { + break; + } + + switch (bdid) { + case BDID_IPAD1G: + device_id = DEVICE_IPAD1G; + break; + + case BDID_IPHONE4: + device_id = DEVICE_IPHONE4; + break; + + case BDID_IPOD4G: + device_id = DEVICE_IPOD4G; + break; + + case BDID_APPLETV2: + device_id = DEVICE_APPLETV2; + break; + + default: + device_id = DEVICE_UNKNOWN; + break; + } + break; + + default: + device_id = DEVICE_UNKNOWN; + break; + } + + *device = &irecv_devices[device_id]; + return IRECV_E_SUCCESS; +} + +irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) { + irecv_error_t error = 0; + irecv_client_t new_client = NULL; + irecv_event_cb_t progress_callback = client->progress_callback; + + if (check_context(client) == IRECV_E_SUCCESS) { + irecv_close(client); + } + + if (initial_pause > 0) { + debug("Waiting %d seconds for the device to pop up...\n", initial_pause); + sleep(initial_pause); + } + + error = irecv_open_attempts(&new_client, 10); + if(error != IRECV_E_SUCCESS) { + return NULL; + } + + new_client->progress_callback = progress_callback; + return new_client; +} + +void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr) { + int i, j; + printf("0x%08x: ", addr); + for (i = 0; i < len; i++) { + if (i % 16 == 0 && i != 0) { + for (j=i-16; j < i; j++) { + unsigned char car = buf[j]; + if (car < 0x20 || car > 0x7f) car = '.'; + printf("%c", car); + } + printf("\n"); + addr += 0x10; + printf("0x%08x: ", addr); + } + printf("%02x ", buf[i]); + } + + int done = (i % 16); + int remains = 16 - done; + if (done > 0) { + for (j = 0; j < remains; j++) { + printf(" "); + } + } + + if ((i - done) >= 0) { + if (done == 0 && i > 0) done = 16; + for (j = (i - done); j < i; j++) { + unsigned char car = buf[j]; + if (car < 0x20 || car > 0x7f) car = '.'; + printf("%c", car); + } + } + printf("\n"); +} diff --git a/src/libirecovery.c b/src/libirecovery.c deleted file mode 100644 index dd5c734..0000000 --- a/src/libirecovery.c +++ /dev/null @@ -1,672 +0,0 @@ -/** - * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode - * Copyright (C) 2008 - 2009 westbaer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - **/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <libusb-1.0/libusb.h> - -#include "libirecovery.h" - -#define BUFFER_SIZE 0x1000 -#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) - -static int libirecovery_debug = 0; -static libusb_context* libirecovery_context = NULL; - -int irecv_write_file(const char* filename, const void* data, size_t size); -int irecv_read_file(const char* filename, char** data, uint32_t* size); - -irecv_error_t irecv_open(irecv_client_t* pclient) { - int i = 0; - char serial[256]; - struct libusb_device* usb_device = NULL; - struct libusb_device** usb_device_list = NULL; - struct libusb_device_handle* usb_handle = NULL; - struct libusb_device_descriptor usb_descriptor; - - *pclient = NULL; - libusb_init(&libirecovery_context); - if(libirecovery_debug) { - irecv_set_debug_level(libirecovery_debug); - } - - irecv_error_t error = IRECV_E_SUCCESS; - int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); - for (i = 0; i < usb_device_count; i++) { - usb_device = usb_device_list[i]; - libusb_get_device_descriptor(usb_device, &usb_descriptor); - if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { - /* verify this device is in a mode we understand */ - if (usb_descriptor.idProduct == kRecoveryMode1 || - usb_descriptor.idProduct == kRecoveryMode2 || - usb_descriptor.idProduct == kRecoveryMode3 || - usb_descriptor.idProduct == kRecoveryMode4 || - usb_descriptor.idProduct == kDfuMode) { - - libusb_open(usb_device, &usb_handle); - if (usb_handle == NULL) { - libusb_free_device_list(usb_device_list, 1); - libusb_close(usb_handle); - libusb_exit(libirecovery_context); - return IRECV_E_UNABLE_TO_CONNECT; - } - libusb_free_device_list(usb_device_list, 1); - - irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); - if (client == NULL) { - libusb_close(usb_handle); - libusb_exit(libirecovery_context); - return IRECV_E_OUT_OF_MEMORY; - } - - memset(client, '\0', sizeof(struct irecv_client)); - client->interface = 0; - client->handle = usb_handle; - client->mode = usb_descriptor.idProduct; - - error = irecv_set_configuration(client, 1); - if (error != IRECV_E_SUCCESS) { - return error; - } - - error = irecv_set_interface(client, 1, 1); - if (error != IRECV_E_SUCCESS) { - return error; - } - - *pclient = client; - return IRECV_E_SUCCESS; - } - } - } - - return IRECV_E_UNABLE_TO_CONNECT; -} - -irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - debug("Setting to configuration %d", configuration); - - int current = 0; - libusb_get_configuration(client->handle, ¤t); - if (current != configuration) { - if (libusb_set_configuration(client->handle, configuration) < 0) { - return IRECV_E_USB_CONFIGURATION; - } - } - - client->config = configuration; - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - if (client->interface == interface) { - return IRECV_E_SUCCESS; - } - - debug("Setting to interface %d:%d", interface, alt_interface); - if (libusb_claim_interface(client->handle, interface) < 0) { - return IRECV_E_USB_INTERFACE; - } - - if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { - return IRECV_E_USB_INTERFACE; - } - - client->interface = interface; - client->alt_interface = alt_interface; - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_reset(irecv_client_t client) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - libusb_reset_device(client->handle); - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { - switch(type) { - case IRECV_RECEIVED: - client->received_callback = callback; - break; - - case IRECV_PROGRESS: - client->progress_callback = callback; - - case IRECV_CONNECTED: - client->connected_callback = callback; - - case IRECV_PRECOMMAND: - client->precommand_callback = callback; - break; - - case IRECV_POSTCOMMAND: - client->postcommand_callback = callback; - break; - - case IRECV_DISCONNECTED: - client->disconnected_callback = callback; - - default: - return IRECV_E_UNKNOWN_ERROR; - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { - switch(type) { - case IRECV_RECEIVED: - client->received_callback = NULL; - break; - - case IRECV_PROGRESS: - client->progress_callback = NULL; - - case IRECV_CONNECTED: - client->connected_callback = NULL; - - case IRECV_PRECOMMAND: - client->precommand_callback = NULL; - break; - - case IRECV_POSTCOMMAND: - client->postcommand_callback = NULL; - break; - - case IRECV_DISCONNECTED: - client->disconnected_callback = NULL; - - default: - return IRECV_E_UNKNOWN_ERROR; - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_close(irecv_client_t client) { - if (client != NULL) { - if(client->disconnected_callback != NULL) { - irecv_event_t event; - event.size = 0; - event.data = NULL; - event.progress = 0; - event.type = IRECV_DISCONNECTED; - client->disconnected_callback(client, &event); - } - - if (client->handle != NULL) { - libusb_release_interface(client->handle, client->interface); - libusb_close(client->handle); - client->handle = NULL; - } - - if (libirecovery_context != NULL) { - libusb_exit(libirecovery_context); - libirecovery_context = NULL; - } - - free(client); - client = NULL; - } - - return IRECV_E_SUCCESS; -} - -void irecv_set_debug_level(int level) { - libirecovery_debug = level; - if(libirecovery_context) { - libusb_set_debug(libirecovery_context, libirecovery_debug); - } -} - -irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - unsigned int length = strlen(command); - if (length >= 0x100) { - length = 0xFF; - } - - irecv_event_t event; - if(client->precommand_callback != NULL) { - event.size = length; - event.data = command; - event.type = IRECV_PRECOMMAND; - if(client->precommand_callback(client, &event)) { - return IRECV_E_SUCCESS; - } - } - - if (length > 0) { - libusb_control_transfer(client->handle, 0x40, 0, 0, 0, command, length + 1, 100); - } - - if(client->postcommand_callback != NULL) { - event.size = length; - event.data = command; - event.type = IRECV_POSTCOMMAND; - if(client->postcommand_callback(client, &event)) { - return IRECV_E_SUCCESS; - } - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - FILE* file = fopen(filename, "rb"); - if (file == NULL) { - return IRECV_E_FILE_NOT_FOUND; - } - - fseek(file, 0, SEEK_END); - int length = ftell(file); - fseek(file, 0, SEEK_SET); - - unsigned char* buffer = (unsigned char*) malloc(length); - if (buffer == NULL) { - fclose(file); - return IRECV_E_OUT_OF_MEMORY; - } - - int bytes = fread(buffer, 1, length, file); - fclose(file); - - if (bytes != length) { - free(buffer); - return IRECV_E_UNKNOWN_ERROR; - } - - irecv_error_t error = irecv_send_buffer(client, buffer, length); - free(buffer); - return error; -} - -irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { - if (client == NULL || client->handle == NULL) { - *status = 0; - return IRECV_E_NO_DEVICE; - } - - unsigned char buffer[6]; - memset(buffer, '\0', 6); - if (libusb_control_transfer(client->handle, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { - *status = 0; - return IRECV_E_USB_STATUS; - } - - debug("status: %d\n", (unsigned int) buffer[4]); - *status = (unsigned int) buffer[4]; - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned int length) { - irecv_error_t error = 0; - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - int last = length % 0x800; - int packets = length / 0x800; - if (last != 0) { - packets++; - } - - int i = 0; - double progress = 0; - unsigned int count = 0; - unsigned int status = 0; - for (i = 0; i < packets; i++) { - int size = i + 1 < packets ? 0x800 : last; - int bytes = libusb_control_transfer(client->handle, 0x21, 1, 0, 0, &buffer[i * 0x800], size, 1000); - if (bytes != size) { - return IRECV_E_USB_UPLOAD; - } - - error = irecv_get_status(client, &status); - if (error != IRECV_E_SUCCESS) { - return error; - } - - if (status != 5) { - return IRECV_E_USB_UPLOAD; - } - - count += size; - if(client->progress_callback != NULL) { - irecv_event_t event; - event.progress = ((double) count/ (double) length) * 100.0; - event.type = IRECV_PROGRESS; - event.data = "Uploading"; - event.size = count; - client->progress_callback(client, &event); - } else { - debug("Sent: %d bytes - %d of %d\n", bytes, count, length); - } - } - - libusb_control_transfer(client->handle, 0x21, 1, 0, 0, buffer, 0, 1000); - for (i = 0; i < 3; i++) { - error = irecv_get_status(client, &status); - if (error != IRECV_E_SUCCESS) { - return error; - } - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_receive(irecv_client_t client) { - unsigned char buffer[BUFFER_SIZE]; - memset(buffer, '\0', BUFFER_SIZE); - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - int bytes = 0; - while (libusb_bulk_transfer(client->handle, 0x81, buffer, BUFFER_SIZE, &bytes, 100) == 0) { - if (bytes > 0) { - if (client->received_callback != NULL) { - irecv_event_t event; - event.size = bytes; - event.data = buffer; - event.type = IRECV_RECEIVED; - if (client->received_callback(client, &event) != 0) { - return IRECV_E_SUCCESS; - } - } - } else break; - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { - char command[256]; - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - *value = NULL; - - if(variable == NULL) { - return IRECV_E_UNKNOWN_ERROR; - } - - memset(command, '\0', sizeof(command)); - snprintf(command, sizeof(command)-1, "getenv %s", variable); - irecv_error_t error = irecv_send_command(client, command); - if(error != IRECV_E_SUCCESS) { - return error; - } - - unsigned char* response = (unsigned char*) malloc(256); - if (response == NULL) { - return IRECV_E_OUT_OF_MEMORY; - } - - memset(response, '\0', 256); - int ret = libusb_control_transfer(client->handle, 0xC0, 0, 0, 0, response, 255, 500); - if (ret < 0) { - return IRECV_E_UNKNOWN_ERROR; - } - - *value = response; - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { - char info[256]; - memset(info, '\0', 256); - - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - libusb_get_string_descriptor_ascii(client->handle, 3, info, 255); - - unsigned char* cpid_string = strstr(info, "CPID:"); - if (cpid_string == NULL) { - *cpid = 0; - return IRECV_E_UNKNOWN_ERROR; - } - sscanf(cpid_string, "CPID:%d", cpid); - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { - char info[256]; - memset(info, '\0', 256); - - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - libusb_get_string_descriptor_ascii(client->handle, 3, info, 255); - - unsigned char* bdid_string = strstr(info, "BDID:"); - if (bdid_string == NULL) { - *bdid = 0; - return IRECV_E_UNKNOWN_ERROR; - } - sscanf(bdid_string, "BDID:%d", bdid); - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { - char info[256]; - memset(info, '\0', 256); - - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - libusb_get_string_descriptor_ascii(client->handle, 3, info, 255); - - unsigned char* ecid_string = strstr(info, "ECID:"); - if (ecid_string == NULL) { - *ecid = 0; - return IRECV_E_UNKNOWN_ERROR; - } - sscanf(ecid_string, "ECID:%qX", ecid); - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_send_exploit(irecv_client_t client) { - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - libusb_control_transfer(client->handle, 0x21, 2, 0, 0, NULL, 0, 100); - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { - irecv_error_t error = IRECV_E_SUCCESS; - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - int file_size = 0; - char* file_data = NULL; - if(irecv_read_file(filename, &file_data, &file_size) < 0) { - return IRECV_E_FILE_NOT_FOUND; - } - - char* line = strtok(file_data, "\n"); - while(line != NULL) { - if(line[0] != '#') { - error = irecv_send_command(client, line); - if(error != IRECV_E_SUCCESS) { - return error; - } - - error = irecv_receive(client); - if(error != IRECV_E_SUCCESS) { - return error; - } - } - line = strtok(NULL, "\n"); - } - - return IRECV_E_SUCCESS; -} - -irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { - char command[256]; - if (client == NULL || client->handle == NULL) { - return IRECV_E_NO_DEVICE; - } - - if(variable == NULL || value == NULL) { - return IRECV_E_UNKNOWN_ERROR; - } - - memset(command, '\0', sizeof(command)); - snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); - irecv_error_t error = irecv_send_command(client, command); - if(error != IRECV_E_SUCCESS) { - return error; - } - - return IRECV_E_SUCCESS; -} - -const char* irecv_strerror(irecv_error_t error) { - switch (error) { - case IRECV_E_SUCCESS: - return "Command completed successfully"; - - case IRECV_E_NO_DEVICE: - return "Unable to find device"; - - case IRECV_E_OUT_OF_MEMORY: - return "Out of memory"; - - case IRECV_E_UNABLE_TO_CONNECT: - return "Unable to connect to device"; - - case IRECV_E_INVALID_INPUT: - return "Invalid input"; - - case IRECV_E_FILE_NOT_FOUND: - return "File not found"; - - case IRECV_E_USB_UPLOAD: - return "Unable to upload data to device"; - - case IRECV_E_USB_STATUS: - return "Unable to get device status"; - - case IRECV_E_USB_INTERFACE: - return "Unable to set device interface"; - - case IRECV_E_USB_CONFIGURATION: - return "Unable to set device configuration"; - - default: - return "Unknown error"; - } - - return NULL; -} - -int irecv_write_file(const char* filename, const void* data, size_t size) { - size_t bytes = 0; - FILE* file = NULL; - - debug("Writing data to %s\n", filename); - file = fopen(filename, "wb"); - if (file == NULL) { - error("read_file: Unable to open file %s\n", filename); - return -1; - } - - bytes = fwrite(data, 1, size, file); - fclose(file); - - if (bytes != size) { - error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); - return -1; - } - - return size; -} - -int irecv_read_file(const char* filename, char** data, uint32_t* size) { - size_t bytes = 0; - size_t length = 0; - FILE* file = NULL; - char* buffer = NULL; - debug("Reading data from %s\n", filename); - - *size = 0; - *data = NULL; - - file = fopen(filename, "rb"); - if (file == NULL) { - error("read_file: File %s not found\n", filename); - return -1; - } - - fseek(file, 0, SEEK_END); - length = ftell(file); - rewind(file); - - buffer = (char*) malloc(length); - if(buffer == NULL) { - error("ERROR: Out of memory\n"); - fclose(file); - return -1; - } - bytes = fread(buffer, 1, length, file); - fclose(file); - - if(bytes != length) { - error("ERROR: Unable to read entire file\n"); - free(buffer); - return -1; - } - - *size = length; - *data = buffer; - return 0; -} |