mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Merge in the libusbp changes.
This commit is contained in:
		| @@ -26,13 +26,6 @@ | ||||
| <PropertyDeltas /> | ||||
| </CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b> | ||||
| <CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1"> | ||||
| <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="patcher.vbs" persistent="patcher.vbs"> | ||||
| <Hidden v="False" /> | ||||
| </CyGuid_31768f72-0253-412b-af77-e7dba74d1330> | ||||
| <build_action v="OTHER;;;;" /> | ||||
| <PropertyDeltas /> | ||||
| </CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b> | ||||
| <CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1"> | ||||
| <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="protocol.h" persistent="..\protocol.h"> | ||||
| <Hidden v="False" /> | ||||
| </CyGuid_31768f72-0253-412b-af77-e7dba74d1330> | ||||
| @@ -3301,7 +3294,7 @@ | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Optimization Level" v="" /> | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Link Time Optimization" v="" /> | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Fat LTO objects" v="" /> | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Pre Build Commands" v="cscript patcher.vbs" /> | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Pre Build Commands" v="" /> | ||||
| <name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Post Build Commands" v="" /> | ||||
| </name> | ||||
| </platform> | ||||
|   | ||||
| @@ -937,3 +937,75 @@ int main(void) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| const uint8_t USBFS_MSOS_CONFIGURATION_DESCR[USBFS_MSOS_CONF_DESCR_LENGTH] = { | ||||
| /*  Length of the descriptor 4 bytes       */   0x28u, 0x00u, 0x00u, 0x00u, | ||||
| /*  Version of the descriptor 2 bytes      */   0x00u, 0x01u, | ||||
| /*  wIndex - Fixed:INDEX_CONFIG_DESCRIPTOR */   0x04u, 0x00u, | ||||
| /*  bCount - Count of device functions.    */   0x01u, | ||||
| /*  Reserved : 7 bytes                     */   0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, | ||||
| /*  bFirstInterfaceNumber                  */   0x00u, | ||||
| /*  Reserved                               */   0x01u, | ||||
| /*  compatibleId - "WINUSB\0\0"            */   'W', 'I', 'N', 'U', 'S', 'B', 0, 0, | ||||
| /*  subcompatibleID - "00001\0\0"          */   '0', '0', '0', '0', '1', | ||||
|                                                 0x00u, 0x00u, 0x00u, | ||||
| /*  Reserved : 6 bytes                     */   0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u | ||||
| }; | ||||
|  | ||||
| const uint8_t USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[224] = { | ||||
|     /* Length; 4 bytes       */   224, 0, 0, 0, | ||||
|     /* Version; 2 bytes      */   0x00, 0x01, /* 1.0 */ | ||||
|     /* wIndex                */   0x05, 0x00, | ||||
|     /* Number of sections    */   0x01, 0x00, | ||||
|     /* Property section size */   214, 0, 0, 0, | ||||
|     /* Property data type    */   0x07, 0x00, 0x00, 0x00, /* 7 = REG_MULTI_SZ Unicode */ | ||||
|     /* Property name length  */   42, 0, | ||||
|     /* Property name         */   'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, | ||||
|                                   'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, | ||||
|                                   'a', 0, 'c', 0, 'e', 0, 'G', 0, 'U', 0, 'I', 0, | ||||
|                                   'D', 0, 's', 0, 0,   0, | ||||
|     /* Property data length  */   158, 0, 0, 0, | ||||
|     /* GUID #1 data          */   '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0, | ||||
|                                   'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0, | ||||
|                                   '3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0, | ||||
|                                   '5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0, | ||||
|                                   '-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0, | ||||
|                                   '5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0, | ||||
|                                   '8', 0, '}', 0, '\0', 0, | ||||
|     /* GUID #2 data          */   '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0, | ||||
|                                   'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0, | ||||
|                                   '3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0, | ||||
|                                   '5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0, | ||||
|                                   '-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0, | ||||
|                                   '5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0, | ||||
|                                   '8', 0, '}', 0, '\0', 0, '\0', 0 | ||||
| }; | ||||
|  | ||||
| uint8 USBFS_HandleVendorRqst(void) | ||||
| { | ||||
|     if (!(USBFS_bmRequestTypeReg & USBFS_RQST_DIR_D2H)) | ||||
|         return false; | ||||
|  | ||||
|     switch (USBFS_bRequestReg) | ||||
|     { | ||||
|         case USBFS_GET_EXTENDED_CONFIG_DESCRIPTOR: | ||||
|             switch (USBFS_wIndexLoReg) | ||||
|             { | ||||
|                 case 4: | ||||
|                     USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_CONFIGURATION_DESCR[0u]; | ||||
|                     USBFS_currentTD.count = USBFS_MSOS_CONFIGURATION_DESCR[0u]; | ||||
|                     return USBFS_InitControlRead(); | ||||
|                      | ||||
|                 case 5: | ||||
|                     USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u]; | ||||
|                     USBFS_currentTD.count = USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u]; | ||||
|                     return USBFS_InitControlRead(); | ||||
|             } | ||||
|  | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| const READ = 1 | ||||
| const WRITE = 2 | ||||
|  | ||||
| filename = "Generated_Source\PSoC5\USBFS_descr.c" | ||||
| set fso = CreateObject("Scripting.FileSystemObject") | ||||
|  | ||||
| set file = fso.OpenTextFile(filename, READ) | ||||
| text = file.ReadAll | ||||
| file.Close | ||||
|  | ||||
| set r = New RegExp | ||||
| r.MultiLine = True | ||||
| r.Pattern = "/\* +compatibleID.*\n.*" | ||||
| text = r.replace(text, "'W', 'I', 'N', 'U', 'S', 'B', 0, 0,") | ||||
|  | ||||
| set file = fso.CreateTextFile(filename, True) | ||||
| file.Write text | ||||
| file.Close | ||||
							
								
								
									
										24
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,6 +1,9 @@ | ||||
| PACKAGES = zlib sqlite3 libusb-1.0 protobuf | ||||
| PACKAGES = zlib sqlite3 protobuf | ||||
|  | ||||
| export CFLAGS = -x c++ --std=gnu++2a -ffunction-sections -fdata-sections \ | ||||
| export CFLAGS = \ | ||||
| 	-ffunction-sections -fdata-sections | ||||
| export CXXFLAGS = $(CFLAGS) \ | ||||
| 	-x c++ --std=gnu++2a \ | ||||
| 	-Wno-deprecated-enum-enum-conversion \ | ||||
| 	-Wno-deprecated-enum-float-conversion | ||||
| export LDFLAGS = -pthread | ||||
| @@ -11,15 +14,25 @@ export LDOPTFLAGS = -Os | ||||
| export CDBGFLAGS = -O0 -g | ||||
| export LDDBGFLAGS = -O0 -g | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| else | ||||
| ifeq ($(shell uname),Darwin) | ||||
| else | ||||
| 	PACKAGES += libudev | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ifeq ($(OS), Windows_NT) | ||||
| export PROTOC = /mingw32/bin/protoc | ||||
| export CC = /mingw32/bin/gcc | ||||
| export CXX = /mingw32/bin/g++ | ||||
| export AR = /mingw32/bin/ar rc | ||||
| export RANLIB = /mingw32/bin/ranlib | ||||
| export STRIP = /mingw32/bin/strip | ||||
| export CFLAGS += -I/mingw32/include/libusb-1.0 -I/mingw32/include | ||||
| export LDFLAGS += | ||||
| export LIBS += -L/mingw32/lib -static -lz -lsqlite3 -lusb-1.0 -lprotobuf | ||||
| export LIBS += -L/mingw32/lib -static -lz -lsqlite3 \ | ||||
| 	-lsetupapi -lwinusb -lole32 -lprotobuf -luuid | ||||
| export EXTENSION = .exe | ||||
| else | ||||
|  | ||||
| @@ -30,6 +43,7 @@ $(error You must have these pkg-config packages installed: $(PACKAGES)) | ||||
| endif | ||||
|  | ||||
| export PROTOC = protoc | ||||
| export CC = gcc | ||||
| export CXX = g++ | ||||
| export AR = ar rc | ||||
| export RANLIB = ranlib | ||||
| @@ -42,6 +56,10 @@ export EXTENSION = | ||||
| ifeq ($(shell uname),Darwin) | ||||
| AR = ar rcS | ||||
| RANLIB += -c -no_warning_for_no_symbols | ||||
| export CC = clang | ||||
| export CXX = clang++ | ||||
| export COBJC = clang | ||||
| export LDFLAGS += -framework IOKit -framework CoreFoundation | ||||
| endif | ||||
|  | ||||
| endif | ||||
|   | ||||
| @@ -230,3 +230,6 @@ As an exception, `dep/snowhouse` contains the snowhouse assertion library, | ||||
| taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License | ||||
| 1.0 licensed. Please see the contents of the directory for the full text. | ||||
|  | ||||
| As an exception, `dep/libusbp` contains the libusbp library, taken from | ||||
| https://github.com/pololu/libusbp. It is MIT licensed. Please see the contents | ||||
| of the directory for the full text. | ||||
|   | ||||
							
								
								
									
										5
									
								
								dep/libusbp/.dir-locals.el
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dep/libusbp/.dir-locals.el
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| ( | ||||
|   (c-default-style . "BSD") | ||||
|   (c-mode . ((c-basic-offset . 4) (tab-width . 4) (indent-tabs-mode . nil))) | ||||
|   (nil . ((fill-column . 80))) | ||||
| ) | ||||
							
								
								
									
										130
									
								
								dep/libusbp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								dep/libusbp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| cmake_minimum_required (VERSION 2.8.11) | ||||
|  | ||||
| # Fix behavior of CMAKE_CXX_STANDARD when targeting macOS. | ||||
| if (POLICY CMP0025) | ||||
|   cmake_policy(SET CMP0025 NEW) | ||||
| endif () | ||||
|  | ||||
| # Fix a warning on macOS. | ||||
| if (POLICY CMP0042) | ||||
|   cmake_policy(SET CMP0042 NEW) | ||||
| endif () | ||||
|  | ||||
| # Don't use -rdynamic since it breaks causes musl static linking. | ||||
| if (POLICY CMP0065) | ||||
|   cmake_policy(SET CMP0065 NEW) | ||||
| endif () | ||||
|  | ||||
| project (libusbp) | ||||
|  | ||||
| set (LIBUSBP_VERSION_MAJOR 1) | ||||
| set (LIBUSBP_VERSION_MINOR 2) | ||||
| set (LIBUSBP_VERSION_PATCH 0) | ||||
|  | ||||
| # Make 'Release' be the default build type, since the debug builds | ||||
| # include exported symbols that might cause name conflicts. | ||||
| if (NOT CMAKE_BUILD_TYPE) | ||||
|   set (CMAKE_BUILD_TYPE "Release" CACHE STRING | ||||
|     "Options are Debug Release RelWithDebInfo MinSizeRel" FORCE) | ||||
| endif () | ||||
|  | ||||
| option (BUILD_SHARED_LIBS "Build as shared library" TRUE) | ||||
| if (NOT BUILD_SHARED_LIBS) | ||||
|   add_definitions (-DLIBUSBP_STATIC) | ||||
|   set (PC_MORE_CFLAGS "-DLIBUSBP_STATIC") | ||||
| endif () | ||||
|  | ||||
| set(ENABLE_EXAMPLES FALSE CACHE BOOL | ||||
|   "True if you want to build the examples.") | ||||
|  | ||||
| set(ENABLE_TESTS FALSE CACHE BOOL | ||||
|   "True if you want to build the tests.") | ||||
|  | ||||
| set(LIBUSBP_LOG FALSE CACHE BOOL | ||||
|   "Output log messages to stderr for debugging.") | ||||
|  | ||||
| set(VBOX_LINUX_ON_WINDOWS FALSE CACHE BOOL | ||||
|   "Skip tests known to cause problems on a Linux VirtualBox guest on Windows.") | ||||
|  | ||||
| set(ENABLE_GCOV FALSE CACHE BOOL | ||||
|   "Compile with special options needed for gcov.") | ||||
|  | ||||
| # Our C code uses features from the C99 standard. | ||||
| macro(use_c99) | ||||
|   if (CMAKE_VERSION VERSION_LESS "3.1") | ||||
|     if (CMAKE_C_COMPILER_ID STREQUAL "GNU") | ||||
|       set (CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}") | ||||
|     endif () | ||||
|   else () | ||||
|     set (CMAKE_C_STANDARD 99) | ||||
|   endif () | ||||
| endmacro(use_c99) | ||||
|  | ||||
| # Our C++ code uses features from the C++11 standard. | ||||
| macro(use_cxx11) | ||||
|   if (CMAKE_VERSION VERSION_LESS "3.1") | ||||
|     if (CMAKE_C_COMPILER_ID STREQUAL "GNU") | ||||
|       # Use --std=gnu++0x instead of --std=gnu++11 in order to support GCC 4.6. | ||||
|       set (CMAKE_CXX_FLAGS "--std=gnu++0x ${CMAKE_C_FLAGS}") | ||||
|     endif () | ||||
|   else () | ||||
|     set (CMAKE_CXX_STANDARD 11) | ||||
|   endif () | ||||
| endmacro(use_cxx11) | ||||
|  | ||||
| set (LIBUSBP_VERSION ${LIBUSBP_VERSION_MAJOR}.${LIBUSBP_VERSION_MINOR}.${LIBUSBP_VERSION_PATCH}) | ||||
|  | ||||
| if (CMAKE_VERSION VERSION_GREATER "2.8.10") | ||||
|   string(TIMESTAMP YEAR "%Y") | ||||
| endif () | ||||
|  | ||||
| find_package(PkgConfig) | ||||
|  | ||||
| # Put libraries and executables in the top level of the build directory | ||||
| # so that the executables can find the libraries and it is easy to run | ||||
| # everything. | ||||
| set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) | ||||
| set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) | ||||
|  | ||||
| # Warn about everything. | ||||
| set (CMAKE_C_FLAGS "-Wall -Wextra -pedantic ${CMAKE_C_FLAGS}") | ||||
| set (CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic ${CMAKE_CXX_FLAGS}") | ||||
|  | ||||
| if (ENABLE_GCOV) | ||||
|   set (CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage ${CMAKE_C_FLAGS}") | ||||
| endif () | ||||
|  | ||||
| if (WIN32) | ||||
|  | ||||
|   # Enable correct behavior for the return value of vsnprintf. | ||||
|   add_definitions (-D__USE_MINGW_ANSI_STDIO=1) | ||||
|  | ||||
|   # Enable functions only available in Windows Vista and later, | ||||
|   # such as StringCompareEx. | ||||
|   add_definitions (-D_WIN32_WINNT=0x0600 -DNTDDI_VERSION=0x06000000) | ||||
|  | ||||
| endif () | ||||
|  | ||||
| # Detect Linux. | ||||
| if (CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||||
|   set (LINUX 1) | ||||
| endif () | ||||
|  | ||||
| # Install the header files into include/ | ||||
| install(FILES include/libusbp.h include/libusbp.hpp | ||||
|   DESTINATION "include/libusbp-${LIBUSBP_VERSION_MAJOR}") | ||||
|  | ||||
| add_subdirectory (src) | ||||
|  | ||||
| if (ENABLE_TESTS) | ||||
|   add_subdirectory (test) | ||||
|   add_subdirectory (manual_tests) | ||||
| endif () | ||||
|  | ||||
| if (ENABLE_EXAMPLES) | ||||
|   add_subdirectory (examples) | ||||
| endif () | ||||
|  | ||||
| if (WIN32) | ||||
|   add_subdirectory (install_helper) | ||||
| endif () | ||||
							
								
								
									
										74
									
								
								dep/libusbp/CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								dep/libusbp/CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| # Contributing to libusbp | ||||
|  | ||||
| ## Development Tools | ||||
|  | ||||
| The following tools are used to help build, test, debug, and document this library: | ||||
|  | ||||
| - USB Test Device A: This is a custom USB device that is used for testing this library.  You must have a device like this plugged in if you want to run the full test suite.  The `test/firmware/wixel` folder contains firmware that can turn a [Pololu Wixel](https://www.pololu.com/product/1337) into a USB Test Device A, and it should be possible to implement it on other USB-capable boards as well. | ||||
| - [cmake](http://www.cmake.org) | ||||
| - [catch](https://github.com/philsquared/Catch) | ||||
| - [Doxygen](http://www.stack.nl/~dimitri/doxygen/) | ||||
| - Development environments: | ||||
|   - Windows: [MSYS2](http://msys2.github.io/) | ||||
|   - macOS: [Homebrew](http://brew.sh/) | ||||
| - Memory leak checkers: | ||||
|   - Windows: Install Dr. Memory and run `drmemory -leaks_only ./run_test.exe` | ||||
|   - Linux: Install Valgrind and run `valgrind ./run_test` | ||||
|   - macOS:  Run `MallocStackLogging=1 ./run_test -p`, wait for the tests to finish, press Ctrl+Z, run `leaks run_test`, then finally run `fg` to go back to the stopped test process and end it. | ||||
|  | ||||
| ## Underlying API documentation | ||||
|  | ||||
| This section attempts to organize the documentation and information about the underlying APIs used by libusbp, which should be useful to anyone reviewing or editing the code. | ||||
|  | ||||
| ### Windows APIs | ||||
|  | ||||
| - [WinUSB](https://msdn.microsoft.com/en-us/library/windows/hardware/ff540196) | ||||
| - [SetupAPI](https://msdn.microsoft.com/en-us/library/cc185682) | ||||
| - [PnP Configuration Manager Reference](https://msdn.microsoft.com/en-us/library/windows/hardware/ff549717) | ||||
| - [Standard USB Identifiers](https://msdn.microsoft.com/en-us/library/windows/hardware/ff553356) | ||||
| - [What characters or bytes are valid in a USB serial number?](https://msdn.microsoft.com/en-us/library/windows/hardware/dn423379#usbsn) | ||||
| - [INFO: Windows Rundll and Rundll32 Interface](https://support.microsoft.com/en-us/kb/164787) | ||||
| - [MSI Custom Action Type 17](https://msdn.microsoft.com/en-us/library/aa368076?f=255&MSPPError=-2147217396) | ||||
|  | ||||
| ### Linux APIs | ||||
|  | ||||
| - udev | ||||
|   - [libudev Reference Manual](https://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ch01.html) | ||||
|   - [Kernel documentation of sysfs-bus-usb](https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-bus-usb) | ||||
|   - [sysfs.c](https://github.com/torvalds/linux/blob/master/drivers/usb/core/sysfs.c) | ||||
|   - [libudev and Sysfs Tutorial](http://www.signal11.us/oss/udev/) | ||||
| - USB device node | ||||
|   - [USB documentation folder in the kernel tree](https://github.com/torvalds/linux/tree/master/Documentation/usb) | ||||
|     - [USB error code documentation](https://github.com/torvalds/linux/blob/master/Documentation/usb/error-codes.txt) | ||||
|   - [usbdevice_fs.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/usbdevice_fs.h) | ||||
|   - [devio.c](https://github.com/torvalds/linux/blob/master/drivers/usb/core/devio.c) | ||||
|   - [Linux USB Drivers presentation](http://free-electrons.com/doc/linux-usb.pdf) | ||||
|   - [USB Drivers chapter from a book](http://lwn.net/images/pdf/LDD3/ch13.pdf) | ||||
|   - [linux_usbfs.c from libusb](https://github.com/libusb/libusb/blob/master/libusb/os/linux_usbfs.c) | ||||
| - Error numbers | ||||
|   - [errno.h](https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h) | ||||
|   - [errno-base.h](https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h) | ||||
|  | ||||
| ### macOS APIs | ||||
|  | ||||
| - [I/O Kit Framework Reference](https://developer.apple.com/library/mac/documentation/Darwin/Reference/IOKit/index.html#//apple_ref/doc/uid/TP30000815) | ||||
| - [IOKitLib.h Reference](https://developer.apple.com/library/mac/documentation/IOKit/Reference/IOKitLib_header_reference/) | ||||
| - [Accessing Hardware From Applications](https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware) | ||||
| - [USB Device Interface Guide](https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/USBBook/) | ||||
| - [IOUSBInterfaceClass.cpp](https://github.com/opensource-apple/IOUSBFamily/blob/master/IOUSBLib/Classes/IOUSBInterfaceClass.cpp) | ||||
| - [IOUSBInterfaceUserClient.cpp](https://github.com/opensource-apple/IOUSBFamily/blob/master/IOUSBUserClient/Classes/IOUSBInterfaceUserClient.cpp) | ||||
| - [darwin_usb.c from libusb](https://github.com/libusb/libusb/blob/master/libusb/os/darwin_usb.c) | ||||
| - [Mach messaging interface (mach ports)](http://www.gnu.org/software/hurd/gnumach-doc/Messaging-Interface.html) | ||||
|  | ||||
| ## Future development | ||||
|  | ||||
| Here are some things we might want to work on in future versions of the library: | ||||
|  | ||||
| - Serial port support.  (Even just listing the serial ports of a USB device would be useful.) | ||||
| - Human interface device (HID) support. | ||||
| - Hotplug support: detect when new devices are added and detect when a specific device is removed. | ||||
| - Stronger guarantees about thread safety.  (This might not require code changes.) | ||||
| - More options for asynchronous transfers. | ||||
| - Perhaps use asynchronous operations to implement the synchronous ones, like libusb does.  This would fix some quirks on various platforms: it would allow us to have timeouts for interrupt endpoints on Mac OS X and it would probably allow long synchronous operations on Linux to be interrupted with Ctrl+C.  The main drawback is that it would add complexity. | ||||
| - Sending data on OUT pipes. | ||||
| - A device library template to help people who want to make a cross-platform C or C++ library for a USB device based on libusbp. | ||||
							
								
								
									
										26
									
								
								dep/libusbp/Doxyfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								dep/libusbp/Doxyfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # Doxygen configuration file for generating documentation. | ||||
| PROJECT_NAME = "libusbp" | ||||
| OUTPUT_DIRECTORY = docs | ||||
| INLINE_INHERITED_MEMB = YES | ||||
| INPUT = README.md PLATFORM_NOTES.md CONTRIBUTING.md THREADS.md include | ||||
| USE_MDFILE_AS_MAINPAGE = README.md | ||||
| RECURSIVE = YES | ||||
| SOURCE_BROWSER = YES | ||||
| USE_MATHJAX = YES | ||||
| GENERATE_LATEX = NO | ||||
| # TYPEDEF_HIDES_STRUCT = YES | ||||
|  | ||||
| MACRO_EXPANSION        = YES | ||||
| EXPAND_ONLY_PREDEF     = YES | ||||
| PREDEFINED = \ | ||||
|     LIBUSBP_API= \ | ||||
|     LIBUSBP_WARN_UNUSED= \ | ||||
|     _WIN32=1 \ | ||||
|     __linux__=1 \ | ||||
|     __APPLE__=1 | ||||
|  | ||||
| EXCLUDE_SYMBOLS = \ | ||||
|     LIBUSBP_API \ | ||||
|     LIBUSBP_DLL_EXPORT \ | ||||
|     LIBUSBP_DLL_IMPORT \ | ||||
|     LIBUSBP_WARN_UNUSED | ||||
							
								
								
									
										25
									
								
								dep/libusbp/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								dep/libusbp/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| Copyright (c) 2015-2017 Pololu Corporation.  For more information, see | ||||
|  | ||||
| http://www.pololu.com/ | ||||
| http://forum.pololu.com/ | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person | ||||
| obtaining a copy of this software and associated documentation | ||||
| files (the "Software"), to deal in the Software without | ||||
| restriction, including without limitation the rights to use, | ||||
| copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the | ||||
| Software is furnished to do so, subject to the following | ||||
| conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be | ||||
| included in all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||||
| OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||||
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||||
| OTHER DEALINGS IN THE SOFTWARE. | ||||
							
								
								
									
										36
									
								
								dep/libusbp/PLATFORM_NOTES.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								dep/libusbp/PLATFORM_NOTES.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| # Platform notes | ||||
|  | ||||
| This page documents some notable differences between the platforms that libusbp supports that can affect the library's behavior. | ||||
|  | ||||
| ## Permissions | ||||
|  | ||||
| On Linux, a udev rule is typically needed in order to grant permissions for non-root users to directly access USB devices.  The simplest way to do this would be to add a file named `usb.rules` in the directory `/etc/udev/rules.d` with the following contents, which grants all users permission to access all USB devices: | ||||
|  | ||||
|     SUBSYSTEM=="usb", MODE="0666" | ||||
|  | ||||
|  | ||||
| ## Multiple handles to the same generic interface | ||||
|  | ||||
| On Linux, you can have multiple simultaneous handles open to the same generic interface of a device.  On Windows and macOS, this is not possible, and you will get an error with the code `LIBUSBP_ERROR_ACCESS_DENIED` when trying to open the second handle with `libusbp_generic_handle_open`. | ||||
|  | ||||
|  | ||||
| ## Timeouts for interrupt IN endpoints | ||||
|  | ||||
| On macOS, you cannot specify a timeout for an interrupt IN endpoint.  Doing so will result in the following error when you try to read from the endpoint: | ||||
|  | ||||
|     Failed to read from pipe.  (iokit/common) invalid argument.  Error code 0xe00002c2. | ||||
|  | ||||
|  | ||||
| ## Serial numbers | ||||
|  | ||||
| On Windows, calling `libusbp_device_get_serial_number` on a device that does not actually have a serial number will retrieve some other type of identifier that contains andpersands (&) and numbers.  On other platforms, an error will be returned with the code `LIBUSBP_ERROR_NO_SERIAL_NUMBER`. | ||||
|  | ||||
|  | ||||
| ## Synchronous operations and Ctrl+C | ||||
|  | ||||
| On Linux, a synchronous (blocking) USB transfer cannot be interrupted by pressing Ctrl+C.  Other signals will probably not work either. | ||||
|  | ||||
|  | ||||
| ## Interface-specific control transfers | ||||
|  | ||||
| Performing control transfers that are directed to a specific interface might not work correctly on all systems, especially if the device is a composite device and the interface you are connected to is not the one addressed in your control transfer.  For example, see [this message](https://sourceforge.net/p/libusb/mailman/message/34414447/) from the libusb mailing list. | ||||
							
								
								
									
										186
									
								
								dep/libusbp/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								dep/libusbp/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| # libusbp: Pololu USB Library | ||||
|  | ||||
| Version: 1.2.0<br/> | ||||
| Release date: 2020-11-16<br/> | ||||
| [www.pololu.com](https://www.pololu.com/) | ||||
|  | ||||
| The **Pololu USB Library** (also known as **libusbp**) is a cross-platform C library for accessing USB devices. | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| - Can retrieve the vendor ID, product ID, revision, and serial number for each connected USB device. | ||||
| - Can perform I/O on generic (vendor-defined) USB interfaces: | ||||
|   - Synchronous control transfers. | ||||
|   - Synchronous and asynchronous bulk/interrupt transfers on IN endpoints. | ||||
|   - Synchronous bulk/interrupt transfers on OUT endpoints. | ||||
| - Can retrieve the names of virtual serial ports provided by a specified USB device (e.g. "COM5"). | ||||
| - Provides detailed error information to the caller. | ||||
|   - Each error includes one or more English sentences describing the error, including error codes from underlying APIs. | ||||
|   - Some errors have libusbp-defined error codes that can be used to programmatically decide how to handle the error. | ||||
| - Provides an object-oriented C++ wrapper (using features of C++11). | ||||
| - Provides access to underlying identifiers, handles, and file descriptors. | ||||
|  | ||||
|  | ||||
| ## Supported platforms | ||||
|  | ||||
| The library runs on: | ||||
|  | ||||
| * Microsoft Windows (Windows Vista and later) | ||||
| * Linux | ||||
| * macOS (10.11 and later) | ||||
|  | ||||
|  | ||||
| ## Supported USB devices and interfaces | ||||
|  | ||||
| The library only supports certain types of USB devices. | ||||
|  | ||||
| On Windows, any generic interface that you want to access with this library must use WinUSB as its driver, so you will need to provide a driver package or use some other mechanism to make sure WinUSB is installed.  The generic interface must have a registry key named "DeviceInterfaceGUIDs" which is a REG_MULTI_SZ, and the first string in the key must be a valid GUID.  The "DeviceInterfaceGUID" key is not supported. | ||||
|  | ||||
| Both composite and non-composite devices are supported. | ||||
|  | ||||
| There is no support for switching device configurations or switching between alternate settings of an interface. | ||||
|  | ||||
| There is no support for accessing a generic interface that is included in an interface association and is not the first interface in the association. | ||||
|  | ||||
|  | ||||
| ## Platform differences | ||||
|  | ||||
| This library is a relatively simple wrapper around low-level USB APIs provided by each supported platform.  Because these APIs have different capabilities and behavior, you should not assume your program will work on a platform on which it has not been tested.  Some notable differences are documented in `PLATFORM_NOTES.md`. | ||||
|  | ||||
|  | ||||
| ## Comparison to libusb | ||||
|  | ||||
| This library has a lot in common with [libusb 1.0](http://libusb.info/), which has been around for longer and has many more features.  However, libusbp does have some useful features that libusb lacks, such as listing the serial number of a USB device without performing I/O or getting information about a USB device's virtual serial ports. | ||||
|  | ||||
|  | ||||
| ## Building from source on Windows with MSYS2 | ||||
|  | ||||
| The recommended way to build this library on Windows is to use [MSYS2](http://msys2.github.io/). | ||||
|  | ||||
| After installing MSYS2, launch it by selecting "MSYS2 MinGW 32-bit" or "MSYS2 MinGW 64-bit" from the Start Menu.  Then run this command to install the required packages: | ||||
|  | ||||
|     pacman -S $MINGW_PACKAGE_PREFIX-{toolchain,cmake} | ||||
|  | ||||
| If pacman prompts you to enter a selection of packages to install, just press enter to install all of the packages. | ||||
|  | ||||
| Download the source code of this library and navigate to the top-level directory using `cd`.  Then run these commands to build the library and install it: | ||||
|  | ||||
|     mkdir build | ||||
|     cd build | ||||
|     MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX | ||||
|     make install DESTDIR=/ | ||||
|  | ||||
| We currently do not provide any build files for Visual Studio.  You can use CMake to generate Visual Studio build files, and the library and its examples will probably compile, but we have not tested the resulting library. | ||||
|  | ||||
|  | ||||
| ## Building from source on Linux | ||||
|  | ||||
| First, you will need to make sure that you have a suitable compiler installed, such as gcc.  You can run `gcc -v` in a shell to make sure it is available. | ||||
|  | ||||
| You will also need to install CMake and libudev.  On Ubuntu, Raspbian, and other Debian-based distributions, the command to do this is: | ||||
|  | ||||
|     sudo apt-get install cmake libudev-dev | ||||
|  | ||||
| On Arch Linux, libudev should already be installed, and you can install CMake by running: | ||||
|  | ||||
|     sudo pacman -S cmake | ||||
|  | ||||
| Download the source code of this library and navigate to the top-level directory using `cd`.  Then run these commands to build the library and install it: | ||||
|  | ||||
|     mkdir build | ||||
|     cd build | ||||
|     cmake .. | ||||
|     make | ||||
|     sudo make install | ||||
|  | ||||
|  | ||||
| ## Building from source on macOS | ||||
|  | ||||
| First, install [Homebrew](http://brew.sh/), a package manager for macOS.  Then use Homebrew to install CMake by running the following command in a Terminal: | ||||
|  | ||||
|     brew install cmake | ||||
|  | ||||
| Download the source code of this library and navigate to the top-level directory using `cd`.  Then run these commands in a Terminal to build the library and install it: | ||||
|  | ||||
|     mkdir build | ||||
|     cd build | ||||
|     cmake .. | ||||
|     make | ||||
|     sudo make install | ||||
|  | ||||
|  | ||||
| ## Incorporating libusbp into a C/C++ project | ||||
|  | ||||
| The first step to incorporating libusbp into another project is to add the libusbp header folder to your project's include search path. | ||||
| The header folder is the folder that contains `libusbp.h` and `libusbp.hpp`, and it will typically be named `libusbp-1`. | ||||
| On systems with `pkg-config`, assuming libusbp has been installed properly, you can run the following command to get the include directory: | ||||
|  | ||||
|     pkg-config --cflags libusbp-1 | ||||
|  | ||||
| The output of that command is formatted so that it can be directly added to the command-line arguments for most compilers. | ||||
|  | ||||
| Next, you should include the appropriate libusbp header in your source code by adding an include directive at the top of one of your source files. | ||||
| To use the C API from a C or C++ project, you should write: | ||||
|  | ||||
|     #include <libusbp.h> | ||||
|  | ||||
| To use the C++ API from a C++ project, you should write: | ||||
|  | ||||
|     #include <libusbp.hpp> | ||||
|  | ||||
| After making the changes above, you should be able compile your project successfully. | ||||
| If the compiler says that the libusbp header file cannot be found, make sure you have specified the include path correctly as described above. | ||||
|  | ||||
| If you add a call to any of the libusbp functions and rebuild, you will probably get an undefined reference error from the linker.  To fix this, you need to add libusbp's linker settings to your project.  To get the linker settings, run: | ||||
|  | ||||
|     pkg-config --libs libusbp-1 | ||||
|  | ||||
| If you are using GCC and a shell that supports Bash-like syntax, here is an example command that compiles a single-file C program using the correct compiler and linker settings: | ||||
|  | ||||
|     gcc program.c `pkg-config --cflags --libs libusbp-1` | ||||
|  | ||||
| Here is an equivalent command for C++.  Note that we use the `--std=gnu++11` option because the libusbp C++ API requires features from C++11: | ||||
|  | ||||
|     g++ --std=gnu++11 program.cpp `pkg-config --cflags --libs libusbp-1` | ||||
|  | ||||
| The order of the arguments above matters: the user program must come before libusbp because it relies on symbols that are defined by libusbp. | ||||
|  | ||||
| The `examples` folder of this repository contains some example code that uses libusbp.  These examples can serve as a starting point for your own project. | ||||
|  | ||||
|  | ||||
| ## Versioning | ||||
|  | ||||
| The version numbers used by this library follow the rules of [Semantic Versioning 2.0.0](http://semver.org/). | ||||
|  | ||||
| A backwards-incompatible version of this library might be released in the future. | ||||
| If that happens, the new version will have a different major version number: its version number will be 2.0.0 and it will be known as libusbp-2 to `pkg-config`. | ||||
| This library was designed to support having different major versions installed side-by-side on the same computer, so you could have both libusbp-1 and libusbp-2 installed at the same time. | ||||
| However, you would not be able to use both versions from a single program. | ||||
|  | ||||
| If you write software that depends on libusbp, we recommend specifying which major version of libusbp your software uses in both the documentation of your software and in the scripts used to build it.  Scripts or instructions for downloading the source code of libusbp should use a branch name to ensure that they downlod the latest version of the code for a given major version number.  For example, the branch of this repository named "v1-latest" will always point to the latest release of libusbp-1. | ||||
|  | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| For detailed documentation of this library, see the header files `libusb.h` and `libusbp.hpp` in the `include` directory, and see the `.md` files in this directory.  You can also use [Doxygen](http://doxygen.org/) to generate documentation from those files. | ||||
|  | ||||
|  | ||||
| ## Version history | ||||
|  | ||||
| * 1.2.0 (2020-11-16): | ||||
|   * Linux: Made the library work with devices attached to the cp210x driver. | ||||
|   * Windows: Made the library work with devices that have lowercase letters in their hardware IDs. | ||||
| * 1.1.0 (2018-11-23): | ||||
|   * Added `libusbp_write_pipe`. | ||||
| * 1.0.4 (2017-08-29): | ||||
|   * Fixed a compilation error for macOS. | ||||
|   * Added the `lsport` example for listing multiple USB serial ports. | ||||
| * 1.0.3 (2017-08-29): | ||||
|   * Compiler flags from libudev.pc are now used. | ||||
|   * For static builds, libusbp-1.pc now requires libudev.pc instead of copying its flags. | ||||
|   * Always build the install helper as a shared library (DLL). | ||||
| * 1.0.2 (2017-06-29): | ||||
|   * Fixed some issues with using libusbp as a static library. | ||||
|   * Added the `drop_in` example, which shows how to use libusbp as a three-file library. | ||||
| * 1.0.1 (2016-03-20): Fixed the `libusbp_broadcast_setting_change*` functions (a bonus feature for the Windows version). | ||||
| * 1.0.0 (2016-03-02): Original release. | ||||
							
								
								
									
										18
									
								
								dep/libusbp/THREADS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								dep/libusbp/THREADS.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # Thread safety | ||||
|  | ||||
| If you are using multiple threads in your application, it is important to make sure that your calls to libusbp will be thread safe.  This page covers everything you need to know about thread safety and libusbp. | ||||
|  | ||||
| We will only discuss the C API functions defined in libusbp.h.  The C++ API defined in libusbp.hpp is just a simple wrapper around the C API and does not introduce or solve any thread safety issues. | ||||
|  | ||||
| This library does not create threads, use mutable global variables, use volatile variables, use mutexes, use memory barriers, or use reference counting. | ||||
|  | ||||
| On this page, two function calls are said to *conflict* with each other if there is no guarantee that executing the function calls concurrently on different threads will work as expected.  To characterize the thread-safety of libusbp, we will specify which pairs of function calls conflict with each other.  A function call consists of the name of a library function being called along with the values of its arguments. | ||||
|  | ||||
| Two function calls will not conflict if the memory areas pointed to by their arguments have no overlap.  This is because the library does not use any mutable global variables.  To determine whether the memory areas overlap, you will need to know which library objects hold pointers to other library objects.  The rules for that are defined in the "Pointer rules" section below. | ||||
|  | ||||
| If there is an overlap in the memory areas pointed to by the arguments of two functions calls, the overlap will not cause a conflict as long as all of the parameters that are responsible for the overlap are marked with the `const` qualifier in the header.  We use `const` as an indicator that the function will not modify the memory pointed to by that argument, and it will not call any API functions that might change the state of the underlying handles held by the object. | ||||
|  | ||||
| ## Pointer rules | ||||
|  | ||||
| * Each ::libusbp_async_in_pipe object may hold a pointer to the ::libusbp_generic_handle that it was created from.  Similarly, the ::libusbp_generic_handle may hold pointers to its ::libusbp_async_in_pipe objects. | ||||
| * All other objects contain no pointers to each other. | ||||
							
								
								
									
										2
									
								
								dep/libusbp/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								dep/libusbp/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| This was taken from https://github.com/pololu/libusbp on 2021-12-11. | ||||
|  | ||||
							
								
								
									
										4
									
								
								dep/libusbp/examples/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								dep/libusbp/examples/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| add_subdirectory(async_in) | ||||
| add_subdirectory(lsport) | ||||
| add_subdirectory(lsusb) | ||||
| add_subdirectory(port_name) | ||||
							
								
								
									
										9
									
								
								dep/libusbp/examples/async_in/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/examples/async_in/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(async_in async_in.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(async_in usbp) | ||||
							
								
								
									
										91
									
								
								dep/libusbp/examples/async_in/async_in.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								dep/libusbp/examples/async_in/async_in.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| /* This example shows how to read a constant stream of data from an IN endpoint. | ||||
|  * By queueing up a large number of asynchronous transfers, we ensure that the | ||||
|  * USB host controller is busy, and that it will retrieve data from the endpoint | ||||
|  * as fast as possible for an indefinite period of time. | ||||
|  * | ||||
|  * This example is designed to connect to Test Device A and read ADC data from | ||||
|  * endpoint 0x82. */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <stdio.h> | ||||
| #include <iostream> | ||||
| #ifdef _MSC_VER | ||||
| #define usleep(x) Sleep(((x) + 999) / 1000) | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | ||||
| const uint16_t vendor_id = 0x1FFB; | ||||
| const uint16_t product_id = 0xDA01; | ||||
| const uint8_t interface_number = 0; | ||||
| const bool composite = true; | ||||
| const uint8_t endpoint_address = 0x82; | ||||
| const size_t transfer_size = 5; | ||||
| const size_t transfer_count = 250; | ||||
|  | ||||
| // Prints the data in the given buffer to the standard output in HEX. | ||||
| void print_data(uint8_t * buffer, size_t size) | ||||
| { | ||||
|     for (uint8_t i = 0; i < size; i++) | ||||
|     { | ||||
|         printf("%02x", buffer[i]); | ||||
|         if (i < size - 1) { putchar(' '); } | ||||
|     } | ||||
|     printf("\n"); | ||||
|     fflush(stdout); | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id); | ||||
|     if (!device) | ||||
|     { | ||||
|         std::cerr << "Device not found." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     libusbp::generic_interface gi(device, interface_number, composite); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     libusbp::async_in_pipe pipe = handle.open_async_in_pipe(endpoint_address); | ||||
|     pipe.allocate_transfers(transfer_count, transfer_size); | ||||
|  | ||||
|     pipe.start_endless_transfers(); | ||||
|  | ||||
|     while(true) | ||||
|     { | ||||
|         uint8_t buffer[transfer_size]; | ||||
|         size_t transferred; | ||||
|         libusbp::error transfer_error; | ||||
|         while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error)) | ||||
|         { | ||||
|             if (transfer_error) { throw transfer_error; } | ||||
|             print_data(buffer, transferred); | ||||
|         } | ||||
|  | ||||
|         pipe.handle_events(); | ||||
|         usleep(500); | ||||
|     } | ||||
|  | ||||
|     // Note that closing an async_in_pipe cleanly without causing memory leaks | ||||
|     // can be difficult.  For more information, see the documentation of | ||||
|     // libusbp_async_in_pipe_close in libusbp.h.  In this example, we don't | ||||
|     // worry about it because we are only ever creating one pipe, so the amount | ||||
|     // of the memory leak is bounded. | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										2
									
								
								dep/libusbp/examples/drop_in/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								dep/libusbp/examples/drop_in/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| /libusbp* | ||||
| /lsusb* | ||||
							
								
								
									
										38
									
								
								dep/libusbp/examples/drop_in/build.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								dep/libusbp/examples/drop_in/build.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| # This script converts libusbp into a format that can easily be dropped into | ||||
| # other projects: one source file, one C header file, and one C++ header file. | ||||
| # | ||||
| # This is handy for people who want to use libusbp in their own C/C++ software | ||||
| # projects but don't want to deal with the issues that come with having one more | ||||
| # dependency. | ||||
| # | ||||
| # It also compiles the lsusb example program using the drop-in library. | ||||
|  | ||||
| set -eou pipefail | ||||
|  | ||||
| cd `dirname $0` | ||||
| SRC=../../src | ||||
|  | ||||
| { | ||||
|   echo "#if 0" | ||||
|   cat $SRC/../LICENSE.txt | ||||
|   echo "#endif" | ||||
|   cat $SRC/libusbp_internal.h | ||||
|   cat $SRC/*.c | ||||
|   echo "#ifdef _WIN32" | ||||
|   cat $SRC/windows/*.c | ||||
|   echo "#endif" | ||||
|   echo "#ifdef __linux__" | ||||
|   cat $SRC/linux/*.c | ||||
|   echo "#endif" | ||||
|   echo "#ifdef __APPLE__" | ||||
|   cat $SRC/mac/*.c | ||||
|   echo "#endif" | ||||
| } | sed 's/#include <libusbp_internal.h>//' > libusbp.c | ||||
|  | ||||
| cp ../lsusb/lsusb.cpp $SRC/../include/libusbp.h* . | ||||
|  | ||||
| # TODO: fix the library so we don't need -fpermissive here | ||||
|  | ||||
| g++ -std=gnu++11 -Wall -fpermissive -g -O2 -I. \ | ||||
|     -DLIBUSBP_DROP_IN -DLIBUSBP_STATIC \ | ||||
|     lsusb.cpp libusbp.c -ludev -o lsusb | ||||
							
								
								
									
										9
									
								
								dep/libusbp/examples/lsport/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/examples/lsport/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(lsport lsport.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(lsport usbp) | ||||
							
								
								
									
										108
									
								
								dep/libusbp/examples/lsport/lsport.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								dep/libusbp/examples/lsport/lsport.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| // This example prints the names of all USB serial ports along with information | ||||
| // about the USB devices they belong to. | ||||
| // | ||||
| // For each USB device, it prints the USB vendor ID, product ID, and serial | ||||
| // number on a line.  Then, on the following lines, it prints any serial port | ||||
| // names it found, sorted by interface number, ascending. | ||||
| // | ||||
| // Note: This example is slow and ugly because libusbp does not yet have | ||||
| // built-in support for listing serial ports; it only has support for finding | ||||
| // a serial port if you already know what USB device it is connected to and what | ||||
| // interface you expect the port to be on.  This might be improved in the future. | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <iostream> | ||||
| #include <iomanip> | ||||
|  | ||||
| std::string serial_number_or_default(const libusbp::device & device, | ||||
|     const std::string & def) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         return device.get_serial_number(); | ||||
|     } | ||||
|     catch (const libusbp::error & error) | ||||
|     { | ||||
|         if (error.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) | ||||
|         { | ||||
|             return def; | ||||
|         } | ||||
|         throw; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool try_print_port_name(const libusbp::device & device, | ||||
|   uint8_t interface_number, bool composite) | ||||
| { | ||||
|     std::string port_name; | ||||
|     try | ||||
|     { | ||||
|         libusbp::serial_port port(device, interface_number, composite); | ||||
|         port_name = port.get_name(); | ||||
|     } | ||||
|     catch (const libusbp::error & error) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     std::cout << "  " << port_name << std::endl; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     auto devices = libusbp::list_connected_devices(); | ||||
|     for (const libusbp::device & device : devices) | ||||
|     { | ||||
|       bool success = false; | ||||
|  | ||||
|       // Print the USB device info. | ||||
|       uint16_t vendor_id = device.get_vendor_id(); | ||||
|       uint16_t product_id = device.get_product_id(); | ||||
|       std::string serial_number = serial_number_or_default(device, "-"); | ||||
|       std::ios::fmtflags flags(std::cout.flags()); | ||||
|       std::cout | ||||
|           << std::hex << std::setfill('0') << std::right | ||||
|           << std::setw(4) << vendor_id | ||||
|           << ':' | ||||
|           << std::setw(4) << product_id | ||||
|           << ' ' | ||||
|           << std::setfill(' ') << std::left << serial_number | ||||
|           << std::endl; | ||||
|       std::cout.flags(flags); | ||||
|  | ||||
|       // First, assume the device is composite and try the first 16 interfaces. | ||||
|       // Most devices don't have more interfaces than that.  Trying all 255 possible | ||||
|       // interfaces slows the program down noticeably.  This issue could be fixed if | ||||
|       // we added better serial port enumeration support to libusbp. | ||||
|       for (uint32_t i = 0; i < 16; i++) | ||||
|       { | ||||
|         success = try_print_port_name(device, i, true) || success; | ||||
|       } | ||||
|  | ||||
|       // Try to find a port assuming the device is non-composite.  Only do so if | ||||
|       // no ports were found earlier, to help avoid printing the same port twice. | ||||
|       if (!success) | ||||
|       { | ||||
|         try_print_port_name(device, 0, false); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|       return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|       std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										9
									
								
								dep/libusbp/examples/lsusb/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/examples/lsusb/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(lsusb lsusb.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(lsusb usbp) | ||||
							
								
								
									
										80
									
								
								dep/libusbp/examples/lsusb/lsusb.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								dep/libusbp/examples/lsusb/lsusb.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* This example shows how to gather information about the USB devices connected | ||||
|  * to the system and print it. */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <iostream> | ||||
| #include <iomanip> | ||||
|  | ||||
| std::string serial_number_or_default(const libusbp::device & device, | ||||
|     const std::string & def) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         return device.get_serial_number(); | ||||
|     } | ||||
|     catch (const libusbp::error & error) | ||||
|     { | ||||
|         if (error.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) | ||||
|         { | ||||
|             return def; | ||||
|         } | ||||
|         throw; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void print_device(libusbp::device & device) | ||||
| { | ||||
|     uint16_t vendor_id = device.get_vendor_id(); | ||||
|     uint16_t product_id = device.get_product_id(); | ||||
|     uint16_t revision = device.get_revision(); | ||||
|     std::string serial_number = serial_number_or_default(device, "-"); | ||||
|     std::string os_id = device.get_os_id(); | ||||
|  | ||||
|     // Note: The serial number might have spaces in it, so it should be the last | ||||
|     // field to avoid confusing programs that are looking for a field after the | ||||
|     // serial number. | ||||
|  | ||||
|     std::ios::fmtflags flags(std::cout.flags()); | ||||
|     std::cout | ||||
|         << std::hex << std::setfill('0') << std::right | ||||
|         << std::setw(4) << vendor_id | ||||
|         << ':' | ||||
|         << std::setw(4) << product_id | ||||
|         << ' ' | ||||
|         << std::setfill(' ') << std::setw(2) << (revision >> 8) | ||||
|         << '.' | ||||
|         << std::setfill('0') << std::setw(2) << (revision & 0xFF) | ||||
|         << ' ' | ||||
|         << os_id | ||||
|         << ' ' | ||||
|         << std::setfill(' ') << std::left << serial_number | ||||
|         << std::endl; | ||||
|     std::cout.flags(flags); | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     std::vector<libusbp::device> list = libusbp::list_connected_devices(); | ||||
|     for (auto it = list.begin(); it != list.end(); ++it) | ||||
|     { | ||||
|         print_device(*it); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										9
									
								
								dep/libusbp/examples/port_name/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/examples/port_name/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(port_name port_name.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(port_name usbp) | ||||
							
								
								
									
										44
									
								
								dep/libusbp/examples/port_name/port_name.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								dep/libusbp/examples/port_name/port_name.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| // This example shows how to get the name of a USB serial port (e.g. "COM6") | ||||
| // for a single USB device. | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <iostream> | ||||
| #include <iomanip> | ||||
|  | ||||
| const uint16_t vendor_id = 0x1FFB; | ||||
| const uint16_t product_id = 0xDA01; | ||||
| const uint8_t interface_number = 2; | ||||
| const bool composite = true; | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id); | ||||
|     if (!device) | ||||
|     { | ||||
|         std::cerr << "Device not found." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     libusbp::serial_port port(device, interface_number, composite); | ||||
|     std::string port_name = port.get_name(); | ||||
|     std::cout << port_name << std::endl; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										737
									
								
								dep/libusbp/include/libusbp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										737
									
								
								dep/libusbp/include/libusbp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,737 @@ | ||||
| // Copyright (C) Pololu Corporation.  See www.pololu.com for details. | ||||
|  | ||||
| /*! \file libusbp.h | ||||
|  * | ||||
|  * This header file provides the C API for libusbp. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /*! The major component of libusbp's version number.  In accordance with | ||||
|  * semantic versioning, this gets incremented every time there is a breaking | ||||
|  * change. */ | ||||
| #define LIBUSBP_VERSION_MAJOR 1 | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <wtypesbase.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #  define LIBUSBP_DLL_EXPORT __declspec(dllexport) | ||||
| #  define LIBUSBP_DLL_IMPORT __declspec(dllimport) | ||||
| #else | ||||
| #  define LIBUSBP_DLL_IMPORT __attribute__((visibility ("default"))) | ||||
| #  define LIBUSBP_DLL_EXPORT __attribute__((visibility ("default"))) | ||||
| #endif | ||||
|  | ||||
| #ifdef _MSC_VER | ||||
| #define LIBUSBP_WARN_UNUSED _Check_return_ | ||||
| #else | ||||
| #define LIBUSBP_WARN_UNUSED __attribute__((warn_unused_result)) | ||||
| #endif | ||||
|  | ||||
| #ifdef LIBUSBP_STATIC | ||||
| #  define LIBUSBP_API | ||||
| #else | ||||
| #error not static | ||||
| #  ifdef LIBUSBP_EXPORTS | ||||
| #    define LIBUSBP_API LIBUSBP_DLL_EXPORT | ||||
| #  else | ||||
| #    define LIBUSBP_API LIBUSBP_DLL_IMPORT | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| /*! Some functions in this library return strings to the caller via a char ** | ||||
|  * argument.  If the function call was successful, it is the caller's | ||||
|  * responsibility to free those strings by passing them to this function. | ||||
|  * Passing the NULL pointer to this function is OK.  Do not pass any strings to | ||||
|  * this function unless they were previously returned by a call to this | ||||
|  * library.  Do not free the same non-NULL string twice. */ | ||||
| LIBUSBP_API | ||||
| void libusbp_string_free(char *); | ||||
|  | ||||
|  | ||||
| /** libusbp_error **************************************************************/ | ||||
|  | ||||
| /*! A libusbp_error object represents an error that occurred in the library. | ||||
|  * Many functions return a libusbp_error pointer as a return value.  The | ||||
|  * convention is that a NULL pointer indicates success.  If the pointer is not | ||||
|  * NULL, the caller needs to free it at some point by calling | ||||
|  * libusbp_error_free(). | ||||
|  * | ||||
|  * NULL is a valid value for a libusbp_error pointer, and can be passed to any | ||||
|  * function in this library that takes a libusbp_error pointer. */ | ||||
| typedef struct libusbp_error | ||||
|                libusbp_error; | ||||
|  | ||||
| /*! Each ::libusbp_error can have 0 or more error codes that give additional | ||||
|  * information about the error that might help the caller take the right action | ||||
|  * when the error occurs.  This enum defines which error codes are possible. */ | ||||
| enum libusbp_error_code | ||||
| { | ||||
|     /*! There were problems allocating memory.  A memory shortage might be the | ||||
|      * root cause of the error, or there might be another error that is masked | ||||
|      * by the memory problems. */ | ||||
|     LIBUSBP_ERROR_MEMORY = 1, | ||||
|  | ||||
|     /*! It is possible that the error was caused by a temporary condition, such | ||||
|      * as the operating system taking some time to initialize drivers.  Any | ||||
|      * function that could return this error will say so explicitly in its | ||||
|      * documentation so you do not have to worry about handling it in too many | ||||
|      * places. */ | ||||
|     LIBUSBP_ERROR_NOT_READY = 2, | ||||
|  | ||||
|     /*! Access was denied.  A common cause of this error on Windows is that | ||||
|      *  another application has a handle open to the same device. */ | ||||
|     LIBUSBP_ERROR_ACCESS_DENIED = 3, | ||||
|  | ||||
|     /*! The device does not have a serial number. */ | ||||
|     LIBUSBP_ERROR_NO_SERIAL_NUMBER = 4, | ||||
|  | ||||
|     /*! The device took too long to respond to a request or transfer data. */ | ||||
|     LIBUSBP_ERROR_TIMEOUT = 5, | ||||
|  | ||||
|     /*! The error might have been caused by the device being disconnected, but | ||||
|      * it is possible it was caused by something else. */ | ||||
|     LIBUSBP_ERROR_DEVICE_DISCONNECTED = 6, | ||||
|  | ||||
|     /*! The error might have been caused by the host receiving a STALL packet | ||||
|      * from the device, but it is possible it was caused by something else. */ | ||||
|     LIBUSBP_ERROR_STALL = 7, | ||||
|  | ||||
|     /*! The error might have been caused by the transfer getting cancelled from | ||||
|      * the host side.  Some data might have been transferred anyway. */ | ||||
|     LIBUSBP_ERROR_CANCELLED = 8, | ||||
| }; | ||||
|  | ||||
| /*! Attempts to copy an error.  If you copy a NULL ::libusbp_error | ||||
|  * pointer, the result will also be NULL.  If you copy a non-NULL ::libusbp_error | ||||
|  * pointer, the result will be non-NULL, but if there are issues allocating | ||||
|  * memory, then the copied error might have different properties than the | ||||
|  * original error, and it will have the ::LIBUSBP_ERROR_MEMORY code. | ||||
|  * | ||||
|  * It is the caller's responsibility to free the copied error. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_error_copy(const libusbp_error *); | ||||
|  | ||||
| /*! Frees a returned error object.  Passing the NULL pointer to this function is | ||||
|  * OK.  Do not free the same non-NULL error twice. */ | ||||
| LIBUSBP_API | ||||
| void libusbp_error_free(libusbp_error *); | ||||
|  | ||||
| /*! Returns true if the error has specified error code.  The error codes are | ||||
|  *  listed in the ::libusbp_error_code enum. */ | ||||
| LIBUSBP_API bool libusbp_error_has_code(const libusbp_error *, uint32_t code); | ||||
|  | ||||
| /*! Returns an English-language ASCII-encoded string describing the error.  The | ||||
|  * message consists of one or more sentences.  If there are multiple sentences, | ||||
|  * the earlier ones will typically explain the context that the error happened | ||||
|  * in. | ||||
|  * | ||||
|  * The returned pointer will be valid until the error is freed, at which point | ||||
|  * it might become invalid.  Do not pass the returned pointer to | ||||
|  * libusbp_string_free(). */ | ||||
| LIBUSBP_API const char * libusbp_error_get_message(const libusbp_error *); | ||||
|  | ||||
|  | ||||
| /** libusbp_async_in_pipe ******************************************************/ | ||||
|  | ||||
| /*! A libusbp_async_in_pipe is an object that holds the memory and other data | ||||
|  *  structures for a set of asynchronous USB requests to read data from a | ||||
|  *  non-zero endpoint.  It can be used to read data from a bulk or IN endpoint | ||||
|  *  with high throughput. */ | ||||
| typedef struct libusbp_async_in_pipe | ||||
|                libusbp_async_in_pipe; | ||||
|  | ||||
| /*! Closes the pipe immediately.  Note that if the pipe has any | ||||
|  * pending transfers, then it is possible that they cannot be freed | ||||
|  * by this function.  Freeing a pipe with pending transfers could | ||||
|  * cause a memory leak, but is otherwise safe. */ | ||||
| LIBUSBP_API | ||||
| void libusbp_async_in_pipe_close(libusbp_async_in_pipe *); | ||||
|  | ||||
| /*! Allocates buffers and other data structures for performing multiple | ||||
|  * concurrent transfers on the pipe. | ||||
|  * | ||||
|  * The @a transfer_count parameter specifies how many transfers to allocate.  You | ||||
|  * can also think of this as the maximum number of concurrent transfers that can | ||||
|  * be active at the same time. | ||||
|  * | ||||
|  * The @a transfer_size parameter specifies how large each transfer's buffer should | ||||
|  * be, and is also the number of bytes that will be requested from the operating | ||||
|  * system when the transfer is submitted. | ||||
|  * | ||||
|  * It is best to set the transfer size to a multiple of the maximum packet size | ||||
|  * of the endpoint.  Otherwise, you might get an error when the device sends | ||||
|  * more data than can fit in the transfer's buffer.  This type of error is | ||||
|  * called an overflow. | ||||
|  * | ||||
|  * If you want to be reading the pipe every millisecond without gaps, you should | ||||
|  * set the transfer count high enough so that it would take about 100 ms to 250 | ||||
|  * ms to finish all the transfers.  As long as the operating system runs your | ||||
|  * process that often, you should be able to keep the USB host controller busy | ||||
|  * permanently.  (Though we have observed gaps in the transfers when trying to | ||||
|  * do this inside a VirtualBox machine.) | ||||
|  * | ||||
|  * You should not set the transfer count too high, or else it might end up | ||||
|  * taking a long time to cancel the transfers when you are closing the pipe. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_async_in_pipe_allocate_transfers( | ||||
|     libusbp_async_in_pipe *, | ||||
|     size_t transfer_count, | ||||
|     size_t transfer_size); | ||||
|  | ||||
| /*! Starts reading data from the pipe. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_async_in_pipe_start_endless_transfers( | ||||
|     libusbp_async_in_pipe *); | ||||
|  | ||||
| /*! Checks for new events, such as a transfer completing.  This | ||||
|  * function and libusbp_async_in_pipe_handle_finished_transfer() should | ||||
|  * be called regularly in order to get data from the pipe. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_async_in_pipe_handle_events(libusbp_async_in_pipe *); | ||||
|  | ||||
| /*! Retrieves a boolean saying whether there are any pending | ||||
|  * transfers.  A pending transfer is a transfer that was submitted to | ||||
|  * the operating system, and it may have been completed, but it has | ||||
|  * not been passed to the caller yet via | ||||
|  * libusbp_async_in_pipe_handle_finished_transfer(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED libusbp_error * | ||||
| libusbp_async_in_pipe_has_pending_transfers( | ||||
|     libusbp_async_in_pipe *, | ||||
|     bool * result); | ||||
|  | ||||
| /*! Checks to see if there is a finished transfer that can be handled. | ||||
|  * If there is one, then this function retrieves the data from the | ||||
|  * transfer, the number of bytes transferred, and any error that might | ||||
|  * have occurred related to the transfer. | ||||
|  * | ||||
|  * @param finished An optional output pointer used to return a pointer that | ||||
|  * indicates whether a transfer was finished.  If the returned value is false, | ||||
|  * then no transfer was finished, and there is no transfer_error or data to | ||||
|  * handle. | ||||
|  * | ||||
|  * @param buffer An optional output pointer used to return the data | ||||
|  * from the transfer.  The buffer must be at least as large as the | ||||
|  * transfer size specifed when | ||||
|  * libusbp_async_in_pipe_allocate_transfers was called. | ||||
|  * | ||||
|  * @param transferred An optional output pointer used to return the | ||||
|  * number of bytes transferred. | ||||
|  * | ||||
|  * @param transfer_error An optional pointer used to return an error | ||||
|  * related to the transfer, such as a timeout or a cancellation.  If | ||||
|  * this pointer is provided, and a non-NULL error is returned via it, | ||||
|  * then the error must later be freed with libusbp_error_free().  There | ||||
|  * will never be a non-NULL transfer error if there is a regular error | ||||
|  * returned as the return value of this function. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED libusbp_error * | ||||
| libusbp_async_in_pipe_handle_finished_transfer( | ||||
|     libusbp_async_in_pipe *, | ||||
|     bool * finished, | ||||
|     void * buffer, | ||||
|     size_t * transferred, | ||||
|     libusbp_error ** transfer_error); | ||||
|  | ||||
| /*! Cancels all the transfers for this pipe.  The cancellation is | ||||
|  * asynchronous, so it won't have an immediate effect.  If you want | ||||
|  * to actually make sure that all the transfers get cancelled, you | ||||
|  * will need to call libusbp_async_in_pipe_handle_events() and | ||||
|  * libusbp_async_in_pipe_handle_finished_transfer() repeatedly until | ||||
|  * libusbp_async_in_pipe_has_pending_transfers() indicates there are | ||||
|  * no pending transfers left. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_async_in_pipe_cancel_transfers(libusbp_async_in_pipe *); | ||||
|  | ||||
|  | ||||
| /** libusbp_device *************************************************************/ | ||||
|  | ||||
| /*! Represents a single USB device.  A composite device with multiple functions | ||||
|  *  is represented by a single libusbp_device object. | ||||
|  * | ||||
|  * A NULL libusbp_device pointer is valid and can be passed to any function in | ||||
|  * this library that takes such pointers.  */ | ||||
| typedef struct libusbp_device | ||||
|                libusbp_device; | ||||
|  | ||||
| /*! Finds all the USB devices connected to the computer and returns a list of them. | ||||
|  * | ||||
|  * The optional @a device_count parameter is used to return the number of | ||||
|  * devices in the list.  The list is actually one element larger because it ends | ||||
|  * with a NULL pointer. | ||||
|  * | ||||
|  * If this function is successful (the returned error pointer is NULL), then you | ||||
|  * must later free each device by calling libusbp_device_free() and free the | ||||
|  * list by calling libusbp_list_free().  The order in which the retrieved | ||||
|  * objects are freed does not matter. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_list_connected_devices( | ||||
|     libusbp_device *** device_list, | ||||
|     size_t * device_count); | ||||
|  | ||||
| /*! Frees a device list returned by libusbp_list_connected_device(). */ | ||||
| LIBUSBP_API | ||||
| void libusbp_list_free(libusbp_device ** list); | ||||
|  | ||||
| /*! Finds a device with the specified vendor ID and product ID and returns a | ||||
|  * pointer to it.  If no device can be found, returns a NULL pointer.  If the | ||||
|  * retrieved device pointer is not NULL, you must free it later by calling | ||||
|  * libusbp_device_free().  The retrieved device pointer will always be NULL if | ||||
|  * an error is returned. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_find_device_with_vid_pid( | ||||
|     uint16_t vendor_id, | ||||
|     uint16_t product_id, | ||||
|     libusbp_device ** device); | ||||
|  | ||||
| /*! Makes a copy of a device object.  If this function is successful, you will | ||||
|  * need to free the copy by calling libusbp_device_free() at some point. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_copy( | ||||
|     const libusbp_device * source, | ||||
|     libusbp_device ** dest); | ||||
|  | ||||
| /*! Frees a device object.  Passing a NULL pointer to this function is OK.  Do | ||||
|  * not free the same non-NULL device twice. */ | ||||
| LIBUSBP_API void libusbp_device_free(libusbp_device *); | ||||
|  | ||||
| /*! Gets the USB vendor ID of the device (idVendor). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_get_vendor_id( | ||||
|     const libusbp_device *, | ||||
|     uint16_t * vendor_id); | ||||
|  | ||||
| /*! Gets the USB product ID of the device (idProduct). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_get_product_id( | ||||
|     const libusbp_device *, | ||||
|     uint16_t * product_id); | ||||
|  | ||||
| /*! Gets the USB revision code of the device (bcdDevice). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_get_revision( | ||||
|     const libusbp_device *, | ||||
|     uint16_t * revision); | ||||
|  | ||||
| /*! Gets the serial number of the device as an ASCII-encoded string. | ||||
|  * | ||||
|  * On Windows, this just returns the segment of the Device Instance ID after the | ||||
|  * last slash.  If the device does not have a serial number, it will return some | ||||
|  * other type of identifier that contains andpersands (&).  Windows ignores | ||||
|  * serial numbers with invalid characters in them.  For more information, see: | ||||
|  * | ||||
|  *   https://msdn.microsoft.com/en-us/library/windows/hardware/dn423379#usbsn | ||||
|  * | ||||
|  * On other systems, if the device does not have a serial number, then this | ||||
|  * function returns an error with the code ::LIBUSBP_ERROR_NO_SERIAL_NUMBER. | ||||
|  * | ||||
|  * (Most applications should only call this function on specific USB devices | ||||
|  * that are already known to have serial numbers, in which case the lack of a | ||||
|  * serial number really does indicate a failure.) | ||||
|  * | ||||
|  * You should free the returned string by calling libusbp_string_free(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_get_serial_number( | ||||
|     const libusbp_device *, | ||||
|     char ** serial_number); | ||||
|  | ||||
| /*! Gets an operating system-specific string that identifies the device. | ||||
|  * | ||||
|  * Note that the level of specificity provided by the ID depends on the system | ||||
|  * you are on, and whether your device has a USB serial number.  As long as the | ||||
|  * device remains connected to the bus, this ID is not expected to change and | ||||
|  * there should be no other devices that have the same ID.  However, if the | ||||
|  * device gets disconnected from the bus, it may be possible for the ID to be | ||||
|  * reused by another device. | ||||
|  * | ||||
|  * @b Windows: This will be a device instance ID, and it will look something | ||||
|  * like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * USB\\VID_1FFB&PID_DA01\6&11A23516&18&0000 | ||||
|  * </pre> | ||||
|  * | ||||
|  * If your device has a serial number, the part after the slash will be the | ||||
|  * serial number.  Otherwise, it will be a string with andpersands in it. | ||||
|  * | ||||
|  * @b Linux: This will be a sysfs path, and it will look like something like | ||||
|  * this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2 | ||||
|  * </pre> | ||||
|  * | ||||
|  * <b>macOS:</b> This will be an integer from | ||||
|  * IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with | ||||
|  * no leading zeros.  It will look something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * 10000021a | ||||
|  * </pre> | ||||
|  */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_device_get_os_id( | ||||
|     const libusbp_device *, | ||||
|     char ** id); | ||||
|  | ||||
|  | ||||
| /** libusbp_generic_interface **************************************************/ | ||||
|  | ||||
| /*! Represents a generic or vendor-defined interface of a USB device.  A null | ||||
|  * libusbp_generic_interface pointer is valid and can be passed to any function | ||||
|  * in this library that takes such pointers. */ | ||||
| typedef struct libusbp_generic_interface | ||||
|                libusbp_generic_interface; | ||||
|  | ||||
| /*! Creates a generic interface object for a specified interface of the | ||||
|  * specified USB device.  This function does as many checks as possible to make | ||||
|  * sure that a handle to the interface could be opened, without actually opening | ||||
|  * it yet. | ||||
|  * | ||||
|  * On all platforms, if a record of the interface cannot be found, then an error | ||||
|  * is returned with the code LIBUSBP_ERROR_NOT_READY, because this could just be | ||||
|  * a temporary condition that happens right after the device is plugged in. | ||||
|  * | ||||
|  * On Windows, the generic interface must use the WinUSB driver, or this | ||||
|  * function will fail.  If it is using no driver, that could be a temporary | ||||
|  * condition, and the error returned will use the LIBUSBP_ERROR_NOT_READY error | ||||
|  * code. | ||||
|  * | ||||
|  * On Linux, if the corresponding devnode file does not exist, an error with | ||||
|  * code LIBUSBP_ERROR_NOT_READY is returned.  If the interface is assigned to a | ||||
|  * driver that is not "usbfs", an error is returned. | ||||
|  * | ||||
|  * On macOS, we do not have any additional checks beyond just making sure | ||||
|  * that an entry for the interface is found.  For non-composite devices, that | ||||
|  * check is deferred until a handle is opened. | ||||
|  * | ||||
|  * @param interface_number The lowest @a bInterfaceNumber for the interfaces in | ||||
|  * the USB function you want to use. | ||||
|  * | ||||
|  * @param composite Should be true if the device is composite, and false | ||||
|  * otherwise. | ||||
|  * | ||||
|  * The returned object must be freed with libusbp_generic_interface_free(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_interface_create( | ||||
|     const libusbp_device *, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_generic_interface **); | ||||
|  | ||||
| /*! Frees the specified generic interface object.  Passing the NULL pointer to | ||||
|  * this function is OK.  Do not free the same non-NULL pointer twice. */ | ||||
| LIBUSBP_API void libusbp_generic_interface_free(libusbp_generic_interface *); | ||||
|  | ||||
| /*! Makes a copy of the generic interface object.  The copy must be freed with | ||||
|  *  libusbp_generic_interface_free(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_interface_copy( | ||||
|     const libusbp_generic_interface * source, | ||||
|     libusbp_generic_interface ** dest); | ||||
|  | ||||
| /*! Returns an operating system-specific string that can be used to uniquely | ||||
|  * identify this generic interface. | ||||
|  * | ||||
|  * <b>Windows:</b> This will be a device instance ID specific to this interface, and | ||||
|  * it will look something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * USB\\VID_1FFB&PID_DA01&MI_00\6&11A23516&18&0000 | ||||
|  * </pre> | ||||
|  * | ||||
|  * <b>Linux:</b> This will be a sysfs path specific to this interface, and it will | ||||
|  * look like something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2/1-2:1.0 | ||||
|  * </pre> | ||||
|  * | ||||
|  * <b>macOS:</b> This will be an integer from | ||||
|  * IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with | ||||
|  * no leading zeros.  It will look something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * 10000021a | ||||
|  * </pre> | ||||
|  */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_interface_get_os_id( | ||||
|     const libusbp_generic_interface *, | ||||
|     char ** id); | ||||
|  | ||||
| /*! Returns an operating system-specific filename corresponding to this | ||||
|  * interface. | ||||
|  * | ||||
|  * <b>Windows:</b> This will be the name of a file you can use with CreateFile to | ||||
|  * access the device, and it will look something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * \\\\?\\usb#vid_1ffb&pid_da01&mi_00#6&11a23516&18&0000#{99c4bbb0-e925-4397-afee-981cd0702163} | ||||
|  * </pre> | ||||
|  * | ||||
|  * <b>Linux:</b> this will return a device node file name that represents the | ||||
|  * overall USB device.  It will look something like: | ||||
|  * | ||||
|  * <pre> | ||||
|  * /dev/bus/usb/001/007 | ||||
|  * </pre> | ||||
|  * | ||||
|  * <b>macOS:</b> This will be an integer from | ||||
|  * IORegistryEntryGetRegistryEntryID, formatted as a lower-case hex number with | ||||
|  * no leading zeros.  It will look something like this: | ||||
|  * | ||||
|  * <pre> | ||||
|  * 10000021a | ||||
|  * </pre> | ||||
|  */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_interface_get_os_filename( | ||||
|     const libusbp_generic_interface *, | ||||
|     char ** filename); | ||||
|  | ||||
|  | ||||
| /** libusbp_generic_handle *****************************************************/ | ||||
|  | ||||
| /*! Represents a generic handle to a USB device.  This handle can be used to | ||||
|  * perform operations such as control transfers and reading and writing data | ||||
|  * from non-zero endpoints. | ||||
|  * | ||||
|  * NULL is a valid value for a libusbp_generic_handle pointer, and can be passed | ||||
|  * in any functions of this library that take a libusbp_generic_handle | ||||
|  * pointer. */ | ||||
| typedef struct libusbp_generic_handle | ||||
|                libusbp_generic_handle; | ||||
|  | ||||
| /*! Opens a generic handle to the specified interface of a USB device which can | ||||
|  * be used to perform USB I/O operations. | ||||
|  * | ||||
|  * The handle must later be closed with libusbp_generic_handle_close(). | ||||
|  * | ||||
|  * On Windows, for devices using WinUSB, if another application has a handle | ||||
|  * open already when this function is called, then this function will fail and | ||||
|  * the returned error will have code ::LIBUSBP_ERROR_ACCESS_DENIED. | ||||
|  * | ||||
|  * On macOS, this function will set the device's configuration to 1 as a | ||||
|  * side effect in case it is not already configured. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_handle_open( | ||||
|     const libusbp_generic_interface *, | ||||
|     libusbp_generic_handle **); | ||||
|  | ||||
| /*! Closes and frees the specified generic handle.  It is OK to pass NULL to | ||||
|  * this function.  Do not close the same non-NULL handle twice. All | ||||
|  * ::libusbp_async_in_pipe objects created by the handle must be closed before | ||||
|  * closing the handle. */ | ||||
| LIBUSBP_API | ||||
| void libusbp_generic_handle_close( | ||||
|     libusbp_generic_handle *); | ||||
|  | ||||
| /*! Creates a new asynchronous pipe object for reading data in from the device | ||||
|  * on one of its bulk or interrupt IN endpoints. | ||||
|  * | ||||
|  * The behavior of this library is unspecified if you use both an asynchronous | ||||
|  * IN pipe and synchronous reads with libusbp_read_pipe() on the same pipe of | ||||
|  * the same generic handle.  One reason for that is because for WinUSB devices, | ||||
|  * this function enables RAW_IO for the pipe, and it does not turn off RAW_IO | ||||
|  * again after the pipe is closed.  So the behavior of libusbp_read_pipe() could | ||||
|  * change depending on whether an asynchronous IN pipe has been used. */ | ||||
| LIBUSBP_API | ||||
| libusbp_error * libusbp_generic_handle_open_async_in_pipe( | ||||
|     libusbp_generic_handle *, | ||||
|     uint8_t pipe_id, | ||||
|     libusbp_async_in_pipe ** async_in_pipe); | ||||
|  | ||||
| /*! Sets a timeout for a particular pipe on the USB device. | ||||
|  * | ||||
|  * The @a pipe_id should either be 0 to specify control transfers on endpoint 0, or | ||||
|  * should be a bEndpointAddress value from one of the device's endpoint | ||||
|  * descriptors.  Specifying an invalid pipe might result in an error. | ||||
|  * | ||||
|  * The timeout value is specified in milliseconds, and a value of 0 means no | ||||
|  * timeout (wait forever).  If this function is not called, the default behavior | ||||
|  * of the handle is to have no timeout. | ||||
|  * | ||||
|  * The behavior of this function is unspecified if there is any data being | ||||
|  * transferred on the pipe while this function is running. | ||||
|  * | ||||
|  * It is unspecified whether this timeout has an effect on asynchronous | ||||
|  * transfers. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_generic_handle_set_timeout( | ||||
|     libusbp_generic_handle *, | ||||
|     uint8_t pipe_id, | ||||
|     uint32_t timeout); | ||||
|  | ||||
| /*! Performs a synchronous (blocking) control transfer on endpoint 0. | ||||
|  * | ||||
|  * Under Linux, this blocking transfer unfortunately cannot be interrupted with | ||||
|  * Ctrl+C. | ||||
|  * | ||||
|  * The @a buffer parameter should point to a buffer that is at least @a wLength | ||||
|  * bytes long. | ||||
|  * | ||||
|  * The @a transferred pointer is optional, and is used to return the number of | ||||
|  * bytes that were actually transferred. | ||||
|  * | ||||
|  * The direction of the transfer is determined by the @a bmRequestType parameter. | ||||
|  */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_control_transfer( | ||||
|     libusbp_generic_handle *, | ||||
|     uint8_t bmRequestType, | ||||
|     uint8_t bRequest, | ||||
|     uint16_t wValue, | ||||
|     uint16_t wIndex, | ||||
|     void * buffer, | ||||
|     uint16_t wLength, | ||||
|     size_t * transferred); | ||||
|  | ||||
| /*! Performs a synchronous (blocking) write of data to a bulk or interrupt | ||||
|  * endpoint. | ||||
|  * | ||||
|  * Under Linux, this blocking transfer unfortunately cannot be interrupted with | ||||
|  * Ctrl+C. | ||||
|  * | ||||
|  * The @a pipe_id parameter specifies which endpoint to use.  This argument | ||||
|  * should be bEndpointAddress value from one of the device's IN endpoint | ||||
|  * descriptors.  (Its most significant bit must be 0.) | ||||
|  * | ||||
|  * The @a transferred parameter is an optional pointer to a variable that will | ||||
|  * receive the number of bytes transferred. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_write_pipe( | ||||
|     libusbp_generic_handle *, | ||||
|     uint8_t pipe_id, | ||||
|     const void * buffer, | ||||
|     size_t size, | ||||
|     size_t * transferred); | ||||
|  | ||||
| /*! Performs a synchronous (blocking) read of data from a bulk or interrupt | ||||
|  * endpoint. | ||||
|  * | ||||
|  * It is best to set the buffer size to a multiple of the maximum | ||||
|  * packet size of the endpoint.  Otherwise, this function might return | ||||
|  * an error when the device sends more data than can fit in the | ||||
|  * buffer.  This type of error is called an overflow. | ||||
|  * | ||||
|  * Under Linux, this blocking transfer unfortunately cannot be interrupted with | ||||
|  * Ctrl+C. | ||||
|  * | ||||
|  * The @a pipe_id parameter specifies which endpoint to use.  This argument | ||||
|  * should be bEndpointAddress value from one of the device's IN endpoint | ||||
|  * descriptors.  (Its most significant bit must be 1.) | ||||
|  * | ||||
|  * The @a transferred parameter is an optional pointer to a variable that will | ||||
|  * receive the number of bytes transferred. */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_read_pipe( | ||||
|     libusbp_generic_handle *, | ||||
|     uint8_t pipe_id, | ||||
|     void * buffer, | ||||
|     size_t size, | ||||
|     size_t * transferred); | ||||
|  | ||||
| #ifdef __linux__ | ||||
| /*! Gets the underlying file descriptor of the generic handle.  This function is | ||||
|  * only available on Linux, and is intended for advanced users.  The returned | ||||
|  * file descriptor will remain open and valid as long as the handle is open and | ||||
|  * has not been closed. */ | ||||
| LIBUSBP_API | ||||
| int libusbp_generic_handle_get_fd(libusbp_generic_handle *); | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| /*! Gets the underlying WinUSB handle for the generic handle.  This function is | ||||
|  * only available on Windows, and is intended for advanced users.  The returned | ||||
|  * WinUSB handle will remain open and valid as long as the generic handle is | ||||
|  * open and has not been closed. */ | ||||
| LIBUSBP_API | ||||
| HANDLE libusbp_generic_handle_get_winusb_handle(libusbp_generic_handle *); | ||||
| #endif | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
| /*! Gets the underlying IOCFPlugInInterface object representing the interface. | ||||
|  * You can cast the returned pointer to a `IOCFPlugInInterface **` and then use | ||||
|  * `QueryInterface` to get the corresponding `IOUSBInterfaceInterface **`. | ||||
|  * There is an example of this in generic_handle_test.cpp. */ | ||||
| LIBUSBP_API | ||||
| void ** libusbp_generic_handle_get_cf_plug_in(libusbp_generic_handle *); | ||||
| #endif | ||||
|  | ||||
|  | ||||
| /** libusbp_serial_port ********************************************************/ | ||||
|  | ||||
| /*! Represents a serial port. A null libusbp_serial_port pointer is valid and | ||||
|  * can be passed to any function in this library that takes such pointers. */ | ||||
| typedef struct libusbp_serial_port | ||||
|                libusbp_serial_port; | ||||
|  | ||||
| /*! Creates a serial port object for a specified interface of the | ||||
|  * specified USB device. | ||||
|  * | ||||
|  * On all platforms, if a record of the interface cannot be found, then an error | ||||
|  * is returned with the code LIBUSBP_ERROR_NOT_READY, because this could just be | ||||
|  * a temporary condition that happens right after the device is plugged in. | ||||
|  * | ||||
|  * On macOS, it is assumed that the interface with a @a bInterfaceNumber one | ||||
|  * greater than @a interface_number is the interface that the IOSerialBSDClient | ||||
|  * will attach to.  This should be true if the device implements the USB CDC ACM | ||||
|  * class and has ordered its interfaces so that the control interface is right | ||||
|  * before the data interface. | ||||
|  * | ||||
|  * @param interface_number The lowest @a bInterfaceNumber for the USB interfaces | ||||
|  * that comprise the serial port. | ||||
|  * | ||||
|  * @param composite Should be true if the device is composite, and false | ||||
|  * otherwise. | ||||
|  * | ||||
|  * The returned object must be freed with libusbp_generic_interface_free(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_serial_port_create( | ||||
|     const libusbp_device *, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_serial_port **); | ||||
|  | ||||
| /*! Frees the specified serial port object.  Passing the NULL pointer to | ||||
|  * this function is OK.  Do not free the same non-NULL pointer twice. */ | ||||
| LIBUSBP_API void libusbp_serial_port_free(libusbp_serial_port *); | ||||
|  | ||||
| /*! Makes a copy of the generic interface object.  The copy must be freed with | ||||
|  *  libusbp_generic_interface_free(). */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_serial_port_copy( | ||||
|     const libusbp_serial_port * source, | ||||
|     libusbp_serial_port ** dest); | ||||
|  | ||||
| /*! Gets the user-friendly name of the COM port | ||||
|  * that could be used to open a handle. | ||||
|  * | ||||
|  * On Windows, this will be something like "COM12". | ||||
|  * | ||||
|  * On Linux, it will be something like "/dev/ttyACM0". | ||||
|  * | ||||
|  * On macOS, it will be something like "/dev/cu.usbmodem012345". | ||||
|  * Specifically, it will be a call-out device, not a dial-in device. | ||||
|  * | ||||
|  * You should free the returned string by calling libusbp_string_free(). | ||||
|  */ | ||||
| LIBUSBP_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * libusbp_serial_port_get_name( | ||||
|     const libusbp_serial_port *, | ||||
|     char ** name); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										569
									
								
								dep/libusbp/include/libusbp.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										569
									
								
								dep/libusbp/include/libusbp.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,569 @@ | ||||
| // Copyright (C) Pololu Corporation.  See www.pololu.com for details. | ||||
|  | ||||
| /*! \file libusbp.hpp | ||||
|  * | ||||
|  * This header files provides the C++ API for libusbp.  The classes and | ||||
|  * functions here are just thin wrappers around the C API, so you should see | ||||
|  * libusbp.h for full documentation. */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "libusbp.h" | ||||
| #include <cstddef> | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| /** Display a nice error if C++11 is not enabled (e.g. --std=c++11 or --std=gnu++11). | ||||
|  * The __GXX_EXPERIMENTAL_CXX0X__ check is needed for GCC 4.6, which defines __cplusplus as 1. | ||||
|  * The _MSC_VER check is needed for Visual Studio 2015. */ | ||||
| #if (!defined(__cplusplus) || (__cplusplus < 201103L)) && !defined(__GXX_EXPERIMENTAL_CXX0X__) && !defined(_MSC_VER) | ||||
| #error This header requires features from C++11. | ||||
| #endif | ||||
|  | ||||
| namespace libusbp | ||||
| { | ||||
|     /*! \cond */ | ||||
|     inline void throw_if_needed(libusbp_error * err); | ||||
|     /*! \endcond */ | ||||
|  | ||||
|     /*! Wrapper for libusbp_error_free(). */ | ||||
|     inline void pointer_free(libusbp_error * p) noexcept | ||||
|     { | ||||
|         libusbp_error_free(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_error_copy(). */ | ||||
|     inline libusbp_error * pointer_copy(libusbp_error * p) noexcept | ||||
|     { | ||||
|         return libusbp_error_copy(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_async_in_pipe_close(). */ | ||||
|     inline void pointer_free(libusbp_async_in_pipe * p) noexcept | ||||
|     { | ||||
|         libusbp_async_in_pipe_close(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_device_free(). */ | ||||
|     inline void pointer_free(libusbp_device * p) noexcept | ||||
|     { | ||||
|         libusbp_device_free(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_device_copy(). */ | ||||
|     inline libusbp_device * pointer_copy(libusbp_device * pointer) | ||||
|     { | ||||
|         libusbp_device * copy; | ||||
|         throw_if_needed(libusbp_device_copy(pointer, ©)); | ||||
|         return copy; | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_generic_interface_free(). */ | ||||
|     inline void pointer_free(libusbp_generic_interface * p) noexcept | ||||
|     { | ||||
|         libusbp_generic_interface_free(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_generic_interface_copy(). */ | ||||
|     inline libusbp_generic_interface * pointer_copy(libusbp_generic_interface * pointer) | ||||
|     { | ||||
|         libusbp_generic_interface * copy; | ||||
|         throw_if_needed(libusbp_generic_interface_copy(pointer, ©)); | ||||
|         return copy; | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_generic_handle_free(). */ | ||||
|     inline void pointer_free(libusbp_generic_handle * p) noexcept | ||||
|     { | ||||
|         libusbp_generic_handle_close(p); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_serial_port_copy(). */ | ||||
|     inline libusbp_serial_port * pointer_copy(libusbp_serial_port * pointer) | ||||
|     { | ||||
|         libusbp_serial_port * copy; | ||||
|         throw_if_needed(libusbp_serial_port_copy(pointer, ©)); | ||||
|         return copy; | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_serial_port_free(). */ | ||||
|     inline void pointer_free(libusbp_serial_port * p) noexcept | ||||
|     { | ||||
|         libusbp_serial_port_free(p); | ||||
|     } | ||||
|  | ||||
|     /*! This class is not part of the public API of the library and you should | ||||
|      * not use it directly, but you can use the public methods it provides to | ||||
|      * the classes that inherit from it. | ||||
|      * | ||||
|      * For any type T, if you define pointer_free(T *), then | ||||
|      * unique_pointer_wrapper<T> will be a well-behaved C++ class that provides | ||||
|      * a constructor, implicit conversion to a bool, C++ move operations, | ||||
|      * pointer operations, and forbids C++ copy operations. */ | ||||
|     template<class T> | ||||
|     class unique_pointer_wrapper | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer. */ | ||||
|         explicit unique_pointer_wrapper(T * p = nullptr) noexcept | ||||
|             : pointer(p) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Move constructor. */ | ||||
|         unique_pointer_wrapper(unique_pointer_wrapper && other) noexcept | ||||
|         { | ||||
|             pointer = other.pointer_release(); | ||||
|         } | ||||
|  | ||||
|         /*! Move assignment operator. */ | ||||
|         unique_pointer_wrapper & operator=(unique_pointer_wrapper && other) noexcept | ||||
|         { | ||||
|             pointer_reset(other.pointer_release()); | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
|         /*! Destructor. */ | ||||
|         ~unique_pointer_wrapper() noexcept | ||||
|         { | ||||
|             pointer_reset(); | ||||
|         } | ||||
|  | ||||
|         /*! Implicit conversion to bool.  Returns true if the underlying pointer | ||||
|          *  is not NULL. */ | ||||
|         explicit operator bool() const noexcept | ||||
|         { | ||||
|             return pointer != nullptr; | ||||
|         } | ||||
|  | ||||
|         /*! Returns the underlying pointer. */ | ||||
|         T * pointer_get() const noexcept | ||||
|         { | ||||
|             return pointer; | ||||
|         } | ||||
|  | ||||
|         /*! Sets the underlying pointer to the specified value, freeing the | ||||
|          * previous pointer and taking ownership of the specified one. */ | ||||
|         void pointer_reset(T * p = nullptr) noexcept | ||||
|         { | ||||
|             pointer_free(pointer); | ||||
|             pointer = p; | ||||
|         } | ||||
|  | ||||
|         /*! Releases the pointer, transferring ownership of it to the caller and | ||||
|          * resetting the underlying pointer of this object to nullptr.  The caller | ||||
|          * is responsible for freeing the returned pointer if it is not NULL. */ | ||||
|         T * pointer_release() noexcept | ||||
|         { | ||||
|             T * p = pointer; | ||||
|             pointer = nullptr; | ||||
|             return p; | ||||
|         } | ||||
|  | ||||
|         /*! Returns a pointer to the underlying pointer. */ | ||||
|         T ** pointer_to_pointer_get() noexcept | ||||
|         { | ||||
|             return &pointer; | ||||
|         } | ||||
|  | ||||
|         /*! Copy constructor: forbid. */ | ||||
|         unique_pointer_wrapper(const unique_pointer_wrapper & other) = delete; | ||||
|  | ||||
|         /*! Copy assignment operator: forbid. */ | ||||
|         unique_pointer_wrapper & operator=(const unique_pointer_wrapper & other) = delete; | ||||
|  | ||||
|     protected: | ||||
|         /*! The underlying pointer that is being wrapped.  This pointer will be | ||||
|          * freed when the object is destroyed. */ | ||||
|         T * pointer; | ||||
|     }; | ||||
|  | ||||
|     /*! This class is not part of the public API of the library and you should | ||||
|      * not use it directly, but you can use the public methods it provides to | ||||
|      * the classes that inherit from it. | ||||
|      * | ||||
|      * For any type T, if you define pointer_free(T *) and pointer_copy(T *), then | ||||
|      * unique_pointer_wrapper_with_copy<T> will be a well-behaved C++ class that provides | ||||
|      * a constructor, implicit conversion to a bool, C++ move operations, C++ copy operations, | ||||
|      * and pointer operations. */ | ||||
|     template <class T> | ||||
|     class unique_pointer_wrapper_with_copy : public unique_pointer_wrapper<T> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer. */ | ||||
|         explicit unique_pointer_wrapper_with_copy(T * p = nullptr) noexcept | ||||
|             : unique_pointer_wrapper<T>(p) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Move constructor. */ | ||||
|         unique_pointer_wrapper_with_copy( | ||||
|             unique_pointer_wrapper_with_copy && other) noexcept = default; | ||||
|  | ||||
|         /*! Copy constructor */ | ||||
|         unique_pointer_wrapper_with_copy( | ||||
|             const unique_pointer_wrapper_with_copy & other) | ||||
|             : unique_pointer_wrapper<T>() | ||||
|         { | ||||
|             this->pointer = pointer_copy(other.pointer); | ||||
|         } | ||||
|  | ||||
|         /*! Copy assignment operator. */ | ||||
|         unique_pointer_wrapper_with_copy & operator=( | ||||
|           const unique_pointer_wrapper_with_copy & other) | ||||
|         { | ||||
|             this->pointer_reset(pointer_copy(other.pointer)); | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
|         /*! Move assignment operator. */ | ||||
|         unique_pointer_wrapper_with_copy & operator=( | ||||
|             unique_pointer_wrapper_with_copy && other) = default; | ||||
|     }; | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_error pointer. */ | ||||
|     class error : public unique_pointer_wrapper_with_copy<libusbp_error>, public std::exception | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer.  */ | ||||
|         explicit error(libusbp_error * p = nullptr) noexcept | ||||
|             : unique_pointer_wrapper_with_copy(p) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_error_get_message(). */ | ||||
|         const char * what() const noexcept override { | ||||
|             return libusbp_error_get_message(pointer); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_error_get_message() that returns a | ||||
|          *  std::string. */ | ||||
|         std::string message() const | ||||
|         { | ||||
|             return what(); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_error_has_code(). */ | ||||
|         bool has_code(uint32_t error_code) const noexcept | ||||
|         { | ||||
|             return libusbp_error_has_code(pointer, error_code); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /*! \cond */ | ||||
|     inline void throw_if_needed(libusbp_error * err) | ||||
|     { | ||||
|         if (err != nullptr) | ||||
|         { | ||||
|             throw error(err); | ||||
|         } | ||||
|     } | ||||
|     /*! \endcond */ | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_async_in_pipe pointer. */ | ||||
|     class async_in_pipe : public unique_pointer_wrapper<libusbp_async_in_pipe> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer. */ | ||||
|         explicit async_in_pipe(libusbp_async_in_pipe * pointer = nullptr) | ||||
|             : unique_pointer_wrapper(pointer) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_allocate_transfers(). */ | ||||
|         void allocate_transfers(size_t transfer_count, size_t transfer_size) | ||||
|         { | ||||
|             throw_if_needed(libusbp_async_in_pipe_allocate_transfers( | ||||
|                 pointer, transfer_count, transfer_size)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_start_endless_transfers(). */ | ||||
|         void start_endless_transfers() | ||||
|         { | ||||
|             throw_if_needed(libusbp_async_in_pipe_start_endless_transfers(pointer)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_handle_events(). */ | ||||
|         void handle_events() | ||||
|         { | ||||
|             throw_if_needed(libusbp_async_in_pipe_handle_events(pointer)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_has_pending_transfers(). */ | ||||
|         bool has_pending_transfers() | ||||
|         { | ||||
|             bool result; | ||||
|             throw_if_needed(libusbp_async_in_pipe_has_pending_transfers(pointer, &result)); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_handle_finished_transfer(). */ | ||||
|         bool handle_finished_transfer(void * buffer, size_t * transferred, | ||||
|             error * transfer_error) | ||||
|         { | ||||
|             libusbp_error ** error_out = nullptr; | ||||
|             if (transfer_error != nullptr) | ||||
|             { | ||||
|                 transfer_error->pointer_reset(); | ||||
|                 error_out = transfer_error->pointer_to_pointer_get(); | ||||
|             } | ||||
|  | ||||
|             bool finished; | ||||
|             throw_if_needed(libusbp_async_in_pipe_handle_finished_transfer( | ||||
|                 pointer, &finished, buffer, transferred, error_out)); | ||||
|             return finished; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_async_in_pipe_cancel_transfers(). */ | ||||
|         void cancel_transfers() | ||||
|         { | ||||
|             throw_if_needed(libusbp_async_in_pipe_cancel_transfers(pointer)); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_device pointer. */ | ||||
|     class device : public unique_pointer_wrapper_with_copy<libusbp_device> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer. */ | ||||
|         explicit device(libusbp_device * pointer = nullptr) : | ||||
|             unique_pointer_wrapper_with_copy(pointer) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_device_get_vendor_id(). */ | ||||
|         uint16_t get_vendor_id() const | ||||
|         { | ||||
|             uint16_t id; | ||||
|             throw_if_needed(libusbp_device_get_vendor_id(pointer, &id)); | ||||
|             return id; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_device_get_product_id(). */ | ||||
|         uint16_t get_product_id() const | ||||
|         { | ||||
|             uint16_t id; | ||||
|             throw_if_needed(libusbp_device_get_product_id(pointer, &id)); | ||||
|             return id; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_device_get_revision(). */ | ||||
|         uint16_t get_revision() const | ||||
|         { | ||||
|             uint16_t r; | ||||
|             throw_if_needed(libusbp_device_get_revision(pointer, &r)); | ||||
|             return r; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_device_get_serial_number(). */ | ||||
|         std::string get_serial_number() const | ||||
|         { | ||||
|             char * str; | ||||
|             throw_if_needed(libusbp_device_get_serial_number(pointer, &str)); | ||||
|             std::string serial_number = str; | ||||
|             libusbp_string_free(str); | ||||
|             return serial_number; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_device_get_os_id(). */ | ||||
|         std::string get_os_id() const | ||||
|         { | ||||
|             char * str; | ||||
|             throw_if_needed(libusbp_device_get_os_id(pointer, &str)); | ||||
|             std::string serial_number = str; | ||||
|             libusbp_string_free(str); | ||||
|             return serial_number; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /*! Wrapper for libusbp_list_connected_devices(). */ | ||||
|     inline std::vector<libusbp::device> list_connected_devices() | ||||
|     { | ||||
|         libusbp_device ** device_list; | ||||
|         size_t size; | ||||
|         throw_if_needed(libusbp_list_connected_devices(&device_list, &size)); | ||||
|         std::vector<device> vector; | ||||
|         for(size_t i = 0; i < size; i++) | ||||
|         { | ||||
|             vector.emplace_back(device_list[i]); | ||||
|         } | ||||
|         libusbp_list_free(device_list); | ||||
|         return vector; | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for libusbp_find_device_with_vid_pid(). */ | ||||
|     inline libusbp::device find_device_with_vid_pid(uint16_t vendor_id, uint16_t product_id) | ||||
|     { | ||||
|         libusbp_device * device_pointer; | ||||
|         throw_if_needed(libusbp_find_device_with_vid_pid( | ||||
|                 vendor_id, product_id, &device_pointer)); | ||||
|         return device(device_pointer); | ||||
|     } | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_generic_interface pointer. */ | ||||
|     class generic_interface : public unique_pointer_wrapper_with_copy<libusbp_generic_interface> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer.  This object will free the pointer | ||||
|          *  when it is destroyed. */ | ||||
|         explicit generic_interface(libusbp_generic_interface * pointer = nullptr) | ||||
|             : unique_pointer_wrapper_with_copy(pointer) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_interface_create. */ | ||||
|         explicit generic_interface(const device & device, | ||||
|             uint8_t interface_number = 0, bool composite = false) | ||||
|         { | ||||
|             throw_if_needed(libusbp_generic_interface_create( | ||||
|                     device.pointer_get(), interface_number, composite, &pointer)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_interface_get_os_id(). */ | ||||
|         std::string get_os_id() const | ||||
|         { | ||||
|             char * str; | ||||
|             throw_if_needed(libusbp_generic_interface_get_os_id(pointer, &str)); | ||||
|             std::string id = str; | ||||
|             libusbp_string_free(str); | ||||
|             return id; | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_interface_get_os_filename(). */ | ||||
|         std::string get_os_filename() const | ||||
|         { | ||||
|             char * str; | ||||
|             throw_if_needed(libusbp_generic_interface_get_os_filename(pointer, &str)); | ||||
|             std::string filename = str; | ||||
|             libusbp_string_free(str); | ||||
|             return filename; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_generic_handle pointer. */ | ||||
|     class generic_handle : public unique_pointer_wrapper<libusbp_generic_handle> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer.  This object will free the pointer | ||||
|          *  when it is destroyed. */ | ||||
|         explicit generic_handle(libusbp_generic_handle * pointer = nullptr) noexcept | ||||
|             : unique_pointer_wrapper(pointer) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_handle_open(). */ | ||||
|         explicit generic_handle(const generic_interface & gi) | ||||
|         { | ||||
|             throw_if_needed(libusbp_generic_handle_open(gi.pointer_get(), &pointer)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_handle_close(). */ | ||||
|         void close() noexcept | ||||
|         { | ||||
|             pointer_reset(); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_handle_open_async_in_pipe(). */ | ||||
|         async_in_pipe open_async_in_pipe(uint8_t pipe_id) | ||||
|         { | ||||
|             libusbp_async_in_pipe * pipe; | ||||
|             throw_if_needed(libusbp_generic_handle_open_async_in_pipe( | ||||
|                 pointer, pipe_id, &pipe)); | ||||
|             return async_in_pipe(pipe); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_generic_handle_set_timeout(). */ | ||||
|         void set_timeout(uint8_t pipe_id, uint32_t timeout) | ||||
|         { | ||||
|             throw_if_needed(libusbp_generic_handle_set_timeout(pointer, pipe_id, timeout)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_control_transfer(). */ | ||||
|         void control_transfer( | ||||
|             uint8_t bmRequestType, | ||||
|             uint8_t bRequest, | ||||
|             uint16_t wValue, | ||||
|             uint16_t wIndex, | ||||
|             void * buffer = nullptr, | ||||
|             uint16_t wLength = 0, | ||||
|             size_t * transferred = nullptr) | ||||
|         { | ||||
|             throw_if_needed(libusbp_control_transfer(pointer, | ||||
|                 bmRequestType, bRequest, wValue, wIndex, | ||||
|                 buffer, wLength, transferred)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_write_pipe(). */ | ||||
|         void write_pipe(uint8_t pipe_id, const void * buffer, | ||||
|             size_t size, size_t * transferred) | ||||
|         { | ||||
|             throw_if_needed(libusbp_write_pipe(pointer, | ||||
|                 pipe_id, buffer, size, transferred)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_read_pipe(). */ | ||||
|         void read_pipe(uint8_t pipe_id, void * buffer, | ||||
|             size_t size, size_t * transferred) | ||||
|         { | ||||
|             throw_if_needed(libusbp_read_pipe(pointer, | ||||
|                 pipe_id, buffer, size, transferred)); | ||||
|         } | ||||
|  | ||||
|         #ifdef _WIN32 | ||||
|         /*! Wrapper for libusbp_generic_handle_get_winusb_handle(). */ | ||||
|         HANDLE get_winusb_handle() | ||||
|         { | ||||
|             return libusbp_generic_handle_get_winusb_handle(pointer); | ||||
|         } | ||||
|         #endif | ||||
|  | ||||
|         #ifdef __linux__ | ||||
|         /*! Wrapper for libusbp_generic_handle_get_fd(). */ | ||||
|         int get_fd() | ||||
|         { | ||||
|             return libusbp_generic_handle_get_fd(pointer); | ||||
|         } | ||||
|         #endif | ||||
|  | ||||
|         #ifdef __APPLE__ | ||||
|         /*! Wrapper for libusbp_generic_handle_get_cf_plug_in(). */ | ||||
|         void ** get_cf_plug_in() | ||||
|         { | ||||
|             return libusbp_generic_handle_get_cf_plug_in(pointer); | ||||
|         } | ||||
|         #endif | ||||
|     }; | ||||
|  | ||||
|     /*! Wrapper for a ::libusbp_serial_port pointer. */ | ||||
|     class serial_port : public unique_pointer_wrapper_with_copy<libusbp_serial_port> | ||||
|     { | ||||
|     public: | ||||
|         /*! Constructor that takes a pointer.  This object will free the pointer | ||||
|          *  when it is destroyed. */ | ||||
|         explicit serial_port(libusbp_serial_port * pointer = nullptr) | ||||
|             : unique_pointer_wrapper_with_copy(pointer) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_serial_port_create(). */ | ||||
|         explicit serial_port(const device & device, | ||||
|             uint8_t interface_number = 0, bool composite = false) | ||||
|         { | ||||
|             throw_if_needed(libusbp_serial_port_create( | ||||
|                     device.pointer_get(), interface_number, composite, &pointer)); | ||||
|         } | ||||
|  | ||||
|         /*! Wrapper for libusbp_serial_port_get_name(). */ | ||||
|         std::string get_name() const | ||||
|         { | ||||
|             char * str; | ||||
|             throw_if_needed(libusbp_serial_port_get_name(pointer, &str)); | ||||
|             std::string id = str; | ||||
|             libusbp_string_free(str); | ||||
|             return id; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								dep/libusbp/include/libusbp_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								dep/libusbp/include/libusbp_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| #pragma once | ||||
|  | ||||
| #define BUILD_SYSTEM_LIBUSBP_VERSION_MAJOR 1 | ||||
| #define LIBUSBP_STATIC | ||||
| #undef LIBUSBP_LOG | ||||
| #undef VBOX_LINUX_ON_WINDOWS | ||||
| #undef USE_TEST_DEVICE_A | ||||
| #undef USE_TEST_DEVICE_B | ||||
							
								
								
									
										15
									
								
								dep/libusbp/install_helper/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								dep/libusbp/install_helper/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| use_c99() | ||||
|  | ||||
| add_library (install_helper SHARED install_helper_windows.c dll.def) | ||||
|  | ||||
| target_link_libraries (install_helper setupapi msi) | ||||
|  | ||||
| set_target_properties (install_helper PROPERTIES | ||||
|   OUTPUT_NAME usbp-install-helper-${LIBUSBP_VERSION_MAJOR} | ||||
|   LINK_FLAGS "-Wl,--enable-stdcall-fixup -static" | ||||
| ) | ||||
|  | ||||
| install (TARGETS install_helper | ||||
|   RUNTIME DESTINATION bin | ||||
|   LIBRARY DESTINATION lib | ||||
|   ARCHIVE DESTINATION lib) | ||||
							
								
								
									
										3
									
								
								dep/libusbp/install_helper/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								dep/libusbp/install_helper/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| The files in this directory compile a separate, statically linked DLL named | ||||
| `libusbp-install-helper-<x>.dll` that can be useful in installers of USB | ||||
| software. | ||||
							
								
								
									
										5
									
								
								dep/libusbp/install_helper/dll.def
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dep/libusbp/install_helper/dll.def
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| EXPORTS | ||||
|     libusbp_install_inf | ||||
|     libusbp_install_infW | ||||
|     libusbp_broadcast_setting_change | ||||
|     libusbp_broadcast_setting_changeW | ||||
							
								
								
									
										167
									
								
								dep/libusbp/install_helper/install_helper_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								dep/libusbp/install_helper/install_helper_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| /* This file contains special functions that can help install an application | ||||
|  * that uses USB.  The functions can be used from an MSI custom action or from | ||||
|  * rundll32. */ | ||||
|  | ||||
| #include <windows.h> | ||||
| #include <devpropdef.h> | ||||
| #include <msiquery.h> | ||||
| #include <setupapi.h> | ||||
| #include <strsafe.h> | ||||
|  | ||||
| #define LIBUSBP_UNUSED(param_name) (void)param_name; | ||||
|  | ||||
| typedef struct install_context | ||||
| { | ||||
|     HWND owner; | ||||
|     MSIHANDLE install; | ||||
| } install_context; | ||||
|  | ||||
| static void log_message(install_context * context, LPCWSTR message) | ||||
| { | ||||
| 	if (context->install == 0) | ||||
|     { | ||||
|         // The MSI handle is not available so just ignore log messages. | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| 		// Report the log message through MSI, which will put it in the log | ||||
| 		// file. | ||||
|         MSIHANDLE record = MsiCreateRecord(1); | ||||
|         MsiRecordSetStringW(record, 0, message); | ||||
|         MsiProcessMessage(context->install, INSTALLMESSAGE_INFO, record); | ||||
|         MsiCloseHandle(record); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Adds an error message to the Windows Installer log file and displays it | ||||
| // to the user in a dialog box with an OK button. | ||||
| static void error_message(install_context * context, LPCWSTR message) | ||||
| { | ||||
| 	if (context->install == 0) | ||||
| 	{ | ||||
| 		// The MSIHANDLE is not available, so just display a dialog box. | ||||
| 		MessageBoxW(context->owner, message, L"Installation Error", MB_ICONERROR); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// Report the error through MSI, which will in turn display the dialog | ||||
| 		// box. | ||||
| 	    MSIHANDLE record = MsiCreateRecord(1); | ||||
| 	    MsiRecordSetStringW(record, 0, message); | ||||
| 	    MsiProcessMessage(context->install, INSTALLMESSAGE_ERROR, record); | ||||
|         MsiCloseHandle(record); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* You might need to call this function after modifying the PATH in order to | ||||
|  * notify other programs about the change.  This allows a newly launched Command | ||||
|  * Prompt to see that the PATH has changed and start using it. */ | ||||
| static void broadcast_setting_change_core(install_context * context) | ||||
| { | ||||
| 	DWORD_PTR result2 = 0; | ||||
| 	LRESULT result = SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, | ||||
|         (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, &result2); | ||||
| 	if (result == 0) | ||||
| 	{ | ||||
| 		WCHAR message[1024]; | ||||
| 		StringCbPrintfW(message, sizeof(message), | ||||
|             L"SendMessageTimeout failed: Error code 0x%lx.  Result %d", | ||||
|             GetLastError(), result2); | ||||
| 		error_message(context, message); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Usage: rundll32 libusbp*.dll libusbp_broadcast_setting_change | ||||
| void __stdcall libusbp_broadcast_setting_changeW( | ||||
|     HWND owner, HINSTANCE hinst, LPWSTR args, int n) | ||||
| { | ||||
|     LIBUSBP_UNUSED(hinst); | ||||
|     LIBUSBP_UNUSED(args); | ||||
|     LIBUSBP_UNUSED(n); | ||||
|     install_context context = {0}; | ||||
|     context.owner = owner; | ||||
| 	broadcast_setting_change_core(&context); | ||||
| } | ||||
|  | ||||
| // Usage: make a Custom Action in an MSI with this function as the entry point. | ||||
| UINT __stdcall libusbp_broadcast_setting_change(MSIHANDLE install) | ||||
| { | ||||
|     install_context context = {0}; | ||||
|     context.install = install; | ||||
|     log_message(&context, L"libusbp_broadcast_setting_change: Begin."); | ||||
| 	broadcast_setting_change_core(&context); | ||||
|     log_message(&context, L"libusbp_broadcast_setting_change: End."); | ||||
| 	return 0;  // Always return success. | ||||
| } | ||||
|  | ||||
| /* Calls SetupCopyOEMInf to install the specified INF file.  The user may be | ||||
|  * prompted to accept the driver software, and if everything works then the file | ||||
|  * will be copied to the C:\Windows\inf directory. */ | ||||
| static void install_inf_core(install_context * context, LPWSTR filename) | ||||
| { | ||||
| 	BOOL success = SetupCopyOEMInfW(filename, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL); | ||||
|  | ||||
| 	if (!success) | ||||
| 	{ | ||||
| 		WCHAR message[1024]; | ||||
|  | ||||
| 		// NOTE: newlines do not show up in the MSI log, but they do show up in | ||||
| 		// the MSI error dialog box. | ||||
|  | ||||
| 		StringCbPrintfW(message, sizeof(message), | ||||
|             L"There was an error installing the driver file %s.  \n" | ||||
| 			L"You might have to manually install this file by right-clicking it " | ||||
|             L"and selecting \"Install\".  \n" | ||||
| 			L"Error code 0x%lx.", filename, GetLastError()); | ||||
| 		error_message(context, message); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Usage: rundll32 libusbp*.dll libusbp_install_inf path | ||||
| void __stdcall libusbp_install_infW(HWND owner, HINSTANCE hinst, LPWSTR args, int n) | ||||
| { | ||||
|     LIBUSBP_UNUSED(hinst); | ||||
|     LIBUSBP_UNUSED(args); | ||||
|     LIBUSBP_UNUSED(n); | ||||
|     install_context context = {0}; | ||||
|     context.owner = owner; | ||||
|     install_inf_core(&context, args); | ||||
| } | ||||
|  | ||||
| // Usage: make a Custom Action in your installer with a "CustomActionData" | ||||
| // property set equal to the full path of the inf file. | ||||
| UINT __stdcall libusbp_install_inf(MSIHANDLE install) | ||||
| { | ||||
|     install_context context = {0}; | ||||
|     context.install = install; | ||||
| 	broadcast_setting_change_core(&context); | ||||
|  | ||||
| 	log_message(&context, L"libusbp_install_inf: Begin."); | ||||
|  | ||||
| 	WCHAR message[1024]; | ||||
|  | ||||
| 	// Get the name of inf file. | ||||
| 	WCHAR filename[1024]; | ||||
| 	DWORD length = 1024; | ||||
| 	UINT result = MsiGetPropertyW(install, L"CustomActionData", filename, &length); | ||||
| 	if (result != ERROR_SUCCESS) | ||||
| 	{ | ||||
| 		StringCbPrintfW(message, sizeof(message), | ||||
|             L"libusbp_install_inf: Unable to get filename parameter.  Error code %d.", result); | ||||
| 		error_message(&context, message); | ||||
| 		return 0;  // Return success anyway. | ||||
| 	} | ||||
|  | ||||
| 	StringCbPrintfW(message, sizeof(message), L"libusbp_install_inf: filename=%s", filename); | ||||
| 	log_message(&context, message); | ||||
|  | ||||
| 	install_inf_core(&context, filename); | ||||
|  | ||||
| 	StringCbPrintfW(message, sizeof(message), L"libusbp_install_inf: End. result2=%d", result); | ||||
| 	log_message(&context, message); | ||||
|  | ||||
|     // Always return 0 even if there was an error, because we don't want to roll | ||||
|     // back the rest of the installation just because this part fails.  The user | ||||
|     // can either manually install the INF files or try again. | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										4
									
								
								dep/libusbp/manual_tests/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								dep/libusbp/manual_tests/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| add_subdirectory(test_async_in) | ||||
| add_subdirectory(test_long_read) | ||||
| add_subdirectory(test_long_write) | ||||
| add_subdirectory(test_transitions) | ||||
							
								
								
									
										9
									
								
								dep/libusbp/manual_tests/test_async_in/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/manual_tests/test_async_in/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(test_async_in test_async_in.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(test_async_in usbp) | ||||
							
								
								
									
										107
									
								
								dep/libusbp/manual_tests/test_async_in/test_async_in.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								dep/libusbp/manual_tests/test_async_in/test_async_in.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /* Tests that libusbp is capable of reliably reading data from an IN endpoint on | ||||
|  * every frame using asynchronous transfers.  It prints to the standard output | ||||
|  * as it successfully receives data, and prints to the standard error if there | ||||
|  * was a gap in the data (a USB frame where the device did not generate a new | ||||
|  * packet for the host). | ||||
|  * | ||||
|  * You can also check the CPU usage while running this function to make | ||||
|  * sure libusbp is not doing anything too inefficient. | ||||
|  */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <stdio.h> | ||||
| #include <iostream> | ||||
| #ifdef _MSC_VER | ||||
| #define usleep(x) Sleep(((x) + 999) / 1000) | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | ||||
| const uint16_t vendor_id = 0x1FFB; | ||||
| const uint16_t product_id = 0xDA01; | ||||
| const uint8_t interface_number = 0; | ||||
| const bool composite = true; | ||||
| const uint8_t endpoint_address = 0x82; | ||||
| const size_t packet_size = 5; | ||||
| const size_t transfer_size = packet_size; | ||||
| const size_t transfer_count = 250; | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id); | ||||
|     if (!device) | ||||
|     { | ||||
|         std::cerr << "Device not found." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     libusbp::generic_interface gi(device, interface_number, composite); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     libusbp::async_in_pipe pipe = handle.open_async_in_pipe(endpoint_address); | ||||
|     pipe.allocate_transfers(transfer_count, transfer_size); | ||||
|  | ||||
|     pipe.start_endless_transfers(); | ||||
|  | ||||
|     uint8_t last_f = 0; | ||||
|  | ||||
|     uint32_t finish_count = 0; | ||||
|     while(true) | ||||
|     { | ||||
|         uint8_t buffer[transfer_size]; | ||||
|         size_t transferred; | ||||
|         libusbp::error transfer_error; | ||||
|         while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error)) | ||||
|         { | ||||
|             if (transfer_error) | ||||
|             { | ||||
|                 fprintf(stderr, "Transfer error.\n"); | ||||
|                 throw transfer_error; | ||||
|             } | ||||
|  | ||||
| 	    if (transferred != transfer_size) | ||||
| 	    { | ||||
| 	        fprintf(stderr, "Got %d bytes instead of %d.\n", | ||||
| 			(int)transferred, (int)transfer_size); | ||||
| 	    } | ||||
|  | ||||
| 	    uint8_t f = buffer[0]; | ||||
| 	    if (f != (uint8_t)(last_f + transfer_size/packet_size)) | ||||
| 	    { | ||||
|                // If this happens, it indicates there was a USB frame where the | ||||
|                // device did not generate a new packet for the host, which is | ||||
|                // bad.  However, you should expect to see a few of these at the | ||||
|                // very beginning of the test because there will be some old | ||||
|                // packets queued up in the device from earlier, and because | ||||
|                // last_f always starts at 0. | ||||
|                fprintf(stderr, "Frame number gap: %d to %d\n", last_f, f); | ||||
| 	    } | ||||
| 	    last_f = f; | ||||
|  | ||||
|             if ((++finish_count % 4096) == 0) | ||||
|             { | ||||
|                 printf("Another 4096 transfers done.\n"); | ||||
|                 fflush(stdout); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pipe.handle_events(); | ||||
|         usleep(20000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										9
									
								
								dep/libusbp/manual_tests/test_long_read/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/manual_tests/test_long_read/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(test_long_read test_long_read.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(test_long_read usbp) | ||||
							
								
								
									
										90
									
								
								dep/libusbp/manual_tests/test_long_read/test_long_read.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								dep/libusbp/manual_tests/test_long_read/test_long_read.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| /* Tests what happens if we do a synchronous read from an IN endpoint that | ||||
|  * takes a long time to complete.  This can be used to check that pressing | ||||
|  * Ctrl+C is able to interrupt the read. */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <stdio.h> | ||||
| #include <iostream> | ||||
|  | ||||
| const uint16_t vendor_id = 0x1FFB; | ||||
| const uint16_t product_id = 0xDA01; | ||||
| const uint8_t interface_number = 0; | ||||
| const bool composite = true; | ||||
| const uint8_t endpoint_address = 0x82; | ||||
| const size_t packet_size = 5; | ||||
| const size_t transfer_size = packet_size * 10000; | ||||
|  | ||||
| void long_read(libusbp::generic_handle & handle) | ||||
| { | ||||
|     uint8_t buffer[transfer_size]; | ||||
|     size_t transferred; | ||||
|  | ||||
|     printf("Reading %d bytes...\n", (unsigned int)transfer_size); | ||||
|     fflush(stdout); | ||||
|     handle.read_pipe(endpoint_address, buffer, sizeof(buffer), &transferred); | ||||
|     if (transferred == transfer_size) | ||||
|     { | ||||
|         printf("Transfer successful.\n"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         printf("Transferred only %d bytes out of %d.\n", | ||||
|             (unsigned int)transferred, (unsigned int)transfer_size); | ||||
|     } | ||||
|     fflush(stdout); | ||||
| } | ||||
|  | ||||
| void long_control_read(libusbp::generic_handle & handle) | ||||
| { | ||||
|     uint8_t buffer[5]; | ||||
|     size_t transferred; | ||||
|  | ||||
|     printf("Performing a slow control read...\n"); | ||||
|     fflush(stdout); | ||||
|     handle.control_transfer(0xC0, 0x91, 10000, 5, buffer, sizeof(buffer), &transferred); | ||||
|     if (transferred == 5) | ||||
|     { | ||||
|         printf("Control read successful.\n"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         printf("Transferred only %d bytes out of %d.\n", | ||||
|             (unsigned int)transferred, (unsigned int)sizeof(buffer)); | ||||
|     } | ||||
|     fflush(stdout); | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     libusbp::device device = libusbp::find_device_with_vid_pid(vendor_id, product_id); | ||||
|     if (!device) | ||||
|     { | ||||
|         std::cerr << "Device not found." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     libusbp::generic_interface gi(device, interface_number, composite); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|  | ||||
|     long_read(handle); | ||||
|     long_control_read(handle); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										9
									
								
								dep/libusbp/manual_tests/test_long_write/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/manual_tests/test_long_write/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(test_long_write test_long_write.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(test_long_write usbp) | ||||
							
								
								
									
										70
									
								
								dep/libusbp/manual_tests/test_long_write/test_long_write.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								dep/libusbp/manual_tests/test_long_write/test_long_write.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* Tests what happens if we do a synchronous write that takes a long time to | ||||
|  * complete.  This can be used to check that pressing Ctrl+C is able to | ||||
|  * interrupt the write. */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <stdio.h> | ||||
| #include <iostream> | ||||
|  | ||||
| const uint16_t vendor_id = 0x1FFB; | ||||
| const uint16_t product_id = 0xDA01; | ||||
| const uint8_t interface_number = 0; | ||||
| const bool composite = true; | ||||
| const uint8_t endpoint_address = 0x03; | ||||
| const size_t packet_size = 32; | ||||
| const size_t transfer_size = packet_size * 4; | ||||
|  | ||||
| void long_write(libusbp::generic_handle & handle) | ||||
| { | ||||
|     // First packet causes a delay of 4 seconds (0x0FA0 ms) | ||||
|     uint8_t buffer[transfer_size] = { 0xDE, 0xA0, 0x0F }; | ||||
|     size_t transferred; | ||||
|  | ||||
|     printf("Writing...\n"); | ||||
|     fflush(stdout); | ||||
|     handle.write_pipe(endpoint_address, buffer, sizeof(buffer), &transferred); | ||||
|     if (transferred == transfer_size) | ||||
|     { | ||||
|         printf("Transfer successful.\n"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         printf("Transferred only %d bytes out of %d.\n", | ||||
|             (unsigned int)transferred, (unsigned int)transfer_size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     libusbp::device device = libusbp::find_device_with_vid_pid( | ||||
|         vendor_id, product_id); | ||||
|     if (!device) | ||||
|     { | ||||
|         std::cerr << "Device not found." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     libusbp::generic_interface gi(device, interface_number, composite); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|  | ||||
|     long_write(handle); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										9
									
								
								dep/libusbp/manual_tests/test_transitions/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								dep/libusbp/manual_tests/test_transitions/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| use_cxx11() | ||||
|  | ||||
| add_executable(test_transitions test_transitions.cpp) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(test_transitions usbp) | ||||
| @@ -0,0 +1,98 @@ | ||||
| /* This program helps us test the transitions that a USB device goes through as | ||||
|  * it gets connected or disconnected from a computer.  It helps us identify | ||||
|  * errors that might occur so we can assign code to them such as | ||||
|  * LIBUSBP_ERROR_NOT_READY. */ | ||||
|  | ||||
| #include <libusbp.hpp> | ||||
| #include <iostream> | ||||
| #include <iomanip> | ||||
| #include <chrono> | ||||
| #ifdef _MSC_VER | ||||
| #define usleep(x) Sleep(((x) + 999) / 1000) | ||||
| #else | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | ||||
| #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 6 | ||||
| typedef std::chrono::monotonic_clock clock_type; | ||||
| #else | ||||
| typedef std::chrono::steady_clock clock_type; | ||||
| #endif | ||||
|  | ||||
| std::ostream & log() | ||||
| { | ||||
|     return std::cout | ||||
|         << clock_type::now().time_since_epoch().count() | ||||
|         << ": "; | ||||
| } | ||||
|  | ||||
| void check_test_device_a(libusbp::device device) | ||||
| { | ||||
|     static std::string current_status; | ||||
|     std::string status; | ||||
|  | ||||
|     if (device) | ||||
|     { | ||||
|         status = "Found " + device.get_serial_number() + "."; | ||||
|  | ||||
|         // Try to connect to the generic interface. | ||||
|         try | ||||
|         { | ||||
|             libusbp::generic_interface gi(device, 0, true); | ||||
|             libusbp::generic_handle handle(gi); | ||||
|             status += "  Interface 0 works."; | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             status += "  Interface 0 error: " + error.message(); | ||||
|             if (!error.has_code(LIBUSBP_ERROR_NOT_READY)) | ||||
|             { | ||||
|                 status += "  Lacks code LIBUSBP_ERROR_NOT_READY!"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         status = "Not found."; | ||||
|     } | ||||
|  | ||||
|     if (current_status != status) | ||||
|     { | ||||
|         log() << "Test device A: " << status << std::endl; | ||||
|         current_status = status; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main_with_exceptions() | ||||
| { | ||||
|     std::cout | ||||
|         << "Clock tick period: " | ||||
|         << clock_type::period::num | ||||
|         << "/" | ||||
|         << clock_type::period::den | ||||
|         << " seconds" << std::endl; | ||||
|  | ||||
|     while(1) | ||||
|     { | ||||
|         check_test_device_a(libusbp::find_device_with_vid_pid(0x1FFB, 0xDA01)); | ||||
|         usleep(10); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     // Suppress unused parameter warnings. | ||||
|     (void)argc; | ||||
|     (void)argv; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         return main_with_exceptions(); | ||||
|     } | ||||
|     catch(const std::exception & error) | ||||
|     { | ||||
|         std::cerr << "Error: " << error.what() << std::endl; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										116
									
								
								dep/libusbp/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								dep/libusbp/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| use_c99() | ||||
|  | ||||
| # Settings for GCC | ||||
| if (CMAKE_C_COMPILER_ID STREQUAL "GNU") | ||||
|   # By default, symbols are not visible outside of the library. | ||||
|   set (CMAKE_C_FLAGS "-fvisibility=hidden ${CMAKE_C_FLAGS}") | ||||
|  | ||||
|   # Avoid tons of errors from strsafe.h. | ||||
|   set (CMAKE_C_FLAGS "-fgnu89-inline ${CMAKE_C_FLAGS}") | ||||
| endif () | ||||
|  | ||||
| # Define cross-platform source files. | ||||
| set (sources | ||||
|   async_in_pipe.c | ||||
|   error.c | ||||
|   error_hresult.c | ||||
|   find_device.c | ||||
|   list.c | ||||
|   pipe_id.c | ||||
|   string.c) | ||||
|  | ||||
| # Define operating system-specific source files. | ||||
| if (WIN32) | ||||
|   set (sources ${sources} | ||||
|     windows/error_windows.c | ||||
|     windows/device_windows.c | ||||
|     windows/interface_windows.c | ||||
|     windows/device_instance_id_windows.c | ||||
|     windows/generic_interface_windows.c | ||||
|     windows/list_windows.c | ||||
|     windows/generic_handle_windows.c | ||||
|     windows/async_in_transfer_windows.c | ||||
|     windows/serial_port_windows.c | ||||
|     ${CMAKE_CURRENT_BINARY_DIR}/info.rc) | ||||
| elseif (LINUX) | ||||
|   set (sources ${sources} | ||||
|     linux/list_linux.c | ||||
|     linux/device_linux.c | ||||
|     linux/generic_interface_linux.c | ||||
|     linux/generic_handle_linux.c | ||||
|     linux/error_linux.c | ||||
|     linux/udev_linux.c | ||||
|     linux/usbfd_linux.c | ||||
|     linux/async_in_transfer_linux.c | ||||
|     linux/serial_port_linux.c) | ||||
| elseif (APPLE) | ||||
|   set (sources ${sources} | ||||
|     mac/list_mac.c | ||||
|     mac/device_mac.c | ||||
|     mac/error_mac.c | ||||
|     mac/generic_interface_mac.c | ||||
|     mac/generic_handle_mac.c | ||||
|     mac/async_in_transfer_mac.c | ||||
|     mac/serial_port_mac.c | ||||
|     mac/iokit_mac.c) | ||||
| endif () | ||||
|  | ||||
| add_library (usbp ${sources}) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
|   "${CMAKE_CURRENT_SOURCE_DIR}" | ||||
|   "${CMAKE_CURRENT_BINARY_DIR}" | ||||
| ) | ||||
|  | ||||
| if (WIN32) | ||||
|   target_link_libraries (usbp setupapi winusb uuid ole32) | ||||
|   if (NOT BUILD_SHARED_LIBS) | ||||
|     set (PC_MORE_LIBS "-lsetupapi -lwinusb -luuid -lole32") | ||||
|   endif () | ||||
| elseif (LINUX) | ||||
|   pkg_check_modules(LIBUDEV REQUIRED libudev) | ||||
|   string (REPLACE ";" " " LIBUDEV_CFLAGS "${LIBUDEV_CFLAGS}") | ||||
|   set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUDEV_CFLAGS}") | ||||
|   target_link_libraries (usbp udev) | ||||
|   if (NOT BUILD_SHARED_LIBS) | ||||
|     set (PC_REQUIRES "libudev") | ||||
|   endif () | ||||
| elseif (APPLE) | ||||
|   set (link_flags "-framework IOKit -framework CoreFoundation ${link_flags}") | ||||
|   if (NOT BUILD_SHARED_LIBS) | ||||
|     set (PC_MORE_LIBS "${link_flags}") | ||||
|   endif () | ||||
| endif () | ||||
|  | ||||
| set_target_properties(usbp PROPERTIES | ||||
|   OUTPUT_NAME usbp-${LIBUSBP_VERSION_MAJOR} | ||||
|   SOVERSION ${LIBUSBP_VERSION} | ||||
|   VERSION ${LIBUSBP_VERSION} | ||||
|   DEFINE_SYMBOL LIBUSBP_EXPORTS | ||||
|   LINK_FLAGS "${link_flags}" | ||||
| ) | ||||
|  | ||||
| configure_file ( | ||||
|   "libusbp_config.h.in" | ||||
|   "libusbp_config.h" | ||||
| ) | ||||
|  | ||||
| configure_file ( | ||||
|   "info.rc.in" | ||||
|   "info.rc" | ||||
| ) | ||||
|  | ||||
| configure_file ( | ||||
|   "libusbp.pc.in" | ||||
|   "libusbp-${LIBUSBP_VERSION_MAJOR}.pc" | ||||
|   @ONLY | ||||
| ) | ||||
|  | ||||
| install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libusbp-${LIBUSBP_VERSION_MAJOR}.pc" | ||||
|   DESTINATION lib/pkgconfig) | ||||
|  | ||||
| install(TARGETS usbp | ||||
|   RUNTIME DESTINATION bin | ||||
|   LIBRARY DESTINATION lib | ||||
|   ARCHIVE DESTINATION lib) | ||||
							
								
								
									
										343
									
								
								dep/libusbp/src/async_in_pipe.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								dep/libusbp/src/async_in_pipe.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_async_in_pipe | ||||
| { | ||||
|     libusbp_generic_handle * handle; | ||||
|     uint8_t pipe_id; | ||||
|     async_in_transfer ** transfer_array; | ||||
|     size_t transfer_size; | ||||
|     size_t transfer_count; | ||||
|  | ||||
|     bool endless_transfers_enabled; | ||||
|  | ||||
|     // The number of transfers that are pending, meaning that they were | ||||
|     // submitted (and possibly completed by the kernel) but not finished (handed | ||||
|     // to the user of the pipe) yet.  This variable allows us to distinguish | ||||
|     // between the state where no transfers are pending and the state where all | ||||
|     // of them are pending.  Note that this definition of pending is different | ||||
|     // than the definition of pending in the async_in_transfer struct. | ||||
|     size_t pending_count; | ||||
|  | ||||
|     // The index of the transfer that should finish next.  That transfer will be | ||||
|     // pending (and thus able to be checked) if pending_count > 0. | ||||
|     size_t next_finish; | ||||
|  | ||||
|     // The index of the transfer that will be submitted next when we need to | ||||
|     // submit more transfers.  That transfer can be submitted if pending_count < | ||||
|     // transfer_count. | ||||
|     size_t next_submit; | ||||
| }; | ||||
|  | ||||
| static inline size_t increment_and_wrap_size(size_t n, size_t bound) | ||||
| { | ||||
|     n++; | ||||
|     return n >= bound ? 0 : n; | ||||
| } | ||||
|  | ||||
| static void async_in_transfer_array_free(async_in_transfer ** array, size_t transfer_count) | ||||
| { | ||||
|     if (array == NULL) { return; } | ||||
|  | ||||
|     for (size_t i = 0; i < transfer_count; i++) | ||||
|     { | ||||
|         async_in_transfer_free(array[i]); | ||||
|     } | ||||
|     free(array); | ||||
| } | ||||
|  | ||||
| void libusbp_async_in_pipe_close(libusbp_async_in_pipe * pipe) | ||||
| { | ||||
|     if (pipe != NULL) | ||||
|     { | ||||
|         async_in_transfer_array_free(pipe->transfer_array, pipe->transfer_count); | ||||
|         free(pipe); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_pipe_create(libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, libusbp_async_in_pipe ** pipe) | ||||
| { | ||||
|     // Check the pipe output pointer. | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *pipe = NULL; | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Check the pipe_id parameter. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id(pipe_id); | ||||
|     } | ||||
|     if (error == NULL && !(pipe_id & 0x80)) | ||||
|     { | ||||
|         error = error_create("Asynchronous pipes for OUT endpoints are not supported."); | ||||
|     } | ||||
|  | ||||
|     // Perform OS-specific setup. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = async_in_pipe_setup(handle, pipe_id); | ||||
|     } | ||||
|  | ||||
|     libusbp_async_in_pipe * new_pipe = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_pipe = calloc(1, sizeof(libusbp_async_in_pipe)); | ||||
|         if (new_pipe == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_pipe->handle = handle; | ||||
|         new_pipe->pipe_id = pipe_id; | ||||
|         *pipe = new_pipe; | ||||
|         new_pipe = NULL; | ||||
|     } | ||||
|  | ||||
|     free(new_pipe); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_allocate_transfers( | ||||
|     libusbp_async_in_pipe * pipe, | ||||
|     size_t transfer_count, | ||||
|     size_t transfer_size) | ||||
| { | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     if (pipe->transfer_array != NULL) | ||||
|     { | ||||
|         return error_create("Transfers were already allocated for this pipe."); | ||||
|     } | ||||
|  | ||||
|     if (transfer_count == 0) | ||||
|     { | ||||
|         return error_create("Transfer count cannot be zero."); | ||||
|     } | ||||
|  | ||||
|     if (transfer_size == 0) | ||||
|     { | ||||
|         return error_create("Transfer size cannot be zero."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     async_in_transfer ** new_transfer_array = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer_array = calloc(transfer_count, sizeof(async_in_transfer *)); | ||||
|         if (new_transfer_array == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for(size_t i = 0; error == NULL && i < transfer_count; i++) | ||||
|     { | ||||
|         error = async_in_transfer_create(pipe->handle, pipe->pipe_id, | ||||
|             transfer_size, &new_transfer_array[i]); | ||||
|     } | ||||
|  | ||||
|     // Put the new array and the information about it into the pipe. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         pipe->transfer_array = new_transfer_array; | ||||
|         pipe->transfer_count = transfer_count; | ||||
|         pipe->transfer_size = transfer_size; | ||||
|         new_transfer_array = NULL; | ||||
|     } | ||||
|  | ||||
|     async_in_transfer_array_free(new_transfer_array, transfer_count); | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to allocate transfers for asynchronous IN pipe."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| static void async_in_pipe_submit_next_transfer(libusbp_async_in_pipe * pipe) | ||||
| { | ||||
|     assert(pipe != NULL); | ||||
|     assert(pipe->pending_count < pipe->transfer_count); | ||||
|  | ||||
|     // Submit the next transfer. | ||||
|     async_in_transfer_submit(pipe->transfer_array[pipe->next_submit]); | ||||
|  | ||||
|     // Update the counts and indices. | ||||
|     pipe->pending_count++; | ||||
|     pipe->next_submit = increment_and_wrap_size(pipe->next_submit, pipe->transfer_count); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_start_endless_transfers( | ||||
|     libusbp_async_in_pipe * pipe) | ||||
| { | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     if (pipe->transfer_array == NULL) | ||||
|     { | ||||
|         return error_create("Pipe transfers have not been allocated yet."); | ||||
|     } | ||||
|  | ||||
|     pipe->endless_transfers_enabled = true; | ||||
|  | ||||
|     while(pipe->pending_count < pipe->transfer_count) | ||||
|     { | ||||
|         async_in_pipe_submit_next_transfer(pipe); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_handle_events(libusbp_async_in_pipe * pipe) | ||||
| { | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     return generic_handle_events(pipe->handle); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_has_pending_transfers( | ||||
|     libusbp_async_in_pipe * pipe, | ||||
|     bool * result) | ||||
| { | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL && result == NULL) | ||||
|     { | ||||
|         error = error_create("Boolean output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *result = false; | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && pipe == NULL) | ||||
|     { | ||||
|         error = error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *result = pipe->pending_count ? 1 : 0; | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_handle_finished_transfer( | ||||
|     libusbp_async_in_pipe * pipe, | ||||
|     bool * finished, | ||||
|     void * buffer, | ||||
|     size_t * transferred, | ||||
|     libusbp_error ** transfer_error) | ||||
| { | ||||
|     if (finished != NULL) | ||||
|     { | ||||
|         *finished = false; | ||||
|     } | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (transfer_error != NULL) | ||||
|     { | ||||
|         *transfer_error = NULL; | ||||
|     } | ||||
|  | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     if (pipe->pending_count == 0) | ||||
|     { | ||||
|         // There are no pending transfers that we could check for completion. | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     async_in_transfer * transfer = pipe->transfer_array[pipe->next_finish]; | ||||
|  | ||||
|     if (async_in_transfer_pending(transfer)) | ||||
|     { | ||||
|         // The next transfer we expect to finish is still pending; | ||||
|         // the kernel has not told us that it is done. | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = async_in_transfer_get_results(transfer, buffer, | ||||
|         transferred, transfer_error); | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         if (finished != NULL) | ||||
|         { | ||||
|             *finished = true; | ||||
|         } | ||||
|  | ||||
|         pipe->pending_count--; | ||||
|         pipe->next_finish = increment_and_wrap_size(pipe->next_finish, pipe->transfer_count); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && pipe->endless_transfers_enabled) | ||||
|     { | ||||
|         async_in_pipe_submit_next_transfer(pipe); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_async_in_pipe_cancel_transfers(libusbp_async_in_pipe * pipe) | ||||
| { | ||||
|     if (pipe == NULL) | ||||
|     { | ||||
|         return error_create("Pipe argument is null."); | ||||
|     } | ||||
|  | ||||
|     pipe->endless_transfers_enabled = false; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     #ifdef __linux__ | ||||
|     // In Linux, transfers need to be cancelled individually. | ||||
|     for (size_t i = 0; error == NULL && i < pipe->transfer_count; i++) | ||||
|     { | ||||
|         error = async_in_transfer_cancel(pipe->transfer_array[i]); | ||||
|  | ||||
|         // This doesn't help the performance issue in this function: | ||||
|         //if (error == NULL) { error = generic_handle_events(pipe->handle); } | ||||
|     } | ||||
|  | ||||
|     #else | ||||
|  | ||||
|     // On other platforms, any of the transfers has all the information needed | ||||
|     // to cancel the others. | ||||
|     error = async_in_transfer_cancel(pipe->transfer_array[0]); | ||||
|  | ||||
|     #endif | ||||
|  | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										223
									
								
								dep/libusbp/src/error.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								dep/libusbp/src/error.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #if (defined(__GNUC__) && !defined(__USE_MINGW_ANSI_STDIO)) || (defined(_MSC_VER) && _MSC_VER < 1900) | ||||
| #error This code depends on vsnprintf returning a number of characters. | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| struct libusbp_error | ||||
| { | ||||
|     bool do_not_free; | ||||
|     char * message; | ||||
|     size_t code_count; | ||||
|     uint32_t * code_array; | ||||
| }; | ||||
|  | ||||
| static uint32_t mem_error_code_array[1] = { LIBUSBP_ERROR_MEMORY }; | ||||
|  | ||||
| static char error_no_memory_msg[] = "Failed to allocate memory."; | ||||
| libusbp_error error_no_memory = | ||||
| { | ||||
|     .do_not_free = true, | ||||
|     .message = error_no_memory_msg, | ||||
|     .code_count = 1, | ||||
|     .code_array = mem_error_code_array, | ||||
| }; | ||||
|  | ||||
| static char error_masked_by_no_memory_msg[] = "Failed to allocate memory for reporting an error."; | ||||
| static libusbp_error error_masked_by_no_memory = | ||||
| { | ||||
|     .do_not_free = true, | ||||
|     .message = error_masked_by_no_memory_msg, | ||||
|     .code_count = 1, | ||||
|     .code_array = mem_error_code_array, | ||||
| }; | ||||
|  | ||||
| static libusbp_error error_blank = | ||||
| { | ||||
|     .do_not_free = true, | ||||
|     .message = NULL, | ||||
|     .code_count = 0, | ||||
|     .code_array = NULL, | ||||
| }; | ||||
|  | ||||
| void libusbp_error_free(libusbp_error * error) | ||||
| { | ||||
|     if (error != NULL && !error->do_not_free) | ||||
|     { | ||||
|         free(error->message); | ||||
|         free(error->code_array); | ||||
|         free(error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Copies the error.  If the input is not NULL, the output will always | ||||
| // be not NULL, but it might be a immutable error (do_not_free=1). | ||||
| libusbp_error * libusbp_error_copy(const libusbp_error * src_error) | ||||
| { | ||||
|     if (src_error == NULL) { return NULL; } | ||||
|  | ||||
|     const char * src_message = src_error->message; | ||||
|     if (src_message == NULL) { src_message = ""; } | ||||
|     size_t message_length = strlen(src_message); | ||||
|  | ||||
|     size_t code_count = src_error->code_count; | ||||
|     if (src_error->code_array == NULL) { code_count = 0; } | ||||
|  | ||||
|     // Allocate memory. | ||||
|     libusbp_error * new_error = malloc(sizeof(libusbp_error)); | ||||
|     char * new_message = malloc(message_length + 1); | ||||
|     uint32_t * new_code_array = malloc(code_count * sizeof(uint32_t)); | ||||
|     if (new_error == NULL || new_message == NULL || | ||||
|         (code_count != 0 && new_code_array == NULL)) | ||||
|     { | ||||
|         free(new_error); | ||||
|         free(new_message); | ||||
|         free(new_code_array); | ||||
|         return &error_masked_by_no_memory; | ||||
|     } | ||||
|  | ||||
|     if (code_count != 0) | ||||
|     { | ||||
|         memcpy(new_code_array, src_error->code_array, code_count * sizeof(uint32_t)); | ||||
|     } | ||||
|     strncpy(new_message, src_message, message_length + 1); | ||||
|     new_error->do_not_free = false; | ||||
|     new_error->message = new_message; | ||||
|     new_error->code_count = code_count; | ||||
|     new_error->code_array = new_code_array; | ||||
|     return new_error; | ||||
| } | ||||
|  | ||||
| // Tries to make the error mutable.  Always returns a non-NULL error, | ||||
| // but it might return an immutable error (do_not_free == 1) if memory | ||||
| // cannot be allocated. | ||||
| static libusbp_error * error_make_mutable(libusbp_error * error) | ||||
| { | ||||
|     if (error == NULL) { error = &error_blank; } | ||||
|     if (error->do_not_free) | ||||
|     { | ||||
|       error = libusbp_error_copy(error); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Tries to add a message to the error.  After calling this, you | ||||
| // should not use the error you passed as an input, because it might | ||||
| // have been freed. | ||||
| libusbp_error * error_add_v(libusbp_error * error, const char * format, va_list ap) | ||||
| { | ||||
|     if (format == NULL) { return error; } | ||||
|  | ||||
|     error = error_make_mutable(error); | ||||
|     if (error == NULL || error->do_not_free) { return error; } | ||||
|  | ||||
|     if (error->message == NULL) { error->message = ""; } | ||||
|  | ||||
|     // Determine all the string lengths. | ||||
|     size_t outer_message_length = 0; | ||||
|     { | ||||
|         char x[1]; | ||||
|         va_list ap2; | ||||
|         va_copy(ap2, ap); | ||||
|         int result = vsnprintf(x, 0, format, ap2); | ||||
|         if (result > 0) | ||||
|         { | ||||
|             outer_message_length = (size_t) result; | ||||
|         } | ||||
|         va_end(ap2); | ||||
|     } | ||||
|     size_t inner_message_length = strlen(error->message); | ||||
|     size_t separator_length = (inner_message_length && outer_message_length) ? 2 : 0; | ||||
|     size_t message_length = outer_message_length + separator_length + inner_message_length; | ||||
|  | ||||
|     char * message = malloc(message_length + 1); | ||||
|     if (message == NULL) | ||||
|     { | ||||
|         libusbp_error_free(error); | ||||
|         return &error_masked_by_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Assemble the message. | ||||
|     vsnprintf(message, outer_message_length + 1, format, ap); | ||||
|     if (separator_length) | ||||
|     { | ||||
|       strncpy(message + outer_message_length, "  ", separator_length + 1); | ||||
|     } | ||||
|     strncpy(message + outer_message_length + separator_length, | ||||
|         error->message, inner_message_length + 1); | ||||
|     message[message_length] = 0; | ||||
|  | ||||
|     free(error->message); | ||||
|     error->message = message; | ||||
|     return error; | ||||
| } | ||||
|  | ||||
|  | ||||
| // Tries to add the specified code to the error. | ||||
| // This is just like error_add_v in terms of pointer ownership. | ||||
| libusbp_error * error_add_code(libusbp_error * error, uint32_t code) | ||||
| { | ||||
|     error = error_make_mutable(error); | ||||
|     if (error == NULL || error->do_not_free) { return error; } | ||||
|     if (error->code_count >= SIZE_MAX / sizeof(uint32_t)) { return error; } | ||||
|  | ||||
|     size_t size = (error->code_count + 1) * sizeof(uint32_t); | ||||
|     uint32_t * new_array = realloc(error->code_array, size); | ||||
|     if (new_array == NULL) | ||||
|     { | ||||
|         libusbp_error_free(error); | ||||
|         return &error_masked_by_no_memory; | ||||
|     } | ||||
|     error->code_array = new_array; | ||||
|     error->code_array[error->code_count++] = code; | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Variadic version of error_add_v. | ||||
| libusbp_error * error_add(libusbp_error * error, const char * format, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Creates a new error. | ||||
| libusbp_error * error_create(const char * format, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     libusbp_error * error = error_add_v(NULL, format, ap); | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| bool libusbp_error_has_code(const libusbp_error * error, uint32_t code) | ||||
| { | ||||
|     if (error == NULL) { return false; } | ||||
|  | ||||
|     for (size_t i = 0; i < error->code_count; i++) | ||||
|     { | ||||
|         if (error->code_array[i] == code) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| const char * libusbp_error_get_message(const libusbp_error * error) | ||||
| { | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         return "No error."; | ||||
|     } | ||||
|     if (error->message == NULL) | ||||
|     { | ||||
|         return ""; | ||||
|     } | ||||
|     return error->message; | ||||
| } | ||||
							
								
								
									
										21
									
								
								dep/libusbp/src/error_hresult.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								dep/libusbp/src/error_hresult.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| #if defined(_WIN32) || defined(__APPLE__) | ||||
|  | ||||
| libusbp_error * error_create_hr(HRESULT hr, const char * format, ...) | ||||
| { | ||||
|     // HRESULT should be an int32_t on the systems we care about (Mac OS X, | ||||
|     // Win32, Win64), but let's assert it here in case that ever changes. | ||||
|     assert(sizeof(HRESULT) == 4); | ||||
|     assert((HRESULT)-1 < (HRESULT)0); | ||||
|  | ||||
|     libusbp_error * error = error_create("HRESULT error code 0x%x.", (int32_t)hr); | ||||
|  | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										70
									
								
								dep/libusbp/src/find_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								dep/libusbp/src/find_device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| static libusbp_error * check_device_vid_pid(const libusbp_device * device, | ||||
|     uint16_t vendor_id, uint16_t product_id, bool * matches) | ||||
| { | ||||
|     assert(matches != NULL); | ||||
|  | ||||
|     *matches = false; | ||||
|  | ||||
|     libusbp_error * error; | ||||
|  | ||||
|     uint16_t device_vid; | ||||
|     error = libusbp_device_get_vendor_id(device, &device_vid); | ||||
|     if (error != NULL) { return error; } | ||||
|     if (device_vid != vendor_id) { return NULL; } | ||||
|  | ||||
|     uint16_t device_pid; | ||||
|     error = libusbp_device_get_product_id(device, &device_pid); | ||||
|     if (error != NULL) { return error; } | ||||
|     if (device_pid != product_id) { return NULL; } | ||||
|  | ||||
|     *matches = true; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_find_device_with_vid_pid( | ||||
|     uint16_t vendor_id, uint16_t product_id, libusbp_device ** device) | ||||
| { | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *device = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_device ** new_list = NULL; | ||||
|     size_t size = 0; | ||||
|     error = libusbp_list_connected_devices(&new_list, &size); | ||||
|  | ||||
|     assert(error != NULL || new_list != NULL); | ||||
|  | ||||
|     for(size_t i = 0; error == NULL && i < size; i++) | ||||
|     { | ||||
|         // Each iteration of this loop checks one candidate device | ||||
|         // and either passes it to the caller or frees it. | ||||
|  | ||||
|         libusbp_device * candidate = new_list[i]; | ||||
|  | ||||
|         if (*device == NULL) | ||||
|         { | ||||
|             bool matches; | ||||
|             error = check_device_vid_pid(candidate, vendor_id, product_id, &matches); | ||||
|             if (error != NULL) { break; } | ||||
|  | ||||
|             if (matches) | ||||
|             { | ||||
|                 // Return the device to the caller. | ||||
|                 *device = candidate; | ||||
|                 candidate = NULL; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         libusbp_device_free(candidate); | ||||
|     } | ||||
|  | ||||
|     libusbp_list_free(new_list); | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										28
									
								
								dep/libusbp/src/info.rc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								dep/libusbp/src/info.rc.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #define VERSION             ${LIBUSBP_VERSION_MAJOR},${LIBUSBP_VERSION_MINOR},${LIBUSBP_VERSION_PATCH},0 | ||||
| #define VERSION_STR         "${LIBUSBP_VERSION_MAJOR}.${LIBUSBP_VERSION_MINOR}.${LIBUSBP_VERSION_PATCH}" | ||||
|  | ||||
| #include <winver.h> | ||||
| VS_VERSION_INFO VERSIONINFO | ||||
| FILEVERSION    	VERSION | ||||
| PRODUCTVERSION 	VERSION | ||||
| FILEOS         	VOS_NT_WINDOWS32 | ||||
| FILETYPE       	VFT_DLL | ||||
| BEGIN | ||||
|     BLOCK "StringFileInfo" | ||||
|     BEGIN | ||||
|         BLOCK "040904E4" | ||||
|         BEGIN | ||||
|             VALUE "CompanyName",      "Pololu Corporation" | ||||
|             VALUE "FileDescription",  "Pololu USB Library" | ||||
|             VALUE "FileVersion",      VERSION_STR | ||||
|             VALUE "ProductName",      "Pololu USB Library (${CMAKE_BUILD_TYPE})" | ||||
|             VALUE "ProductVersion",   VERSION_STR | ||||
|             VALUE "LegalCopyright",   "Copyright (C) ${YEAR} Pololu Corporation" | ||||
|         END | ||||
|     END | ||||
|  | ||||
|     BLOCK "VarFileInfo" | ||||
|     BEGIN | ||||
|         VALUE "Translation", 0x409, 1252 | ||||
|     END | ||||
| END | ||||
							
								
								
									
										10
									
								
								dep/libusbp/src/libusbp.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								dep/libusbp/src/libusbp.pc.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| prefix=@CMAKE_INSTALL_PREFIX@ | ||||
| libdir=${prefix}/lib | ||||
| includedir=${prefix}/include | ||||
|  | ||||
| Name: libusbp | ||||
| Version: @LIBUSBP_VERSION@ | ||||
| Description: Library for accessing USB devices. | ||||
| Requires: @PC_REQUIRES@ | ||||
| Libs: -L${libdir} -lusbp-@LIBUSBP_VERSION_MAJOR@ @PC_MORE_LIBS@ | ||||
| Cflags: -I${includedir}/libusbp-@LIBUSBP_VERSION_MAJOR@ @PC_MORE_CFLAGS@ | ||||
							
								
								
									
										11
									
								
								dep/libusbp/src/libusbp_config.h.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								dep/libusbp/src/libusbp_config.h.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| #define BUILD_SYSTEM_LIBUSBP_VERSION_MAJOR @LIBUSBP_VERSION_MAJOR@ | ||||
|  | ||||
| #cmakedefine LIBUSBP_LOG | ||||
|  | ||||
| #cmakedefine VBOX_LINUX_ON_WINDOWS | ||||
|  | ||||
| #cmakedefine USE_TEST_DEVICE_A | ||||
|  | ||||
| #cmakedefine USE_TEST_DEVICE_B | ||||
							
								
								
									
										367
									
								
								dep/libusbp/src/libusbp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								dep/libusbp/src/libusbp_internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,367 @@ | ||||
| #ifdef LIBUSBP_DROP_IN | ||||
| #include "libusbp.h" | ||||
| #else | ||||
| #pragma once | ||||
| #include <libusbp_config.h> | ||||
| #include <libusbp.h> | ||||
| #if BUILD_SYSTEM_LIBUSBP_VERSION_MAJOR != LIBUSBP_VERSION_MAJOR | ||||
| #error Major version in libusbp.h disagrees with build system. | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| // Don't warn about zero-length format strings, which we sometimes use when | ||||
| // constructing error objects. | ||||
| #ifdef __GNUC__ | ||||
| #pragma GCC diagnostic ignored "-Wformat-zero-length" | ||||
| #endif | ||||
|  | ||||
| // Silence some warnings from the Microsoft C Compiler. | ||||
| #ifdef _MSC_VER | ||||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| #define strdup _strdup | ||||
| #endif | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <limits.h> | ||||
| #include <stdio.h> | ||||
| #include <stdarg.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <windows.h> | ||||
| #include <devpropdef.h> | ||||
| #include <setupapi.h> | ||||
| #include <cfgmgr32.h> | ||||
| #include <usbiodef.h> | ||||
| #include <usbioctl.h> | ||||
| #include <stringapiset.h> | ||||
| #include <winusb.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef __linux__ | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| #include <libudev.h> | ||||
| #include <linux/usbdevice_fs.h> | ||||
| #include <linux/usb/ch9.h> | ||||
| #include <sys/ioctl.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
| #include <CoreFoundation/CoreFoundation.h> | ||||
| #include <mach/error.h> | ||||
| #include <mach/mach_error.h> | ||||
| #include <IOKit/IOKitLib.h> | ||||
| #include <IOKit/IOCFBundle.h> | ||||
| #include <IOKit/IOCFPlugIn.h> | ||||
| #include <IOKit/usb/IOUSBLib.h> | ||||
| #include <IOKit/serial/IOSerialKeys.h> | ||||
| #include <IOKit/serial/ioss.h> | ||||
| #endif | ||||
|  | ||||
| // For debug builds (NDEBUG undefined), functions and variables declared with | ||||
| // LIBUSBP_TEST_API will be exported in the library so that they can be unit | ||||
| // tested, but they are not part of the public API and they might change at any | ||||
| // time.  Those symbols can cause name collisions with symbols defined by the | ||||
| // user, so this type of build is not suitable for making a general release. | ||||
| #ifdef NDEBUG | ||||
| #define LIBUSBP_TEST_API | ||||
| #else | ||||
| #define LIBUSBP_TEST_API LIBUSBP_API | ||||
| #endif | ||||
|  | ||||
| #ifdef _MSC_VER | ||||
| #define LIBUSBP_PRINTF(f, a) | ||||
| #else | ||||
| #define LIBUSBP_PRINTF(f, a) __attribute__((format (printf, f, a))) | ||||
| #endif | ||||
|  | ||||
| // Suppresses unused parameter warnings. | ||||
| #define LIBUSBP_UNUSED(param_name) (void)param_name; | ||||
|  | ||||
| #define MAX_ENDPOINT_NUMBER 15 | ||||
|  | ||||
| typedef struct libusbp_setup_packet | ||||
| { | ||||
|     uint8_t bmRequestType; | ||||
|     uint8_t bRequest; | ||||
|     uint16_t wValue; | ||||
|     uint16_t wIndex; | ||||
|     uint16_t wLength; | ||||
| } libusbp_setup_packet; | ||||
|  | ||||
| LIBUSBP_TEST_API extern libusbp_error error_no_memory; | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED libusbp_error * error_create( | ||||
|     const char * format, ...) LIBUSBP_PRINTF(1, 2); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * error_add_v( | ||||
|     libusbp_error * error, const char * format, va_list ap); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED libusbp_error * error_add( | ||||
|     libusbp_error * error, const char * format, ...) | ||||
|     LIBUSBP_PRINTF(2, 3); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED libusbp_error * error_add_code( | ||||
|     libusbp_error * error, uint32_t code); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * string_copy( | ||||
|     const char * input_string, | ||||
|     char ** output_string); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * check_pipe_id(uint8_t pipe_id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * check_pipe_id_in(uint8_t pipe_id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * check_pipe_id_out(uint8_t pipe_id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * device_list_create(libusbp_device *** device_list); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * device_list_append(libusbp_device *** device_list, | ||||
|     size_t * device_count, libusbp_device * device); | ||||
|  | ||||
| void free_devices_and_list(libusbp_device ** device_list); | ||||
|  | ||||
| typedef struct async_in_transfer | ||||
|                async_in_transfer; | ||||
|  | ||||
| void async_in_transfer_handle_completion(async_in_transfer *); | ||||
|  | ||||
| void async_in_transfer_free(async_in_transfer * transfer); | ||||
|  | ||||
| libusbp_error * async_in_transfer_create( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     size_t transfer_size, | ||||
|     async_in_transfer ** transfer); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * async_in_pipe_create( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     libusbp_async_in_pipe ** pipe); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * async_in_pipe_setup(libusbp_generic_handle * gh, uint8_t pipe_id); | ||||
|  | ||||
| void async_in_transfer_submit(async_in_transfer * transfer); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * async_in_transfer_get_results(async_in_transfer * transfer, | ||||
|     void * buffer, size_t * transferred, libusbp_error ** transfer_error); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * async_in_transfer_cancel(async_in_transfer * transfer); | ||||
|  | ||||
| bool async_in_transfer_pending(async_in_transfer * transfer); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * generic_handle_events(libusbp_generic_handle * handle); | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * create_device(HDEVINFO list, | ||||
|     PSP_DEVINFO_DATA info, libusbp_device ** device); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(1, 2) | ||||
| libusbp_error * error_create_winapi(const char * format, ...); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(1, 2) | ||||
| libusbp_error * error_create_overlapped(const char * format, ...); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(2, 3) | ||||
| libusbp_error * error_create_cr(CONFIGRET, const char * format, ...); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * get_interface( | ||||
|     const char * device_instance_id, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     HDEVINFO * list, | ||||
|     PSP_DEVINFO_DATA info); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * create_id_string( | ||||
|     HDEVINFO list, PSP_DEVINFO_DATA info, char ** id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED libusbp_error * get_filename_from_devinst_and_guid( | ||||
|     DEVINST devinst, | ||||
|     const GUID * guid, | ||||
|     char ** filename); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef __linux__ | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(1, 2) | ||||
| libusbp_error * error_create_errno(const char * format, ...); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(2, 3) | ||||
| libusbp_error * error_create_udev(int error_code, const char * format, ...); | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * error_from_urb_status(struct usbdevfs_urb * urb); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * device_create(struct udev_device * dev, libusbp_device ** device); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * generic_interface_get_device_copy( | ||||
|     const libusbp_generic_interface * gi, libusbp_device ** device); | ||||
|  | ||||
| /** udevw **********************************************************************/ | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_create_context(struct udev **); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_create_usb_list(struct udev * context, struct udev_enumerate ** list); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_device_from_syspath(struct udev *, | ||||
|     const char * syspath, struct udev_device ** dev); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_device_type(struct udev_device *, const char ** devtype); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_sysattr_uint8( | ||||
|     struct udev_device * dev, const char * name, uint8_t * value); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_sysattr_uint16( | ||||
|     struct udev_device * dev, const char * name, uint16_t * value); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_sysattr_if_exists_copy( | ||||
|   struct udev_device * dev, const char * name, char ** value); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_interface( | ||||
|     struct udev * udev, | ||||
|     const char * device_syspath, | ||||
|     uint8_t interface_number, | ||||
|     struct udev_device ** device); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_tty( | ||||
|     struct udev * udev, | ||||
|     struct udev_device * parent, | ||||
|     struct udev_device ** device); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_syspath(struct udev_device * device, const char ** syspath); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_syspath_copy(struct udev_device * device, char ** devnode); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_devnode(struct udev_device * device, const char ** devnode); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_devnode_copy(struct udev_device * device, char ** devnode); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * udevw_get_devnode_copy_from_syspath(const char * syspath, char ** devnode); | ||||
|  | ||||
|  | ||||
| /** usbfd **********************************************************************/ | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_check_existence(const char * filename); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_open(const char * path, int * fd); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_get_device_descriptor(int fd, struct usb_device_descriptor * desc); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_control_transfer(int fd, libusbp_setup_packet setup, | ||||
|     uint32_t timeout, void * data, size_t * transferred); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_control_transfer_async(int fd, void * combined_buffer, | ||||
|     size_t size, uint32_t timeout, void * user_context); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_bulk_or_interrupt_transfer(int fd, uint8_t pipe, uint32_t timeout, | ||||
|     void * buffer, size_t size, size_t * transferred); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_submit_urb(int fd, struct usbdevfs_urb * urb); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_reap_urb(int fd, struct usbdevfs_urb ** urb); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * usbfd_discard_urb(int fd, struct usbdevfs_urb * urb); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(2, 3) | ||||
| libusbp_error * error_create_mach(kern_return_t error_code, const char * format, ...); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * create_device(io_service_t service, libusbp_device ** device); | ||||
|  | ||||
| uint64_t device_get_id(const libusbp_device * device); | ||||
|  | ||||
| bool generic_interface_get_interface_id(const libusbp_generic_interface * gi, uint64_t * id); | ||||
| uint64_t generic_interface_get_device_id(const libusbp_generic_interface * gi); | ||||
| uint8_t generic_interface_get_interface_number(const libusbp_generic_interface * gi); | ||||
|  | ||||
| IOUSBInterfaceInterface182 ** generic_handle_get_ioh(const libusbp_generic_handle * handle); | ||||
|  | ||||
| uint8_t generic_handle_get_pipe_index(const libusbp_generic_handle * handle, uint8_t pipe_id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * iokit_id_to_string(uint64_t id, char ** str); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * service_get_from_id(uint64_t id, io_service_t *); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * service_get_usb_interface(io_service_t service, | ||||
|     uint8_t interface_number, io_service_t * interface_service); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * service_get_child_by_class(io_service_t service, | ||||
|     const char * class_name, io_service_t * interface_service); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * service_to_interface(io_service_t, CFUUIDRef pluginType, REFIID rid, | ||||
|     void **, IOCFPlugInInterface ***); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * get_id(io_registry_entry_t entry, uint64_t * id); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * get_string(io_registry_entry_t entry, | ||||
|     CFStringRef name, char ** value); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * get_int32(io_registry_entry_t entry, | ||||
|     CFStringRef name, int32_t * value); | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| libusbp_error * get_uint16(io_registry_entry_t entry, | ||||
|     CFStringRef name, uint16_t * value); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) || defined(__APPLE__) | ||||
|  | ||||
| LIBUSBP_TEST_API LIBUSBP_WARN_UNUSED LIBUSBP_PRINTF(2, 3) | ||||
| libusbp_error * error_create_hr(HRESULT, const char * format, ...); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										187
									
								
								dep/libusbp/src/linux/async_in_transfer_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								dep/libusbp/src/linux/async_in_transfer_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct async_in_transfer | ||||
| { | ||||
|     struct usbdevfs_urb urb; | ||||
|     bool pending; | ||||
|     libusbp_error * error; | ||||
|     int fd; | ||||
| }; | ||||
|  | ||||
| libusbp_error * async_in_pipe_setup(libusbp_generic_handle * handle, uint8_t pipe_id) | ||||
| { | ||||
|     LIBUSBP_UNUSED(handle); | ||||
|     LIBUSBP_UNUSED(pipe_id); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_create( | ||||
|     libusbp_generic_handle * handle, uint8_t pipe_id, size_t transfer_size, | ||||
|     async_in_transfer ** transfer) | ||||
| { | ||||
|     assert(transfer_size != 0); | ||||
|     assert(transfer != NULL); | ||||
|  | ||||
|     if (transfer_size > INT_MAX) | ||||
|     { | ||||
|         // usbdevfs_urb uses ints to represent sizes. | ||||
|         return error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate the transfer struct. | ||||
|     async_in_transfer * new_transfer = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer = calloc(1, sizeof(async_in_transfer)); | ||||
|         if (new_transfer == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Allocate the buffer for the transfer. | ||||
|     void * new_buffer = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_buffer = malloc(transfer_size); | ||||
|         if (new_buffer == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Assemble the transfer and pass it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer->fd = libusbp_generic_handle_get_fd(handle); | ||||
|  | ||||
|         new_transfer->urb.usercontext = new_transfer; | ||||
|         new_transfer->urb.buffer_length = transfer_size; | ||||
|         new_transfer->urb.type = USBDEVFS_URB_TYPE_BULK; | ||||
|         new_transfer->urb.endpoint = pipe_id; | ||||
|  | ||||
|         new_transfer->urb.buffer = new_buffer; | ||||
|         new_buffer = NULL; | ||||
|  | ||||
|         *transfer = new_transfer; | ||||
|         new_transfer = NULL; | ||||
|     } | ||||
|  | ||||
|     free(new_buffer); | ||||
|     free(new_transfer); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void async_in_transfer_free(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) { return; } | ||||
|  | ||||
|     if (transfer->pending) | ||||
|     { | ||||
|         // Unfortunately, this transfer is still pending, so we cannot free it; | ||||
|         // the kernel needs to be able to write to this transfer's memory when | ||||
|         // it completes.  We could just abort the process here, but instead we | ||||
|         // we choose to have a memory leak, since it can be easily detected and | ||||
|         // might be small enough to be harmless. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     free(transfer->urb.buffer); | ||||
|     free(transfer); | ||||
| } | ||||
|  | ||||
| void async_in_transfer_submit(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(transfer->pending == false); | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     transfer->error = NULL; | ||||
|     transfer->pending = true; | ||||
|  | ||||
|     libusbp_error * error = usbfd_submit_urb(transfer->fd, &transfer->urb); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         transfer->pending = false; | ||||
|         transfer->error = error; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void async_in_transfer_handle_completion(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|  | ||||
|     #ifdef LIBUSBP_LOG | ||||
|     fprintf(stderr, "URB completed: %p, status=%d, actual_length=%d\n", | ||||
|         transfer, transfer->urb.status, transfer->urb.actual_length); | ||||
|     #endif | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = error_from_urb_status(&transfer->urb); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && transfer->urb.error_count != 0) | ||||
|     { | ||||
|         error = error_create("Non-zero error count for USB request: %d.", | ||||
|             transfer->urb.error_count); | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Asynchronous IN transfer failed."); | ||||
|     } | ||||
|  | ||||
|     transfer->pending = false; | ||||
|     transfer->error = error; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_get_results(async_in_transfer * transfer, | ||||
|     void * buffer, size_t * transferred, libusbp_error ** transfer_error) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(!transfer->pending); | ||||
|  | ||||
|     size_t tmp_transferred = transfer->urb.actual_length; | ||||
|  | ||||
|     // Make sure we don't overflow the user's buffer. | ||||
|     if (tmp_transferred > (size_t)transfer->urb.buffer_length) | ||||
|     { | ||||
|         tmp_transferred = transfer->urb.buffer_length; | ||||
|     } | ||||
|  | ||||
|     if (buffer != NULL) | ||||
|     { | ||||
|         memcpy(buffer, transfer->urb.buffer, tmp_transferred); | ||||
|     } | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = tmp_transferred; | ||||
|     } | ||||
|  | ||||
|     if (transfer_error != NULL) | ||||
|     { | ||||
|         *transfer_error = libusbp_error_copy(transfer->error); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_cancel(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) { return NULL; } | ||||
|  | ||||
|     return usbfd_discard_urb(transfer->fd, &transfer->urb); | ||||
| } | ||||
|  | ||||
| bool async_in_transfer_pending(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     return transfer->pending; | ||||
| } | ||||
							
								
								
									
										256
									
								
								dep/libusbp/src/linux/device_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								dep/libusbp/src/linux/device_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_device | ||||
| { | ||||
|     char * syspath; | ||||
|     char * serial_number;  // may be NULL | ||||
|     uint16_t product_id; | ||||
|     uint16_t vendor_id; | ||||
|     uint16_t revision; | ||||
| }; | ||||
|  | ||||
| libusbp_error * device_create(struct udev_device * dev, libusbp_device ** device) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_device = malloc(sizeof(libusbp_device)); | ||||
|         if (new_device == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the syspath. | ||||
|     char * new_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_syspath_copy(dev, &new_syspath); | ||||
|     } | ||||
|  | ||||
|     // Get the vendor ID. | ||||
|     uint16_t vendor_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_sysattr_uint16(dev, "idVendor", &vendor_id); | ||||
|     } | ||||
|  | ||||
|     // Get the product ID. | ||||
|     uint16_t product_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_sysattr_uint16(dev, "idProduct", &product_id); | ||||
|     } | ||||
|  | ||||
|     // Get the revision. | ||||
|     uint16_t revision; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_sysattr_uint16(dev, "bcdDevice", &revision); | ||||
|     } | ||||
|  | ||||
|     // Get the serial number. | ||||
|     char * new_serial_number = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_sysattr_if_exists_copy(dev, "serial", &new_serial_number); | ||||
|     } | ||||
|  | ||||
|     // Populate the new device and give it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_device->syspath = new_syspath; | ||||
|         new_device->serial_number = new_serial_number; | ||||
|         new_device->vendor_id = vendor_id; | ||||
|         new_device->product_id = product_id; | ||||
|         new_device->revision = revision; | ||||
|         *device = new_device; | ||||
|  | ||||
|         new_syspath = NULL; | ||||
|         new_serial_number = NULL; | ||||
|         new_device = NULL; | ||||
|     } | ||||
|  | ||||
|     free(new_serial_number); | ||||
|     free(new_syspath); | ||||
|     free(new_device); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_device_free(libusbp_device * device) | ||||
| { | ||||
|     if (device != NULL) | ||||
|     { | ||||
|         free(device->serial_number); | ||||
|         free(device->syspath); | ||||
|         free(device); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_copy( | ||||
|     const libusbp_device * source, libusbp_device ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Device output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) { return NULL; } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_device = malloc(sizeof(libusbp_device)); | ||||
|         if (new_device == NULL) { error = &error_no_memory; } | ||||
|     } | ||||
|  | ||||
|     // Copy the syspath. | ||||
|     char * new_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         assert(source->syspath != NULL); | ||||
|         error = string_copy(source->syspath, &new_syspath); | ||||
|     } | ||||
|  | ||||
|     // Copy the serial number if it is not NULL. | ||||
|     char * new_serial_number = NULL; | ||||
|     if (error == NULL && source->serial_number != NULL) | ||||
|     { | ||||
|         error = string_copy(source->serial_number, &new_serial_number); | ||||
|     } | ||||
|  | ||||
|     // Assemble the new device and give it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         memcpy(new_device, source, sizeof(libusbp_device)); | ||||
|         new_device->syspath = new_syspath; | ||||
|         new_device->serial_number = new_serial_number; | ||||
|         *dest = new_device; | ||||
|  | ||||
|         new_device = NULL; | ||||
|         new_syspath = NULL; | ||||
|         new_serial_number = NULL; | ||||
|     } | ||||
|  | ||||
|     // Clean up and return. | ||||
|     free(new_device); | ||||
|     free(new_syspath); | ||||
|     free(new_serial_number); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_vendor_id( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * vendor_id) | ||||
| { | ||||
|     if (vendor_id == NULL) | ||||
|     { | ||||
|         return error_create("Vendor ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = device->vendor_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_product_id( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * product_id) | ||||
| { | ||||
|     if (product_id == NULL) | ||||
|     { | ||||
|         return error_create("Product ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = device->product_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_revision( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * revision) | ||||
| { | ||||
|     if (revision == NULL) | ||||
|     { | ||||
|         return error_create("Device revision output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = device->revision; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_serial_number( | ||||
|     const libusbp_device * device, | ||||
|     char ** serial_number) | ||||
| { | ||||
|     if (serial_number == NULL) | ||||
|     { | ||||
|         return error_create("Serial number output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *serial_number = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     if (device->serial_number == NULL) | ||||
|     { | ||||
|         libusbp_error * error = error_create("Device does not have a serial number."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NO_SERIAL_NUMBER); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     return string_copy(device->serial_number, serial_number); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_os_id( | ||||
|     const libusbp_device * device, | ||||
|     char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("Device OS ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     return string_copy(device->syspath, id); | ||||
| } | ||||
							
								
								
									
										123
									
								
								dep/libusbp/src/linux/error_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								dep/libusbp/src/linux/error_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| /* This file has functions for converting Windows error codes libusbp_error | ||||
|  * objects.  See error_message_test.cpp for documentation/justification of the | ||||
|  * conversions that are performed. */ | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * error_create_errno(const char * format, ...) | ||||
| { | ||||
|     int error_code = errno; | ||||
|  | ||||
|     libusbp_error * error = error_create("Error code %d.", error_code); | ||||
|  | ||||
|     bool skip_standard_message = false; | ||||
|  | ||||
|     switch(error_code) | ||||
|     { | ||||
|     case EACCES: | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_ACCESS_DENIED); | ||||
|         break; | ||||
|  | ||||
|     case ENOMEM: | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_MEMORY); | ||||
|         break; | ||||
|  | ||||
|     case EPIPE: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, | ||||
|             "The request was invalid or there was an I/O problem."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_STALL); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|  | ||||
|     case ENODEV: | ||||
|     case ESHUTDOWN: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The device was removed."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|  | ||||
|     case EPROTO: | ||||
|     case ETIME: | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|  | ||||
|     case ETIMEDOUT: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The operation timed out."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_TIMEOUT); | ||||
|         break; | ||||
|  | ||||
|     case EOVERFLOW: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The transfer overflowed."); | ||||
|         break; | ||||
|  | ||||
|     case EILSEQ: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, | ||||
|           "Illegal byte sequence: the device may have been disconnected or the request may have been cancelled."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_CANCELLED); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (!skip_standard_message) | ||||
|     { | ||||
|         // We use strerror_r because strerror is not guaranteed to be | ||||
|         // thread-safe.  Also note that strerror_r does depend on the | ||||
|         // locale. | ||||
|         char buffer[256]; | ||||
|         int result = strerror_r(error_code, buffer, sizeof(buffer) - 1); | ||||
|         if (result == 0) | ||||
|         { | ||||
|             error = error_add(error, "%s.", buffer); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Finally, add the context message provided by the caller. | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * error_from_urb_status(struct usbdevfs_urb * urb) | ||||
| { | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     int error_code = -urb->status; | ||||
|  | ||||
|     switch(error_code) | ||||
|     { | ||||
|     case 0:             // Success | ||||
|         break; | ||||
|  | ||||
|     case ENOENT: | ||||
|         error = error_create("Error code %d.", error_code); | ||||
|         error = error_add(error, "The operation was cancelled."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_CANCELLED); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         errno = error_code; | ||||
|         error = error_create_errno(""); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * error_create_udev(int error_code, const char * format, ...) | ||||
| { | ||||
|     libusbp_error * error = error_create("Error from libudev: %d.", error_code); | ||||
|  | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										389
									
								
								dep/libusbp/src/linux/generic_handle_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								dep/libusbp/src/linux/generic_handle_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,389 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_generic_handle | ||||
| { | ||||
|     libusbp_device * device; | ||||
|     int fd; | ||||
|  | ||||
|     // Timeouts are stored in milliseconds.  0 is forever. | ||||
|     uint32_t in_timeout[MAX_ENDPOINT_NUMBER + 1]; | ||||
|     uint32_t out_timeout[MAX_ENDPOINT_NUMBER + 1]; | ||||
| }; | ||||
|  | ||||
| // Allocates memory structures and opens the device file, but does read or write | ||||
| // anything. | ||||
| static libusbp_error * generic_handle_setup( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     libusbp_generic_handle ** handle) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     assert(handle != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the handle. | ||||
|     libusbp_generic_handle * new_handle = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_handle = calloc(1, sizeof(libusbp_generic_handle)); | ||||
|         if (handle == NULL) { error = &error_no_memory; } | ||||
|     } | ||||
|  | ||||
|     // Copy the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = generic_interface_get_device_copy(gi, &new_device); | ||||
|     } | ||||
|  | ||||
|     // Get the filename. | ||||
|     char * new_filename = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_generic_interface_get_os_filename(gi, &new_filename); | ||||
|     } | ||||
|  | ||||
|     // Open the file. | ||||
|     int new_fd = -1; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = usbfd_open(new_filename, &new_fd); | ||||
|     } | ||||
|  | ||||
|     // Assemble the handle and pass it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_handle->fd = new_fd; | ||||
|         new_fd = -1; | ||||
|  | ||||
|         new_handle->device = new_device; | ||||
|         new_device = NULL; | ||||
|  | ||||
|         *handle = new_handle; | ||||
|         new_handle = NULL; | ||||
|     } | ||||
|  | ||||
|     if (new_fd != -1) { close(new_fd); } | ||||
|     libusbp_string_free(new_filename); | ||||
|     libusbp_device_free(new_device); | ||||
|     free(new_handle); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Reads the device descriptor from the device and makes sure that certain | ||||
| // fields in it match what we were expecting.  This should help detect the | ||||
| // situation where devices were changed between the time that the libusbp_device | ||||
| // object was created and the time that this handle was created. | ||||
| static libusbp_error * check_device_descriptor( | ||||
|     libusbp_generic_handle * handle) | ||||
| { | ||||
|     assert(handle != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct usb_device_descriptor desc; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = usbfd_get_device_descriptor(handle->fd, &desc); | ||||
|     } | ||||
|  | ||||
|     uint16_t vendor_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_vendor_id(handle->device, &vendor_id); | ||||
|     } | ||||
|     if (error == NULL && desc.idVendor != vendor_id) | ||||
|     { | ||||
|         error = error_create("Vendor ID mismatch: 0x%04x != 0x%04x.", | ||||
|             desc.idVendor, vendor_id); | ||||
|     } | ||||
|  | ||||
|     uint16_t product_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_product_id(handle->device, &product_id); | ||||
|     } | ||||
|     if (error == NULL && desc.idProduct != product_id) | ||||
|     { | ||||
|         error = error_create("Product ID mismatch: 0x%04x != 0x%04x.", | ||||
|             desc.idProduct, product_id); | ||||
|     } | ||||
|  | ||||
|     uint16_t revision; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_revision(handle->device, &revision); | ||||
|     } | ||||
|     if (error == NULL && desc.bcdDevice != revision) | ||||
|     { | ||||
|         error = error_create("Device revision mismatch: 0x%04x != 0x%04x.", | ||||
|             desc.bcdDevice, revision); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     libusbp_generic_handle ** handle) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *handle = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Set up the memory structures and open the file handle. | ||||
|     libusbp_generic_handle * new_handle = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = generic_handle_setup(gi, &new_handle); | ||||
|     } | ||||
|  | ||||
|     // Check that the device descriptor is consistent. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_device_descriptor(new_handle); | ||||
|     } | ||||
|  | ||||
|     // Pass the handle to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *handle = new_handle; | ||||
|         new_handle = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_generic_handle_close(new_handle); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_handle_close(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle != NULL) | ||||
|     { | ||||
|         close(handle->fd); | ||||
|         libusbp_device_free(handle->device); | ||||
|         free(handle); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open_async_in_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     libusbp_async_in_pipe ** pipe) | ||||
| { | ||||
|     return async_in_pipe_create(handle, pipe_id, pipe); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_set_timeout( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     uint32_t timeout) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER; | ||||
|  | ||||
|         if (pipe_id & 0x80) | ||||
|         { | ||||
|             handle->in_timeout[endpoint_number] = timeout; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             handle->out_timeout[endpoint_number] = timeout; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_control_transfer( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t bmRequestType, | ||||
|     uint8_t bRequest, | ||||
|     uint16_t wValue, | ||||
|     uint16_t wIndex, | ||||
|     void * data, | ||||
|     uint16_t wLength, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     struct libusbp_setup_packet setup; | ||||
|     setup.bmRequestType = bmRequestType; | ||||
|     setup.bRequest = bRequest; | ||||
|     setup.wValue = wValue; | ||||
|     setup.wIndex = wIndex; | ||||
|     setup.wLength = wLength; | ||||
|  | ||||
|     return usbfd_control_transfer(handle->fd, setup, | ||||
|         handle->out_timeout[0], data, transferred); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_read_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     void * data, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_in(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER; | ||||
|         uint32_t timeout = handle->in_timeout[endpoint_number]; | ||||
|         error = usbfd_bulk_or_interrupt_transfer( | ||||
|             handle->fd, pipe_id, timeout, data, size, transferred); | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to read from pipe."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_write_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     const void * data, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_out(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER; | ||||
|         uint32_t timeout = handle->out_timeout[endpoint_number]; | ||||
|         error = usbfd_bulk_or_interrupt_transfer( | ||||
|             handle->fd, pipe_id, timeout, (void *)data, size, transferred); | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to write to pipe."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| LIBUSBP_WARN_UNUSED | ||||
| static libusbp_error * handle_completed_urb(struct usbdevfs_urb * urb) | ||||
| { | ||||
|     if (urb->usercontext == NULL) | ||||
|     { | ||||
|         return error_create("A completed USB request block has a NULL usercontext."); | ||||
|     } | ||||
|  | ||||
|     if (urb->type == USBDEVFS_URB_TYPE_BULK && (urb->endpoint & 0x80)) | ||||
|     { | ||||
|         async_in_transfer * transfer = urb->usercontext; | ||||
|         async_in_transfer_handle_completion(transfer); | ||||
|         return NULL; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return error_create("A completed USB request block was unrecognized."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * generic_handle_events(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle argument is null."); | ||||
|     } | ||||
|  | ||||
|     while(true) | ||||
|     { | ||||
|         struct usbdevfs_urb * urb; | ||||
|         libusbp_error * error = usbfd_reap_urb(handle->fd, &urb); | ||||
|         if (error != NULL) | ||||
|         { | ||||
|             // There was some problem, like the device being disconnected. | ||||
|             return error; | ||||
|         } | ||||
|  | ||||
|         if (urb == NULL) | ||||
|         { | ||||
|             // No more URBs left to reap. | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         error = handle_completed_urb(urb); | ||||
|         if (error != NULL) | ||||
|         { | ||||
|             return error; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| int libusbp_generic_handle_get_fd(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|     return handle->fd; | ||||
| } | ||||
							
								
								
									
										276
									
								
								dep/libusbp/src/linux/generic_interface_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								dep/libusbp/src/linux/generic_interface_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,276 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_generic_interface | ||||
| { | ||||
|     libusbp_device * device; | ||||
|  | ||||
|     uint8_t interface_number; | ||||
|  | ||||
|     // A sysfs path like "/sys/devices/pci0000:00/0000:00:06.0/usb1/1-2/1-2:1.0" | ||||
|     char * syspath; | ||||
|  | ||||
|     // A filename like "/dev/bus/usb/001/007" | ||||
|     char * filename; | ||||
| }; | ||||
|  | ||||
| libusbp_error * check_driver_installation(struct udev_device * device) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     const char * driver_name = udev_device_get_driver(device); | ||||
|     if (driver_name != NULL && strcmp(driver_name, "usbfs") && strcmp(driver_name, "cp210x")) | ||||
|     { | ||||
|         return error_create("Device is attached to an incorrect driver: %s.", driver_name); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite __attribute__((unused)), | ||||
|     libusbp_generic_interface ** gi) | ||||
| { | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *gi = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the interface. | ||||
|     libusbp_generic_interface * new_gi = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gi = malloc(sizeof(libusbp_generic_interface)); | ||||
|         if (new_gi == NULL) { error = &error_no_memory; } | ||||
|     } | ||||
|  | ||||
|     // Make a copy of the device (since the original device could be freed | ||||
|     // before this generic interface is freed.) | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_copy(device, &new_device); | ||||
|     } | ||||
|  | ||||
|     // Get the syspath of the device. | ||||
|     char * new_device_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_os_id(new_device, &new_device_syspath); | ||||
|     } | ||||
|  | ||||
|     // Get a udev context. | ||||
|     struct udev * new_udev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_context(&new_udev); | ||||
|     } | ||||
|  | ||||
|     // Get the device for the interface. | ||||
|     struct udev_device * new_dev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_interface(new_udev, new_device_syspath, | ||||
|             interface_number, &new_dev); | ||||
|     } | ||||
|  | ||||
|     // Make sure it is not attached to a kernel driver. | ||||
|     // Note: This step might be inappropriate, since libusbp can operate | ||||
|     // on some devices that are attached to a kernel driver, like the cp210x | ||||
|     // driver. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_driver_installation(new_dev); | ||||
|     } | ||||
|  | ||||
|     // Get the syspath of the interface. | ||||
|     char * new_interface_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_syspath_copy(new_dev, &new_interface_syspath); | ||||
|     } | ||||
|  | ||||
|     // Get the filename. | ||||
|     char * new_filename = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_devnode_copy_from_syspath(new_device_syspath, &new_filename); | ||||
|     } | ||||
|  | ||||
|     // Check that the file exists yet, but don't check to see if we have permission | ||||
|     // to open it.  This behavior was chosen to be as similar to the Windows | ||||
|     // behavior as possible. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = usbfd_check_existence(new_filename); | ||||
|     } | ||||
|  | ||||
|     // Assemble the new generic interface and pass it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gi->interface_number = interface_number; | ||||
|  | ||||
|         new_gi->device = new_device; | ||||
|         new_device = NULL; | ||||
|  | ||||
|         new_gi->syspath = new_interface_syspath; | ||||
|         new_interface_syspath = NULL; | ||||
|  | ||||
|         new_gi->filename = new_filename; | ||||
|         new_filename = NULL; | ||||
|  | ||||
|         *gi = new_gi; | ||||
|         new_gi = NULL; | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to initialize generic interface."); | ||||
|     } | ||||
|  | ||||
|     libusbp_string_free(new_filename); | ||||
|     libusbp_string_free(new_interface_syspath); | ||||
|     libusbp_string_free(new_device_syspath); | ||||
|     libusbp_device_free(new_device); | ||||
|     free(new_gi); | ||||
|     if (new_dev != NULL) { udev_device_unref(new_dev); } | ||||
|     if (new_udev != NULL) { udev_unref(new_udev); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_interface_free(libusbp_generic_interface * gi) | ||||
| { | ||||
|     if (gi != NULL) | ||||
|     { | ||||
|         libusbp_device_free(gi->device); | ||||
|         libusbp_string_free(gi->syspath); | ||||
|         libusbp_string_free(gi->filename); | ||||
|         free(gi); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_copy( | ||||
|     const libusbp_generic_interface * source, | ||||
|     libusbp_generic_interface ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the generic interface. | ||||
|     libusbp_generic_interface * new_gi = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gi = malloc(sizeof(libusbp_generic_interface)); | ||||
|         if (new_gi == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Copy the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_copy(source->device, &new_device); | ||||
|     } | ||||
|  | ||||
|     // Copy the syspath. | ||||
|     char * new_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->syspath, &new_syspath); | ||||
|     } | ||||
|  | ||||
|     // Copy the filename. | ||||
|     char * new_filename = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->filename, &new_filename); | ||||
|     } | ||||
|  | ||||
|     // Assemble the new interface and return it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         memcpy(new_gi, source, sizeof(libusbp_generic_interface)); | ||||
|         new_gi->device = new_device; | ||||
|         new_gi->syspath = new_syspath; | ||||
|         new_gi->filename = new_filename; | ||||
|         *dest = new_gi; | ||||
|  | ||||
|         new_filename = NULL; | ||||
|         new_syspath = NULL; | ||||
|         new_device = NULL; | ||||
|         new_gi = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_string_free(new_filename); | ||||
|     libusbp_string_free(new_syspath); | ||||
|     libusbp_device_free(new_device); | ||||
|     free(new_gi); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_id( | ||||
|     const libusbp_generic_interface * gi, char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     assert(gi->syspath != NULL); | ||||
|     return string_copy(gi->syspath, id); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_filename( | ||||
|     const libusbp_generic_interface * gi, char ** filename) | ||||
| { | ||||
|     if (filename == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *filename = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     assert(gi->filename != NULL); | ||||
|     return string_copy(gi->filename, filename); | ||||
| } | ||||
|  | ||||
| libusbp_error * generic_interface_get_device_copy( | ||||
|     const libusbp_generic_interface * gi, libusbp_device ** device) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     assert(device != NULL); | ||||
|     return libusbp_device_copy(gi->device, device); | ||||
| } | ||||
							
								
								
									
										165
									
								
								dep/libusbp/src/linux/list_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								dep/libusbp/src/linux/list_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| // Adds a device to the list, while maintaining the loop invariants for the loop | ||||
| // in libusbp_list_connected_devices. | ||||
| static libusbp_error * add_udev_device_to_list( | ||||
|     struct udev_device * dev, libusbp_device *** device_list, size_t * device_count) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(device_list != NULL); | ||||
|     assert(device_count != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Create a new device. | ||||
|     libusbp_device * new_device; | ||||
|     error = device_create(dev, &new_device); | ||||
|     if (error != NULL) { return error; } | ||||
|  | ||||
|     // Add the device to the list. | ||||
|     error = device_list_append(device_list, device_count, new_device); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         libusbp_device_free(new_device); | ||||
|         return error; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static libusbp_error * add_udev_device_to_list_if_needed( | ||||
|     struct udev * udev, const char * syspath, | ||||
|     libusbp_device *** device_list, size_t * device_count | ||||
| ) | ||||
| { | ||||
|     assert(udev != NULL); | ||||
|     assert(syspath != NULL); | ||||
|     assert(device_list != NULL); | ||||
|     assert(device_count != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Get the udev device. | ||||
|     struct udev_device * dev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_device_from_syspath(udev, syspath, &dev); | ||||
|     } | ||||
|  | ||||
|     // Get the devtype, which is typically usb_device or usb_interface. | ||||
|     const char * devtype = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_device_type(dev, &devtype); | ||||
|     } | ||||
|  | ||||
|     bool skip = false; | ||||
|  | ||||
|     // Skip the interfaces, just add the actual USB devices. | ||||
|     if (error == NULL && !skip && strcmp(devtype, "usb_device") != 0) | ||||
|     { | ||||
|         skip = true; | ||||
|     } | ||||
|  | ||||
|     // Skip devices that have not been initialized yet, because the udev rules | ||||
|     // might not be fully applied.  They might have the wrong permissions. | ||||
|     if (error == NULL && !skip && !udev_device_get_is_initialized(dev)) | ||||
|     { | ||||
|         skip = true; | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && !skip) | ||||
|     { | ||||
|         // This is a USB device, so we do want to add it to the list. | ||||
|         error = add_udev_device_to_list(dev, device_list, device_count); | ||||
|     } | ||||
|  | ||||
|     if (dev != NULL) | ||||
|     { | ||||
|         udev_device_unref(dev); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_list_connected_devices( | ||||
|   libusbp_device *** device_list, size_t * device_count) | ||||
| { | ||||
|     if (device_count != NULL) | ||||
|     { | ||||
|         *device_count = 0; | ||||
|     } | ||||
|  | ||||
|     if (device_list == NULL) | ||||
|     { | ||||
|         return error_create("Device list output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Create a udev context. | ||||
|     struct udev * udev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_context(&udev); | ||||
|     } | ||||
|  | ||||
|     // Create a list of USB devices and interfaces. | ||||
| 	struct udev_enumerate * enumerate = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_usb_list(udev, &enumerate); | ||||
|     } | ||||
|  | ||||
|     // Allocate a new list. | ||||
|     libusbp_device ** new_list = NULL; | ||||
|     size_t count = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_list_create(&new_list); | ||||
|     } | ||||
|  | ||||
|     // Iterate over the devices and add them to the list. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         struct udev_list_entry * first_entry = udev_enumerate_get_list_entry(enumerate); | ||||
|         struct udev_list_entry * list_entry; | ||||
|         udev_list_entry_foreach(list_entry, first_entry) | ||||
|         { | ||||
|             const char * path = udev_list_entry_get_name(list_entry); | ||||
|             assert(path != NULL); | ||||
|  | ||||
|             error = add_udev_device_to_list_if_needed(udev, path, &new_list, &count); | ||||
|             if (error != NULL) | ||||
|             { | ||||
|                 // Something went wrong when getting information about the | ||||
|                 // device.  When unplugging a device, we often see | ||||
|                 // udev_device_new_from_syspath return NULL, which could cause | ||||
|                 // this error.  To make the library more robust and usable, we | ||||
|                 // ignore this error and continue. | ||||
|                 #ifdef LIBUSBP_LOG | ||||
|                 fprintf(stderr, "Problem adding device to list: %s\n", | ||||
|                     libusbp_error_get_message(error)); | ||||
|                 #endif | ||||
|                 libusbp_error_free(error); | ||||
|                 error = NULL; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Pass the list and the count to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device_list = new_list; | ||||
|         new_list = NULL; | ||||
|  | ||||
|         if (device_count != NULL) | ||||
|         { | ||||
|             *device_count = count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Clean up everything we used. | ||||
|     if (enumerate != NULL) { udev_enumerate_unref(enumerate); } | ||||
|     if (udev != NULL) { udev_unref(udev); } | ||||
|     free_devices_and_list(new_list); | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										191
									
								
								dep/libusbp/src/linux/serial_port_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								dep/libusbp/src/linux/serial_port_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_serial_port | ||||
| { | ||||
|     // A sysfs path like "/sys/devices/pci0000:00/0000:00:06.0/usb1/1-2/1-2:1.0/tty/ttyACM0". | ||||
|     // It corresponds to a udev device with subsystem "tty". | ||||
|     char * syspath; | ||||
|  | ||||
|     // A port filename like "/dev/ttyACM0". | ||||
|     char * port_name; | ||||
| }; | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_serial_port ** port) | ||||
| { | ||||
|     LIBUSBP_UNUSED(composite); | ||||
|  | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *port = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_serial_port * new_port = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_port = calloc(1, sizeof(libusbp_serial_port)); | ||||
|         if (new_port == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the syspath of the physical device. | ||||
|     char * new_device_syspath = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_os_id(device, &new_device_syspath); | ||||
|     } | ||||
|  | ||||
|     // Get a udev context. | ||||
|     struct udev * new_udev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_context(&new_udev); | ||||
|     } | ||||
|  | ||||
|     // Get the USB interface device. | ||||
|     struct udev_device * new_interface_dev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_interface(new_udev, new_device_syspath, | ||||
|             interface_number, &new_interface_dev); | ||||
|     } | ||||
|  | ||||
|     // Get the tty device. | ||||
|     struct udev_device * new_tty_dev = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_tty(new_udev, new_interface_dev, &new_tty_dev); | ||||
|     } | ||||
|  | ||||
|     // Get the syspath of the tty device. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_syspath_copy(new_tty_dev, &new_port->syspath); | ||||
|     } | ||||
|  | ||||
|     // Get the port name (e.g. /dev/ttyACM0) | ||||
|     const char * port_name = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         port_name = udev_device_get_property_value(new_tty_dev, "DEVNAME"); | ||||
|         if (port_name == NULL) | ||||
|         { | ||||
|             error = error_create("The DEVNAME property does not exist."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Copy the port name to the new serial port object. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(port_name, &new_port->port_name); | ||||
|     } | ||||
|  | ||||
|     // Pass the new object to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *port = new_port; | ||||
|         new_port = NULL; | ||||
|     } | ||||
|  | ||||
|     if (new_tty_dev != NULL) { udev_device_unref(new_tty_dev); } | ||||
|     if (new_interface_dev != NULL) { udev_device_unref(new_interface_dev); } | ||||
|     if (new_udev != NULL) { udev_unref(new_udev); } | ||||
|     libusbp_string_free(new_device_syspath); | ||||
|     libusbp_serial_port_free(new_port); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_serial_port_free(libusbp_serial_port * port) | ||||
| { | ||||
|     if (port != NULL) | ||||
|     { | ||||
|         libusbp_string_free(port->syspath); | ||||
|         libusbp_string_free(port->port_name); | ||||
|         free(port); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_copy(const libusbp_serial_port * source, | ||||
|     libusbp_serial_port ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the new object. | ||||
|     libusbp_serial_port * new_port = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_port = calloc(1, sizeof(libusbp_serial_port)); | ||||
|         if (new_port == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Copy the syspath. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->syspath, &new_port->syspath); | ||||
|     } | ||||
|  | ||||
|     // Copy the port name. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->port_name, &new_port->port_name); | ||||
|     } | ||||
|  | ||||
|     // Pass the new object to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_port; | ||||
|         new_port = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_serial_port_free(new_port); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_get_name( | ||||
|     const libusbp_serial_port * port, | ||||
|     char ** name) | ||||
| { | ||||
|     if (name == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *name = NULL; | ||||
|  | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port is null."); | ||||
|     } | ||||
|  | ||||
|     return string_copy(port->port_name, name); | ||||
| } | ||||
							
								
								
									
										540
									
								
								dep/libusbp/src/linux/udev_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										540
									
								
								dep/libusbp/src/linux/udev_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,540 @@ | ||||
| /* Wrapper functions for libudev that group together certain operations and | ||||
|  * provide error handling. */ | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| // Creates a new udev context (struct udev *).  If there is no error, the caller | ||||
| // must call udev_unref at some point. | ||||
| libusbp_error * udevw_create_context(struct udev ** context) | ||||
| { | ||||
|     assert(context != NULL); | ||||
|     *context = udev_new(); | ||||
|     if (*context == NULL) | ||||
|     { | ||||
|         return error_create("Failed to create a udev context."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Creates a list (struct udev_enumerate *) of all devices | ||||
| // that are children of a given parent device. | ||||
| static libusbp_error * udevw_create_child_list( | ||||
|     struct udev * udev, | ||||
|     struct udev_device * parent, | ||||
|     struct udev_enumerate ** list) | ||||
| { | ||||
|     assert(udev != NULL); | ||||
|     assert(parent != NULL); | ||||
|     assert(list != NULL); | ||||
|  | ||||
|     *list = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct udev_enumerate * new_list = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_list = udev_enumerate_new(udev); | ||||
|         if (new_list == NULL) | ||||
|         { | ||||
|             error = error_create("Failed to create a udev enumeration context."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         int result = udev_enumerate_add_match_parent(new_list, parent); | ||||
|         if (result != 0) | ||||
|         { | ||||
|             error = error_create_udev(result, "Failed to match by parent device."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         int result = udev_enumerate_scan_devices(new_list); | ||||
|         if (result != 0) | ||||
|         { | ||||
|             error = error_create_udev(result, "Failed to scan devices."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *list = new_list; | ||||
|         new_list = NULL; | ||||
|     } | ||||
|  | ||||
|     if (new_list != NULL) { udev_enumerate_unref(new_list); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Creates a list (struct udev_enumerate *) of all devices in the "usb" | ||||
| // subsystem.  This includes overall USB devices (devtype == "usb_device") and | ||||
| // also interfaces (devtype == "usb_interface").  If there is no error, the | ||||
| // caller must use udev_enumerate_unref at some point. | ||||
| libusbp_error * udevw_create_usb_list(struct udev * udev, struct udev_enumerate ** list) | ||||
| { | ||||
|     assert(udev != NULL); | ||||
|     assert(list != NULL); | ||||
|  | ||||
|     *list = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct udev_enumerate * new_list = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_list = udev_enumerate_new(udev); | ||||
|         if (new_list == NULL) | ||||
|         { | ||||
|             error = error_create("Failed to create a udev enumeration context."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         int result = udev_enumerate_add_match_subsystem(new_list, "usb"); | ||||
|         if (result != 0) | ||||
|         { | ||||
|             error = error_create_udev(result, "Failed to add a subsystem match."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         int result = udev_enumerate_scan_devices(new_list); | ||||
|         if (result != 0) | ||||
|         { | ||||
|             error = error_create_udev(result, "Failed to scan devices."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *list = new_list; | ||||
|         new_list = NULL; | ||||
|     } | ||||
|  | ||||
|     if (new_list != NULL) { udev_enumerate_unref(new_list); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Gets a udev device corresponding to the given syspath.  The syspath is the | ||||
| // unique identifier that we store in order to refer to devices. | ||||
| libusbp_error * udevw_get_device_from_syspath( | ||||
|     struct udev * context, const char * syspath, struct udev_device ** dev) | ||||
| { | ||||
|     assert(context != NULL); | ||||
|     assert(dev != NULL); | ||||
|     assert(syspath != NULL); | ||||
|  | ||||
|     *dev = udev_device_new_from_syspath(context, syspath); | ||||
|     if (*dev == NULL) | ||||
|     { | ||||
|         // This error can happen when the device is being unplugged. | ||||
|         return error_create("Failed to get udev device from syspath: %s.", syspath); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Get the device type of a device, typically "usb_device" or "usb_interface". | ||||
| // The device type is returned as a string that you cannot modify and you do not | ||||
| // have to free; it is owned by the device. | ||||
| libusbp_error * udevw_get_device_type(struct udev_device * dev, const char ** devtype) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(devtype != NULL); | ||||
|  | ||||
|     *devtype = udev_device_get_devtype(dev); | ||||
|     if (*devtype == NULL) | ||||
|     { | ||||
|         return error_create("Failed to get device type."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Gets a sysattr with the specified name and parses it as a hex uint8_t. | ||||
| libusbp_error * udevw_get_sysattr_uint8( | ||||
|     struct udev_device * dev, const char * name, uint8_t * value) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     const char * str = udev_device_get_sysattr_value(dev, name); | ||||
|     if (str == NULL) | ||||
|     { | ||||
|         return error_create("Device does not have sysattr %s.", name); | ||||
|     } | ||||
|  | ||||
|     int result = sscanf(str, "%4hhx\n", value); | ||||
|     if (result != 1) | ||||
|     { | ||||
|         return error_create("Failed to parse sysattr %s.", name); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Gets a sysattr with the specified name and parses it as a hex uint16_t. | ||||
| libusbp_error * udevw_get_sysattr_uint16( | ||||
|     struct udev_device * dev, const char * name, uint16_t * value) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     const char * str = udev_device_get_sysattr_value(dev, name); | ||||
|     if (str == NULL) | ||||
|     { | ||||
|         return error_create("Device does not have sysattr %s.", name); | ||||
|     } | ||||
|  | ||||
|     int result = sscanf(str, "%4hx\n", value); | ||||
|     if (result != 1) | ||||
|     { | ||||
|         return error_create("Failed to parse sysattr %s.", name); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Gets a sysattr string and makes a copy of it.  The string must be freed with | ||||
| // libusbp_string_free().  If the sysattr does not exists, returns a NULL string | ||||
| // instead of raising an error. | ||||
| libusbp_error * udevw_get_sysattr_if_exists_copy( | ||||
|   struct udev_device * dev, const char * name, char ** value) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     *value = NULL; | ||||
|  | ||||
|     const char * str = udev_device_get_sysattr_value(dev, name); | ||||
|     if (str == NULL) { return NULL; } | ||||
|     return string_copy(str, value); | ||||
| } | ||||
|  | ||||
| // Get the USB device of which this device is a child.  This is intended to be | ||||
| // run on devices with devtype == "usb_interface" in order to get information | ||||
| // about the overall USB device. | ||||
| static libusbp_error * udevw_get_parent_usb_device( | ||||
|     struct udev_device * dev, struct udev_device ** parent) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(parent != NULL); | ||||
|     *parent = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); | ||||
|     if (*parent == NULL) | ||||
|     { | ||||
|         return error_create("Failed to get parent USB device."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Helper function for udevw_get_interface. | ||||
| static libusbp_error * udevw_check_if_device_is_specific_interface( | ||||
|     struct udev_device * dev, const char * device_syspath, | ||||
|     uint8_t interface_number, bool * result) | ||||
| { | ||||
|     assert(dev != NULL); | ||||
|     assert(device_syspath != NULL); | ||||
|     assert(result != NULL); | ||||
|  | ||||
|     *result = false; | ||||
|  | ||||
|     libusbp_error * error; | ||||
|  | ||||
|     // Check that the devtype is equal to "usb_interface" | ||||
|     const char * devtype; | ||||
|     error = udevw_get_device_type(dev, &devtype); | ||||
|     if (error != NULL) { return error; } | ||||
|     if (strcmp(devtype, "usb_interface")) { return NULL; } | ||||
|  | ||||
|     // Check that bInterfaceNumber matches our interface number. | ||||
|     uint8_t actual_interface_number; | ||||
|     error = udevw_get_sysattr_uint8(dev, "bInterfaceNumber", &actual_interface_number); | ||||
|     if (error != NULL) { return error; } | ||||
|     if (actual_interface_number != interface_number) { return NULL; } | ||||
|  | ||||
|     // Get the overall USB device. | ||||
|     struct udev_device * parent; | ||||
|     error = udevw_get_parent_usb_device(dev, &parent); | ||||
|     if (error != NULL) { return error; } | ||||
|  | ||||
|     // Check that the overall USB device is the one we are interested in. | ||||
|     const char * parent_syspath = udev_device_get_syspath(parent); | ||||
|     assert(parent_syspath != NULL); | ||||
|     if (strcmp(parent_syspath, device_syspath)) { return NULL; } | ||||
|  | ||||
|     *result = true; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Finds a udev device of type "usb_interface" that is a child of the specified | ||||
| // device and has the specified bInterfaceNumber. | ||||
| libusbp_error * udevw_get_interface( | ||||
|     struct udev * udev, | ||||
|     const char * device_syspath, | ||||
|     uint8_t interface_number, | ||||
|     struct udev_device ** device) | ||||
| { | ||||
|     assert(udev != NULL); | ||||
|     assert(device_syspath != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     *device = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct udev_enumerate * list = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         // Note: It would probably be better to create a list using | ||||
|         // udev_enumerate_add_match_parent so the list is smaller. | ||||
|         error = udevw_create_usb_list(udev, &list); | ||||
|     } | ||||
|  | ||||
|     // Loop over the list to find the device. | ||||
|     struct udev_device * found_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         struct udev_list_entry * first_entry = udev_enumerate_get_list_entry(list); | ||||
|         struct udev_list_entry * list_entry; | ||||
|         udev_list_entry_foreach(list_entry, first_entry) | ||||
|         { | ||||
|             const char * path = udev_list_entry_get_name(list_entry); | ||||
|             assert(path != NULL); | ||||
|  | ||||
|             // Get the udev device. | ||||
|             struct udev_device * dev = NULL; | ||||
|             error = udevw_get_device_from_syspath(udev, path, &dev); | ||||
|             if (error != NULL) { break; } | ||||
|  | ||||
|             // See if it is the right device. | ||||
|             bool correct_device = false; | ||||
|             error = udevw_check_if_device_is_specific_interface(dev, device_syspath, | ||||
|                 interface_number, &correct_device); | ||||
|             if (error != NULL) | ||||
|             { | ||||
|                 udev_device_unref(dev); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // If it is the right device, stop looping. | ||||
|             if (correct_device) | ||||
|             { | ||||
|                 found_device = dev; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             udev_device_unref(dev); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Make sure we found the device. | ||||
|     if (error == NULL && found_device == NULL) | ||||
|     { | ||||
|         // We did not find it.  Maybe the interface is just not ready yet and it | ||||
|         // would be ready in a few milliseconds.  (Note: We have not seen this | ||||
|         // happen, but adding the error code here makes the behavior consistent | ||||
|         // with how the Windows code behaves.) | ||||
|         error = error_create("Could not find interface %d.", interface_number); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|     } | ||||
|  | ||||
|     // Pass the device back to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device = found_device; | ||||
|     } | ||||
|  | ||||
|     if (list != NULL) { udev_enumerate_unref(list); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Finds a udev device of type "usb_interface" that is a child of the specified | ||||
| // device and has the specified bInterfaceNumber. | ||||
| libusbp_error * udevw_get_tty( | ||||
|     struct udev * udev, | ||||
|     struct udev_device * parent, | ||||
|     struct udev_device ** device) | ||||
| { | ||||
|     assert(udev != NULL); | ||||
|     assert(parent != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     *device = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct udev_enumerate * list = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_child_list(udev, parent, &list); | ||||
|     } | ||||
|  | ||||
|     // Loop over the list to find the device. | ||||
|     struct udev_device * found_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         struct udev_list_entry * first_entry = udev_enumerate_get_list_entry(list); | ||||
|         struct udev_list_entry * list_entry; | ||||
|         udev_list_entry_foreach(list_entry, first_entry) | ||||
|         { | ||||
|             const char * path = udev_list_entry_get_name(list_entry); | ||||
|             assert(path != NULL); | ||||
|  | ||||
|             // Get the udev device. | ||||
|             struct udev_device * dev = NULL; | ||||
|             error = udevw_get_device_from_syspath(udev, path, &dev); | ||||
|             if (error != NULL) { break; } | ||||
|  | ||||
|             // See if it is in the "tty" subsystem. | ||||
|             bool correct_device = false; | ||||
|             const char * subsystem = udev_device_get_subsystem(dev); | ||||
|             if (subsystem != NULL && 0 == strcmp(subsystem, "tty")) | ||||
|             { | ||||
|                 correct_device = true; | ||||
|             } | ||||
|  | ||||
|             // If it is the right device, stop looping. | ||||
|             if (correct_device) | ||||
|             { | ||||
|                 found_device = dev; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             udev_device_unref(dev); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Make sure we found the device. | ||||
|     if (error == NULL && found_device == NULL) | ||||
|     { | ||||
|         // We did not find it.  Maybe the interface is just not ready yet and it | ||||
|         // would be ready in a few milliseconds.  (Note: We have not seen this | ||||
|         // happen, but adding the error code here makes the behavior consistent | ||||
|         // with how the Windows code behaves.) | ||||
|         error = error_create("Could not find tty device."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|     } | ||||
|  | ||||
|     // Pass the device back to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device = found_device; | ||||
|     } | ||||
|  | ||||
|     if (list != NULL) { udev_enumerate_unref(list); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Gets the devnode path of the specified udev device.  The returned string is | ||||
| // owned by the device. | ||||
| libusbp_error * udevw_get_syspath(struct udev_device * device, const char ** syspath) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     assert(syspath != NULL); | ||||
|  | ||||
|     *syspath = udev_device_get_syspath(device); | ||||
|     assert(*syspath != NULL); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Gets the syspath of the specified udev device.  The returned string must | ||||
| // be freed with libusbp_string_free. | ||||
| libusbp_error * udevw_get_syspath_copy(struct udev_device * device, char ** syspath) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     assert(syspath != NULL); | ||||
|  | ||||
|     *syspath = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     const char * tmp = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_syspath(device, &tmp); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(tmp, syspath); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Gets the devnode path of the specified udev device.  The returned string is | ||||
| // owned by the device. | ||||
| libusbp_error * udevw_get_devnode(struct udev_device * device, const char ** devnode) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     assert(devnode != NULL); | ||||
|  | ||||
|     *devnode = udev_device_get_devnode(device); | ||||
|     if (*devnode == NULL) | ||||
|     { | ||||
|         return error_create("No device node exists."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| // Gets the devnode of the specified udev device.  The returned string must | ||||
| // be freed with libusbp_string_free. | ||||
| libusbp_error * udevw_get_devnode_copy(struct udev_device * device, char ** devnode) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     assert(devnode != NULL); | ||||
|  | ||||
|     *devnode = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     const char * tmp = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_devnode(device, &tmp); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(tmp, devnode); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Takes a syspath as input and gets the corresponding devpath.  The returned | ||||
| // string must be freed with libusbp_string_free if there were no errors. | ||||
| libusbp_error * udevw_get_devnode_copy_from_syspath(const char * syspath, char ** devnode) | ||||
| { | ||||
|     assert(syspath != NULL); | ||||
|     assert(devnode != NULL); | ||||
|  | ||||
|     *devnode = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     struct udev * context = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_create_context(&context); | ||||
|     } | ||||
|  | ||||
|     struct udev_device * device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_device_from_syspath(context, syspath, &device); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = udevw_get_devnode_copy(device, devnode); | ||||
|     } | ||||
|  | ||||
|     if (device != NULL) { udev_device_unref(device); } | ||||
|     if (context != NULL) { udev_unref(context); } | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										222
									
								
								dep/libusbp/src/linux/usbfd_linux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								dep/libusbp/src/linux/usbfd_linux.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| /* Functions for working with a USB device node file | ||||
|  * (e.g. "/dev/bus/usb/001/002"). */ | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * usbfd_check_existence(const char * filename) | ||||
| { | ||||
|     libusbp_error * error = NULL; | ||||
|     int result = access(filename, F_OK); | ||||
|     if (result != 0) | ||||
|     { | ||||
|         if (errno == ENOENT) | ||||
|         { | ||||
|             // The file does not exist.  This might just be a temporary | ||||
|             // condition, so use the LIBUSBP_ERROR_NOT_READY code. | ||||
|             error = error_create("File does not exist: %s.", filename); | ||||
|             error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Something actually went wrong when checking if the file | ||||
|             // exists. | ||||
|             error = error_create_errno("Failed to check file: %s.", filename); | ||||
|         } | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * usbfd_open(const char * filename, int * fd) | ||||
| { | ||||
|     assert(filename != NULL); | ||||
|     assert(fd != NULL); | ||||
|  | ||||
|     *fd = open(filename, O_RDWR | O_CLOEXEC); | ||||
|     if (*fd == -1) | ||||
|     { | ||||
|         libusbp_error * error = error_create_errno("Failed to open USB device %s.", filename); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Reads the device descriptor.  This is not thread-safe, because it is possible | ||||
| // that another thread might change the position of the file descriptor after | ||||
| // lseek() and before read(). | ||||
| // | ||||
| // The kernel code that provides the device descriptor can be found in | ||||
| // usbdev_read() in devio.c. | ||||
| libusbp_error * usbfd_get_device_descriptor(int fd, struct usb_device_descriptor * desc) | ||||
| { | ||||
|     assert(desc != NULL); | ||||
|  | ||||
|     // Seek to the beginning of the file, where the device descriptor lives. | ||||
|     off_t offset = lseek(fd, 0, SEEK_SET); | ||||
|     if (offset == -1) | ||||
|     { | ||||
|         return error_create_errno("Failed to go to beginning of USB device file."); | ||||
|     } | ||||
|  | ||||
|     ssize_t expected_size = sizeof(struct usb_device_descriptor); | ||||
|     ssize_t size = read(fd, desc, expected_size); | ||||
|     if (size == -1) | ||||
|     { | ||||
|         return error_create_errno("Failed to read device descriptor."); | ||||
|     } | ||||
|     if (size != expected_size) | ||||
|     { | ||||
|         return error_create("Failed to read device descriptor.  " | ||||
|             "Expected %zd-byte device descriptor, read %zd bytes.", | ||||
|             expected_size, size); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * usbfd_control_transfer(int fd, libusbp_setup_packet setup, | ||||
|     uint32_t timeout, void * data, size_t * transferred) | ||||
| { | ||||
|     struct usbdevfs_ctrltransfer transfer = {0}; | ||||
|     transfer.bRequestType = setup.bmRequestType; | ||||
|     transfer.bRequest = setup.bRequest; | ||||
|     transfer.wValue = setup.wValue; | ||||
|     transfer.wIndex = setup.wIndex; | ||||
|     transfer.wLength = setup.wLength; | ||||
|     transfer.timeout = timeout; | ||||
|     transfer.data = data; | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     int result = ioctl(fd, USBDEVFS_CONTROL, &transfer); | ||||
|     if (result < 0) | ||||
|     { | ||||
|         return error_create_errno("Control transfer failed."); | ||||
|     } | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = result; | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* Performs a bulk or interrupt transfer on the specified endpoint. | ||||
|  * | ||||
|  * Despite the name, USBDEVFS_BULK does actually work for interrupt endpoints. | ||||
|  * The function usbdev_do_ioctl in devio.c calls proc_bulk in devio.c, which | ||||
|  * calls usb_bulk_msg in message.c, which explicitly detects if it was called on | ||||
|  * an interrupt endpoint and handle that situation properly. */ | ||||
| libusbp_error * usbfd_bulk_or_interrupt_transfer(int fd, uint8_t pipe, | ||||
|     uint32_t timeout, void * buffer, size_t size, size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     // A buffer size of 0 for IN transfers (or at least interrupt IN | ||||
|     // transfers in a VirtualBox Linux guest running on a Windows | ||||
|     // host) seems to put the Linux USB drivers in some weird state | ||||
|     // where every subsequent request times out. | ||||
|     if (size == 0 && (pipe & 0x80)) | ||||
|     { | ||||
|         return error_create("Transfer size 0 is not allowed."); | ||||
|     } | ||||
|  | ||||
|     // A size greater than UINT_MAX will not fit into the struct below. | ||||
|     if (size > UINT_MAX) | ||||
|     { | ||||
|         return error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     if (buffer == NULL && size) | ||||
|     { | ||||
|         return error_create("Buffer is null."); | ||||
|     } | ||||
|  | ||||
|     struct usbdevfs_bulktransfer transfer = {0}; | ||||
|     transfer.ep = pipe; | ||||
|     transfer.len = size; | ||||
|     transfer.timeout = timeout; | ||||
|     transfer.data = buffer; | ||||
|  | ||||
|     int result = ioctl(fd, USBDEVFS_BULK, &transfer); | ||||
|     if (result < 0) | ||||
|     { | ||||
|         return error_create_errno(""); | ||||
|     } | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = result; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * usbfd_submit_urb(int fd, struct usbdevfs_urb * urb) | ||||
| { | ||||
|     assert(urb != NULL); | ||||
|  | ||||
|     int result = ioctl(fd, USBDEVFS_SUBMITURB, urb); | ||||
|     if (result < 0) | ||||
|     { | ||||
|         return error_create_errno("Submitting USB request block failed."); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /*! Checks to see if there is a finished asynchronous request.  If there is, | ||||
|  * this function "reaps" the request and retrieves a pointer to its URB.  The | ||||
|  * kernel writes to the URB and its associated buffer when you call this | ||||
|  * function, allowing you to get the results of the operation. | ||||
|  * | ||||
|  * If nothing is available to be reaped at the moment, the retrieved URB pointer | ||||
|  * will be NULL. | ||||
|  * | ||||
|  * Note: For Linux kernels older than 4.0, this function will return an error | ||||
|  * if the USB device happens to be disconnected.  In 4.0 and later, you will be | ||||
|  * able to reap URBs from disconnected devices thanks to commit 3f2cee73b from | ||||
|  * Alan Stern on 2015-01-29. */ | ||||
| libusbp_error * usbfd_reap_urb(int fd, struct usbdevfs_urb ** urb) | ||||
| { | ||||
|     assert(urb != NULL); | ||||
|  | ||||
|     int result = ioctl(fd, USBDEVFS_REAPURBNDELAY, urb); | ||||
|     if (result < 0) | ||||
|     { | ||||
|         *urb = NULL; | ||||
|  | ||||
|         if (errno == EAGAIN) | ||||
|         { | ||||
|             // No URBs are available to be reaped right now. | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         return error_create_errno("Failed to reap an asynchronous transfer."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /*! Cancels an URB that was already submitted. */ | ||||
| libusbp_error * usbfd_discard_urb(int fd, struct usbdevfs_urb * urb) | ||||
| { | ||||
|     assert(urb != NULL); | ||||
|  | ||||
|     int result = ioctl(fd, USBDEVFS_DISCARDURB, urb); | ||||
|     if (result < 0) | ||||
|     { | ||||
|         if (errno == EINVAL) | ||||
|         { | ||||
|             // This error code happens if the URB was already completed.  This | ||||
|             // is not an error. | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         return error_create_errno("Failed to cancel asynchronous transfer."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
							
								
								
									
										72
									
								
								dep/libusbp/src/list.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								dep/libusbp/src/list.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /** This file provides libusbp_list_free as well as internal functions that help | ||||
|  * construct lists of devices. | ||||
|  * | ||||
|  * At all times, a list maintained by these functions will be NULL terminated. | ||||
|  */ | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * device_list_create(libusbp_device *** device_list) | ||||
| { | ||||
|     assert(device_list != NULL); | ||||
|  | ||||
|     *device_list = NULL; | ||||
|  | ||||
|     libusbp_device ** new_list = malloc(sizeof(libusbp_device *)); | ||||
|     if (new_list == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     new_list[0] = NULL; | ||||
|  | ||||
|     *device_list = new_list; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * device_list_append(libusbp_device *** device_list, | ||||
|     size_t * count, libusbp_device * device) | ||||
| { | ||||
|     assert(device_list != NULL); | ||||
|     assert(count != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     size_t new_count = *count + 1; | ||||
|     libusbp_device ** expanded_list = realloc(*device_list, | ||||
|         (new_count + 1) * sizeof(libusbp_device *)); | ||||
|  | ||||
|     if (expanded_list == NULL) | ||||
|     { | ||||
|         // Expanding the list failed, so we return an error and leave the | ||||
|         // list in its original state. | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     expanded_list[new_count - 1] = device; | ||||
|     expanded_list[new_count] = NULL; | ||||
|  | ||||
|     *count = new_count; | ||||
|     *device_list = expanded_list; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void free_devices_and_list(libusbp_device ** device_list) | ||||
| { | ||||
|     if (device_list != NULL) | ||||
|     { | ||||
|         libusbp_device ** device = device_list; | ||||
|         while(*device != NULL) | ||||
|         { | ||||
|             libusbp_device_free(*device); | ||||
|             device++; | ||||
|         } | ||||
|         libusbp_list_free(device_list); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void libusbp_list_free(libusbp_device ** device_list) | ||||
| { | ||||
|     if (device_list == NULL) { return; } | ||||
|  | ||||
|     free(device_list); | ||||
| } | ||||
							
								
								
									
										188
									
								
								dep/libusbp/src/mac/async_in_transfer_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								dep/libusbp/src/mac/async_in_transfer_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct async_in_transfer | ||||
| { | ||||
|     IOUSBInterfaceInterface182 ** ioh; | ||||
|     uint8_t pipe_index; | ||||
|     void * buffer; | ||||
|     uint32_t size; | ||||
|     bool pending; | ||||
|     size_t transferred; | ||||
|     libusbp_error * error; | ||||
| }; | ||||
|  | ||||
| static void async_in_transfer_callback(void * context, kern_return_t kr, void * arg0) | ||||
| { | ||||
|     #ifdef LIBUSBP_LOG | ||||
|     printf("async_in_transfer_callback (%p): %p, %#x, %p, %s\n", | ||||
|         async_in_transfer_callback, context, kr, arg0, mach_error_string(kr)); | ||||
|     #endif | ||||
|  | ||||
|     async_in_transfer * transfer = (async_in_transfer *)context; | ||||
|     assert(transfer != NULL); | ||||
|     assert(transfer->pending); | ||||
|     assert(transfer->error == NULL); | ||||
|     assert(transfer->transferred == 0); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         error = error_create_mach(kr, "Asynchronous IN transfer failed."); | ||||
|     } | ||||
|  | ||||
|     transfer->transferred = (size_t)arg0; | ||||
|  | ||||
|     transfer->pending = false; | ||||
|     transfer->error = error; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_create( | ||||
|     libusbp_generic_handle * handle, uint8_t pipe_id, size_t transfer_size, | ||||
|     async_in_transfer ** transfer) | ||||
| { | ||||
|     assert(handle != NULL); | ||||
|  | ||||
|     if (transfer_size > UINT32_MAX) | ||||
|     { | ||||
|         return error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the transfer struct. | ||||
|     async_in_transfer * new_transfer = calloc(1, sizeof(async_in_transfer)); | ||||
|  | ||||
|     if (new_transfer == NULL) | ||||
|     { | ||||
|         error = &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Allocate memory for the buffer. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer->size = transfer_size; | ||||
|         new_transfer->buffer = malloc(transfer_size); | ||||
|         if (new_transfer->buffer == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get needed information from the generic handle. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer->ioh = generic_handle_get_ioh(handle); | ||||
|         new_transfer->pipe_index = generic_handle_get_pipe_index(handle, pipe_id); | ||||
|     } | ||||
|  | ||||
|     // Pass the transfer to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *transfer = new_transfer; | ||||
|         new_transfer = NULL; | ||||
|     } | ||||
|  | ||||
|     async_in_transfer_free(new_transfer); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_pipe_setup(libusbp_generic_handle * gh, uint8_t pipe_id) | ||||
| { | ||||
|     LIBUSBP_UNUSED(gh); | ||||
|     LIBUSBP_UNUSED(pipe_id); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void async_in_transfer_free(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) { return; } | ||||
|  | ||||
|     if (transfer->pending) | ||||
|     { | ||||
|         // Unfortunately, this transfer is still pending, so we cannot free it; | ||||
|         // the kernel needs to be able to write to this transfer's memory when | ||||
|         // it completes.  We could just abort the process here, but instead we | ||||
|         // we choose to have a memory leak. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     free(transfer->buffer); | ||||
|     free(transfer); | ||||
| } | ||||
|  | ||||
| void async_in_transfer_submit(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(transfer->pending == false); | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     transfer->error = NULL; | ||||
|     transfer->transferred = 0; | ||||
|     transfer->pending = true; | ||||
|  | ||||
|     kern_return_t kr = (*transfer->ioh)->ReadPipeAsync(transfer->ioh, | ||||
|         transfer->pipe_index, transfer->buffer, transfer->size, | ||||
|         async_in_transfer_callback, transfer); | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         transfer->pending = false; | ||||
|         transfer->error = error_create_mach(kr, "Failed to submit asynchronous read transfer."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_get_results(async_in_transfer * transfer, | ||||
|     void * buffer, size_t * transferred, libusbp_error ** transfer_error) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(transfer->pending == false); | ||||
|  | ||||
|     size_t tmp_transferred = transfer->transferred; | ||||
|  | ||||
|     // Make sure we don't overflow the user's buffer. | ||||
|     if (tmp_transferred > transfer->size) | ||||
|     { | ||||
|         assert(0); | ||||
|         tmp_transferred = transfer->size; | ||||
|     } | ||||
|  | ||||
|     if (buffer != NULL) | ||||
|     { | ||||
|         memcpy(buffer, transfer->buffer, tmp_transferred); | ||||
|     } | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = tmp_transferred; | ||||
|     } | ||||
|  | ||||
|     if (transfer_error != NULL) | ||||
|     { | ||||
|         *transfer_error = libusbp_error_copy(transfer->error); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // This cancels all of the transfers for the whole pipe.  It is not possible to | ||||
| // cancel an individual transfer on Mac OS X, which is one of the reasons | ||||
| // individual transfers are not provided as first-class objects by the libusbp | ||||
| // API. | ||||
| libusbp_error * async_in_transfer_cancel(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) { return NULL; } | ||||
|  | ||||
|     kern_return_t kr = (*transfer->ioh)->AbortPipe(transfer->ioh, transfer->pipe_index); | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         return error_create_mach(kr, "Failed to cancel transfers."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| bool async_in_transfer_pending(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     return transfer->pending; | ||||
| } | ||||
							
								
								
									
										233
									
								
								dep/libusbp/src/mac/device_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								dep/libusbp/src/mac/device_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,233 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_device | ||||
| { | ||||
|     uint64_t id; | ||||
|     uint16_t product_id; | ||||
|     uint16_t vendor_id; | ||||
|     uint16_t revision; | ||||
|     char * serial_number; | ||||
| }; | ||||
|  | ||||
| static libusbp_error * device_allocate(libusbp_device ** device) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     *device = calloc(1, sizeof(libusbp_device)); | ||||
|     if (*device == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * create_device(io_service_t service, libusbp_device ** device) | ||||
| { | ||||
|     assert(service != MACH_PORT_NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     // Allocate the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     libusbp_error * error = device_allocate(&new_device); | ||||
|  | ||||
|     // Get the numeric IDs. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_uint16(service, CFSTR(kUSBVendorID), &new_device->vendor_id); | ||||
|     } | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_uint16(service, CFSTR(kUSBProductID), &new_device->product_id); | ||||
|     } | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_uint16(service, CFSTR(kUSBDeviceReleaseNumber), &new_device->revision); | ||||
|     } | ||||
|  | ||||
|     // Get the serial number. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_string(service, CFSTR(kUSBSerialNumberString), &new_device->serial_number); | ||||
|     } | ||||
|  | ||||
|     // Get the ID. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_id(service, &new_device->id); | ||||
|     } | ||||
|  | ||||
|     // Pass the device to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device = new_device; | ||||
|         new_device = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_device_free(new_device); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_copy(const libusbp_device * source, libusbp_device ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Device output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate the device. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     error = device_allocate(&new_device); | ||||
|  | ||||
|     // Copy the simple fields, while leaving the pointers owned by the | ||||
|     // device NULL so that libusbp_device_free is still OK to call. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         memcpy(new_device, source, sizeof(libusbp_device)); | ||||
|         new_device->serial_number = NULL; | ||||
|     } | ||||
|  | ||||
|     // Copy the serial number. | ||||
|     if (error == NULL && source->serial_number != NULL) | ||||
|     { | ||||
|         error = string_copy(source->serial_number, &new_device->serial_number); | ||||
|     } | ||||
|  | ||||
|     // Pass the device to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_device; | ||||
|         new_device = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_device_free(new_device); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_device_free(libusbp_device * device) | ||||
| { | ||||
|     if (device != NULL) | ||||
|     { | ||||
|         libusbp_string_free(device->serial_number); | ||||
|         free(device); | ||||
|     } | ||||
| } | ||||
|  | ||||
| uint64_t device_get_id(const libusbp_device * device) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     return device->id; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_vendor_id( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * vendor_id) | ||||
| { | ||||
|     if (vendor_id == NULL) | ||||
|     { | ||||
|         return error_create("Vendor ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = device->vendor_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| libusbp_error * libusbp_device_get_product_id( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * product_id) | ||||
| { | ||||
|     if (product_id == NULL) | ||||
|     { | ||||
|         return error_create("Product ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = device->product_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_revision( | ||||
|     const libusbp_device * device, | ||||
|     uint16_t * revision) | ||||
| { | ||||
|     if (revision == NULL) | ||||
|     { | ||||
|         return error_create("Device revision output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = device->revision; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_serial_number( | ||||
|     const libusbp_device * device, | ||||
|     char ** serial_number) | ||||
| { | ||||
|     if (serial_number == NULL) | ||||
|     { | ||||
|         return error_create("Serial number output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *serial_number = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     if (device->serial_number == NULL) | ||||
|     { | ||||
|         libusbp_error * error = error_create("Device does not have a serial number."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NO_SERIAL_NUMBER); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     return string_copy(device->serial_number, serial_number); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_os_id( | ||||
|     const libusbp_device * device, | ||||
|     char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("Device OS ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     return iokit_id_to_string(device->id, id); | ||||
| } | ||||
							
								
								
									
										61
									
								
								dep/libusbp/src/mac/error_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								dep/libusbp/src/mac/error_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * error_create_mach(kern_return_t error_code, const char * format, ...) | ||||
| { | ||||
|     // Add the numerical error code. | ||||
|     libusbp_error * error = error_create("Error code 0x%x.", error_code); | ||||
|  | ||||
|     bool skip_standard_message = false; | ||||
|  | ||||
|     switch(error_code) | ||||
|     { | ||||
|     case kIOUSBPipeStalled: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The request was invalid or there was an I/O problem."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_STALL); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|  | ||||
|     case kIOReturnNoDevice: | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|  | ||||
|     case kIOUSBTransactionTimeout: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The operation timed out."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_TIMEOUT); | ||||
|         break; | ||||
|  | ||||
|     case kIOReturnExclusiveAccess: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, | ||||
|             "Access is denied.  Try closing all other programs that are using the device."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_ACCESS_DENIED); | ||||
|         break; | ||||
|  | ||||
|     case kIOReturnOverrun: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The transfer overflowed."); | ||||
|         break; | ||||
|  | ||||
|     case kIOReturnAborted: | ||||
|         skip_standard_message = true; | ||||
|         error = error_add(error, "The operation was cancelled."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_CANCELLED); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (!skip_standard_message) | ||||
|     { | ||||
|         // Add a message from the system. | ||||
|         error = error_add(error, "%s.", mach_error_string(error_code)); | ||||
|     } | ||||
|  | ||||
|     // Finally, add the context provided by the caller. | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										600
									
								
								dep/libusbp/src/mac/generic_handle_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										600
									
								
								dep/libusbp/src/mac/generic_handle_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,600 @@ | ||||
| #include <libusbp_internal.h> | ||||
| #include <mach/mach_time.h> | ||||
|  | ||||
| struct libusbp_generic_handle | ||||
| { | ||||
|     // ioh is short for "IOKit Handle" | ||||
|     IOUSBInterfaceInterface182 ** ioh; | ||||
|  | ||||
|     IOCFPlugInInterface ** plug_in; | ||||
|  | ||||
|     mach_port_t async_port; | ||||
|  | ||||
|     // Timeouts are stored in milliseconds.  0 is forever. | ||||
|     uint32_t out_timeout[MAX_ENDPOINT_NUMBER + 1]; | ||||
|     uint32_t in_timeout[MAX_ENDPOINT_NUMBER + 1]; | ||||
|  | ||||
|     // These arrays allow us to convert from a normal USB endpoint address like | ||||
|     // 0x82 (Endpoint 2 IN) to the pipe index needed by IOUSBInterface functions. | ||||
|     uint8_t out_pipe_index[MAX_ENDPOINT_NUMBER + 1]; | ||||
|     uint8_t in_pipe_index[MAX_ENDPOINT_NUMBER + 1]; | ||||
| }; | ||||
|  | ||||
| #ifdef LIBUSBP_LOG | ||||
| void log_mach_msg(mach_msg_return_t mr, mach_msg_header_t * header) | ||||
| { | ||||
|     uint64_t t = mach_absolute_time(); | ||||
|     size_t main_payload_size = header->msgh_size - sizeof(mach_msg_header_t); | ||||
|     assert(main_payload_size < 1024);  // avoid buffer overruns | ||||
|     uint8_t * payload = (uint8_t *)(header + 1); | ||||
|     mach_msg_trailer_t * trailer = (mach_msg_trailer_t *)(payload + main_payload_size); | ||||
|     size_t payload_size = main_payload_size + trailer->msgh_trailer_size; | ||||
|     assert(payload_size < 1024);  // avoid buffer overruns | ||||
|  | ||||
|     printf("mach_msg at %lld: %#x, %s, %d\n", t, mr, mach_error_string(mr), header->msgh_size); | ||||
|     printf("  bits: %#x\n", header->msgh_bits); | ||||
|     printf("  ports: %d, %d, %d\n", header->msgh_remote_port, | ||||
|         header->msgh_local_port, header->msgh_voucher_port); | ||||
|     printf("  id: %d\n", header->msgh_id); | ||||
|     for (size_t i = 0; i < payload_size; i++) | ||||
|     { | ||||
|         if ((i % 8) == 0) { printf("    "); } | ||||
|         printf("%02x ", payload[i]); | ||||
|         if ((i % 8) == 7 || i == payload_size - 1) { printf("\n"); } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| // Gets the properties of all the pipes and uses that to populate the | ||||
| // out_pipe_index and in_pipe_index arrays. | ||||
| static libusbp_error * process_pipe_properties(libusbp_generic_handle * handle) | ||||
| { | ||||
|     uint8_t endpoint_count; | ||||
|     kern_return_t kr = (*handle->ioh)->GetNumEndpoints(handle->ioh, &endpoint_count); | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         return error_create_mach(kr, "Failed to get number of endpoints."); | ||||
|     } | ||||
|  | ||||
|     for(uint32_t i = 1; i <= endpoint_count; i++) | ||||
|     { | ||||
|         uint8_t direction; | ||||
|         uint8_t endpoint_number; | ||||
|         uint8_t transfer_type; | ||||
|         uint16_t max_packet_size; | ||||
|         uint8_t interval; | ||||
|         kr = (*handle->ioh)->GetPipeProperties(handle->ioh, (UInt8) i, | ||||
|           &direction, &endpoint_number, &transfer_type, &max_packet_size, &interval); | ||||
|  | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             return error_create_mach(kr, "Failed to get pipe properties for pipe %d.", i); | ||||
|         } | ||||
|  | ||||
|         if (endpoint_number <= MAX_ENDPOINT_NUMBER) | ||||
|         { | ||||
|             if (direction) | ||||
|             { | ||||
|                 handle->in_pipe_index[endpoint_number] = (uint8_t) i; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 handle->out_pipe_index[endpoint_number] = (uint8_t) i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Sets the configruation of a device to 1 if it is not configured. | ||||
| static libusbp_error * set_configuration(io_service_t service) | ||||
| { | ||||
|     assert(service != MACH_PORT_NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Turn io_service_t into something we can actually use. | ||||
|     IOUSBDeviceInterface ** dev_handle = NULL; | ||||
|     IOCFPlugInInterface ** plug_in = NULL; | ||||
|     error = service_to_interface(service, | ||||
|         kIOUSBDeviceUserClientTypeID, | ||||
|         CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID197), | ||||
|         (void **)&dev_handle, | ||||
|         &plug_in); | ||||
|  | ||||
|     uint8_t config_num = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         kern_return_t kr = (*dev_handle)->GetConfiguration(dev_handle, &config_num); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             // We failed to get the current configuration.  The documentation of | ||||
|             // GetConfiguration doesn't state whether it actually does I/O on | ||||
|             // the device or not, but if it does do I/O then one possible reason | ||||
|             // for GetConfiguration to fail is that the device simply doesn't | ||||
|             // support the request.  Let's just assume the device is not | ||||
|             // configured and set it to configuration 1. | ||||
|             config_num = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Open the device for exclusive access. | ||||
|     if (error == NULL && config_num == 0) | ||||
|     { | ||||
|         kern_return_t kr = (*dev_handle)->USBDeviceOpen(dev_handle); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, "Failed to open handle to device."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Set the configuration. | ||||
|     if (error == NULL && config_num == 0) | ||||
|     { | ||||
|         uint8_t new_config_num = 1; | ||||
|         kern_return_t kr = (*dev_handle)->SetConfiguration(dev_handle, new_config_num); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, "Failed to set configuration to 1."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     if (dev_handle != NULL) | ||||
|     { | ||||
|         (*dev_handle)->USBDeviceClose(dev_handle); | ||||
|         (*dev_handle)->Release(dev_handle); | ||||
|         dev_handle = NULL; | ||||
|         (*plug_in)->Release(plug_in); | ||||
|         plug_in = NULL; | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Sets the configruation of the device to 1 if it is not set, and then | ||||
| // retrieves the io_service_t representing the specific interface we want | ||||
| // to talk to. | ||||
| static libusbp_error * set_configuration_and_get_service( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     io_service_t * service) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     assert(service != NULL); | ||||
|     *service = MACH_PORT_NULL; | ||||
|  | ||||
|     uint64_t device_id = generic_interface_get_device_id(gi); | ||||
|     uint8_t interface_number = generic_interface_get_interface_number(gi); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Get an io_service_t for the physical device. | ||||
|     io_service_t device_service = MACH_PORT_NULL; | ||||
|     error = service_get_from_id(device_id, &device_service); | ||||
|  | ||||
|     // Set the configruation to 1 if it is not set. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = set_configuration(device_service); | ||||
|     } | ||||
|  | ||||
|     // Get the io_service_t for the interface. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = service_get_usb_interface(device_service, interface_number, service); | ||||
|     } | ||||
|  | ||||
|     if (device_service != MACH_PORT_NULL) { IOObjectRelease(device_service); } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     libusbp_generic_handle ** handle) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *handle = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the handle. | ||||
|     libusbp_generic_handle * new_handle = NULL; | ||||
|     new_handle = calloc(1, sizeof(libusbp_generic_handle)); | ||||
|  | ||||
|     if (new_handle == NULL) | ||||
|     { | ||||
|         error = &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Get the io_service_t representing the IOUSBInterface. | ||||
|     io_service_t service = MACH_PORT_NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint64_t interface_id; | ||||
|         bool has_interface_id = generic_interface_get_interface_id(gi, &interface_id); | ||||
|         if (has_interface_id) | ||||
|         { | ||||
|             // This generic interface has an I/O Registry ID for a specific USB interface, | ||||
|             // so lets just get the corresponding io_service_t. | ||||
|             error = service_get_from_id(interface_id, &service); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // This generic interface does not have an ID for the specific USB interface yet, | ||||
|             // probably because it is a non-composite device and we need to put it into the | ||||
|             // right configuration. | ||||
|  | ||||
|             error = set_configuration_and_get_service(gi, &service); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the IOInterfaceInterface | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = service_to_interface(service, | ||||
|             kIOUSBInterfaceUserClientTypeID, | ||||
|             CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID182), | ||||
|             (void **)&new_handle->ioh, | ||||
|             &new_handle->plug_in); | ||||
|     } | ||||
|  | ||||
|     // Open the interface for exclusive access. | ||||
|     // (Otherwise, we can't read from non-zero pipes.) | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         kern_return_t kr = (*new_handle->ioh)->USBInterfaceOpen(new_handle->ioh); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, "Failed to open generic handle."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Retrieve and store important information from the pipe properties. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = process_pipe_properties(new_handle); | ||||
|     } | ||||
|  | ||||
|     // Create the async port. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         kern_return_t kr = (*new_handle->ioh)->CreateInterfaceAsyncPort(new_handle->ioh, | ||||
|             &new_handle->async_port); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, "Failed to create asynchronous port."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Pass the handle to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *handle = new_handle; | ||||
|         new_handle = NULL; | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     libusbp_generic_handle_close(new_handle); | ||||
|     if (service != MACH_PORT_NULL) { IOObjectRelease(service); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_handle_close(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle != NULL) | ||||
|     { | ||||
|         if (handle->ioh != NULL) | ||||
|         { | ||||
|             (*handle->ioh)->USBInterfaceClose(handle->ioh); | ||||
|             (*handle->ioh)->Release(handle->ioh); | ||||
|             (*handle->plug_in)->Release(handle->plug_in); | ||||
|         } | ||||
|         free(handle); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open_async_in_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     libusbp_async_in_pipe ** pipe) | ||||
| { | ||||
|     return async_in_pipe_create(handle, pipe_id, pipe); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_set_timeout( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     uint32_t timeout) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     error = check_pipe_id(pipe_id); | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER; | ||||
|  | ||||
|         if (pipe_id & 0x80) | ||||
|         { | ||||
|             handle->in_timeout[endpoint_number] = timeout; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             handle->out_timeout[endpoint_number] = timeout; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_control_transfer( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t bmRequestType, | ||||
|     uint8_t bRequest, | ||||
|     uint16_t wValue, | ||||
|     uint16_t wIndex, | ||||
|     void * buffer, | ||||
|     uint16_t wLength, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     IOUSBDevRequestTO request; | ||||
|     request.bmRequestType = bmRequestType; | ||||
|     request.bRequest = bRequest; | ||||
|     request.wValue = wValue; | ||||
|     request.wIndex = wIndex; | ||||
|     request.wLength = wLength; | ||||
|     request.pData = buffer; | ||||
|     request.completionTimeout = handle->out_timeout[0]; | ||||
|     request.wLenDone = 0; | ||||
|  | ||||
|     kern_return_t kr = (*handle->ioh)->ControlRequestTO(handle->ioh, 0, &request); | ||||
|     if (transferred != NULL) { *transferred = request.wLenDone; } | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         return error_create_mach(kr, "Control transfer failed."); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_read_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     void * buffer, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (size == 0) | ||||
|     { | ||||
|         error = error_create("Transfer size 0 is not allowed."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && size > UINT32_MAX) | ||||
|     { | ||||
|         error = error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && buffer == NULL) | ||||
|     { | ||||
|         error = error_create("Buffer is null."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_in(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER; | ||||
|         uint32_t no_data_timeout = 0; | ||||
|         uint32_t completion_timeout = handle->in_timeout[endpoint_number]; | ||||
|         uint32_t iokit_size = (uint32_t) size; | ||||
|         uint32_t pipe_index = handle->in_pipe_index[endpoint_number]; | ||||
|         kern_return_t kr = (*handle->ioh)->ReadPipeTO(handle->ioh, (UInt8) pipe_index, | ||||
|           buffer, &iokit_size, no_data_timeout, completion_timeout); | ||||
|         if (transferred != NULL) { *transferred = iokit_size; } | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, ""); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to read from pipe."); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_write_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     const void * buffer, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (size > UINT32_MAX) | ||||
|     { | ||||
|         error = error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && buffer == NULL && size) | ||||
|     { | ||||
|         error = error_create("Buffer is null."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_out(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER; | ||||
|         uint32_t no_data_timeout = 0; | ||||
|         uint32_t completion_timeout = handle->out_timeout[endpoint_number]; | ||||
|         uint32_t pipe_index = handle->out_pipe_index[endpoint_number]; | ||||
|         kern_return_t kr = (*handle->ioh)->WritePipeTO(handle->ioh, (UInt8) pipe_index, | ||||
|           (void *)buffer, (UInt32) size, no_data_timeout, completion_timeout); | ||||
|         if (kr != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(kr, ""); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && transferred != NULL) | ||||
|     { | ||||
|         // There was no error, so just assume the entire amount was transferred. | ||||
|         // WritePipeTO does not give us a number. | ||||
|         *transferred = size; | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to write to pipe."); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| #pragma pack(4) | ||||
| typedef struct | ||||
| { | ||||
|     mach_msg_header_t header; | ||||
|     uint8_t buffer[1024]; | ||||
| } our_msg; | ||||
|  | ||||
| libusbp_error * generic_handle_events(libusbp_generic_handle * handle) | ||||
| { | ||||
|     assert(handle != NULL); | ||||
|  | ||||
|     while(1) | ||||
|     { | ||||
|         // Check for messages on this handle's async port using mach_msg(). | ||||
|         our_msg msg; | ||||
|         mach_msg_option_t option = MACH_RCV_MSG | MACH_RCV_TIMEOUT; | ||||
|         option |= MACH_RCV_LARGE;  // tmphax to receive large messages | ||||
|         mach_msg_size_t send_size = 0; | ||||
|         mach_msg_size_t rcv_size = sizeof(msg); // might need to make this bigger | ||||
|         mach_msg_timeout_t timeout = 0;  // non-blocking | ||||
|         mach_port_t notify = MACH_PORT_NULL; | ||||
|         mach_msg_return_t mr = mach_msg(&msg.header, option, send_size, rcv_size, | ||||
|             handle->async_port, timeout, notify); | ||||
|  | ||||
|         if (mr != MACH_MSG_SUCCESS) | ||||
|         { | ||||
|             if (mr == MACH_RCV_TIMED_OUT) | ||||
|             { | ||||
|                 // There was no message available on the port. | ||||
|                 return NULL; | ||||
|             } | ||||
|             else if (mr == MACH_RCV_TOO_LARGE) | ||||
|             { | ||||
|                 return error_create("Mach message received was too large: %d > %d\n", | ||||
|                     msg.header.msgh_size, (unsigned int)sizeof(msg)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return error_create_mach(mr, "Failed to receive mach message."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #ifdef LIBUSBP_LOG | ||||
|         log_mach_msg(mr, &msg.header); | ||||
|         #endif | ||||
|  | ||||
|         // We have received a message from the mach port that contains a blob of | ||||
|         // binary data.  The blob seems to include a pointer to the | ||||
|         // async_in_transfer, a kern_return_t code for the transfer, and a pointer | ||||
|         // to the callback we specified when we submitted the transfer.  We aren't | ||||
|         // supposed to process that data ourselves; we are supposed to call | ||||
|         // IODispatchCalloutFromMessage. | ||||
|         // | ||||
|         // The third argument to IODispatchCalloutFromMessage is supposed to be a | ||||
|         // IONotificationPortRef, but we don't have access to the | ||||
|         // IONotificationPortRef, which is a protected member of IOUSBInterface | ||||
|         // class with no accessors.  Passing NULL (or any arbitary pointer) seems to | ||||
|         // work.  IODispatchCalloutFromMessage has no return value. | ||||
|         IODispatchCalloutFromMessage(0, &msg.header, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ** libusbp_generic_handle_get_cf_plug_in(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return (void **)handle->plug_in; | ||||
| } | ||||
|  | ||||
| IOUSBInterfaceInterface182 ** generic_handle_get_ioh(const libusbp_generic_handle * handle) | ||||
| { | ||||
|     assert(handle != NULL); | ||||
|     return handle->ioh; | ||||
| } | ||||
|  | ||||
| uint8_t generic_handle_get_pipe_index(const libusbp_generic_handle * handle, uint8_t pipe_id) | ||||
| { | ||||
|     uint8_t endpoint_number = pipe_id & (uint8_t) MAX_ENDPOINT_NUMBER; | ||||
|     if (pipe_id & 0x80) | ||||
|     { | ||||
|         return handle->in_pipe_index[endpoint_number]; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return handle->out_pipe_index[endpoint_number]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										224
									
								
								dep/libusbp/src/mac/generic_interface_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								dep/libusbp/src/mac/generic_interface_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| /* Composite devices on all OSes and non-composite vendor-defined devices on | ||||
|  * Windows and Linux will automatically be set to configuration 1, which means | ||||
|  * we can find device nodes for the specific interface we are interested in | ||||
|  * using in libusbp_generic_interface_create, and return error code | ||||
|  * LIBUSBP_ERROR_NOT_READY if that node is not found.  However, non-composite | ||||
|  * devices on Mac OS X will not automatically get configured unless someone | ||||
|  * tells them what configuration to use. | ||||
|  * | ||||
|  * For composite devices on Mac OS X: | ||||
|  *   libusbp_generic_interface_create() finds the correct device node. | ||||
|  *   libusbp_generic_handle_open() simply opens it. | ||||
|  * | ||||
|  * For non-composite devices on Mac OS X: | ||||
|  *   libusbp_generic_interface_create() just records information from the user. | ||||
|  *   libusbp_generic_handle_open() ensures the device is set to configuration 1, | ||||
|  *     then finds the correct interface and opens it. | ||||
|  */ | ||||
|  | ||||
| struct libusbp_generic_interface | ||||
| { | ||||
|     uint64_t device_id; | ||||
|     uint8_t interface_number; | ||||
|     bool has_interface_id; | ||||
|     uint64_t interface_id; | ||||
| }; | ||||
|  | ||||
| libusbp_error * generic_interface_allocate(libusbp_generic_interface ** gi) | ||||
| { | ||||
|     *gi = calloc(1, sizeof(libusbp_generic_interface)); | ||||
|     if (*gi == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_generic_interface ** gi) | ||||
| { | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *gi = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_generic_interface * new_gi = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = generic_interface_allocate(&new_gi); | ||||
|     } | ||||
|  | ||||
|     // Record the I/O registry ID for the device. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gi->device_id = device_get_id(device); | ||||
|     } | ||||
|  | ||||
|     // Record the interface number. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gi->interface_number = interface_number; | ||||
|     } | ||||
|  | ||||
|     // If this is a composite device, get the id for the specific interface we | ||||
|     // are interested in.  If it is non-composite, we wait until later becuase | ||||
|     // the device might be unconfigured and its interface registry entries might | ||||
|     // not even exist yet. | ||||
|     if (error == NULL && composite) | ||||
|     { | ||||
|         // Get an io_service_t for the physical device. | ||||
|         io_service_t device_service = MACH_PORT_NULL; | ||||
|         error = service_get_from_id(new_gi->device_id, &device_service); | ||||
|  | ||||
|         // Get the io_service_t for the interface. | ||||
|         io_service_t interface_service = MACH_PORT_NULL; | ||||
|         if (error == NULL) | ||||
|         { | ||||
|             error = service_get_usb_interface(device_service, interface_number, &interface_service); | ||||
|         } | ||||
|  | ||||
|         // Get the registry entry ID for the interface. | ||||
|         if (error == NULL) | ||||
|         { | ||||
|             assert(interface_service != MACH_PORT_NULL); | ||||
|             error = get_id(interface_service, &new_gi->interface_id); | ||||
|         } | ||||
|  | ||||
|         // Record the fact that we have an ID for the interface. | ||||
|         if (error == NULL) | ||||
|         { | ||||
|             assert(new_gi->interface_id); | ||||
|             assert(new_gi->interface_id != new_gi->device_id); | ||||
|             new_gi->has_interface_id = true; | ||||
|         } | ||||
|  | ||||
|         if (device_service != MACH_PORT_NULL) { IOObjectRelease(device_service); } | ||||
|         if (interface_service != MACH_PORT_NULL) { IOObjectRelease(interface_service); } | ||||
|     } | ||||
|  | ||||
|     // Pass the new generic interface to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *gi = new_gi; | ||||
|         new_gi = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_generic_interface_free(new_gi); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_interface_free(libusbp_generic_interface * gi) | ||||
| { | ||||
|     free(gi); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_copy( | ||||
|     const libusbp_generic_interface * source, | ||||
|     libusbp_generic_interface ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate the generic interface. | ||||
|     libusbp_generic_interface * new_gi = NULL; | ||||
|     error = generic_interface_allocate(&new_gi); | ||||
|  | ||||
|     // Copy the simple fields. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         memcpy(new_gi, source, sizeof(libusbp_generic_interface)); | ||||
|     } | ||||
|  | ||||
|     // Pass the generic interface to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_gi; | ||||
|         new_gi = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_generic_interface_free(new_gi); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| uint64_t generic_interface_get_device_id(const libusbp_generic_interface * gi) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     return gi->device_id; | ||||
| } | ||||
|  | ||||
| uint8_t generic_interface_get_interface_number(const libusbp_generic_interface * gi) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     return gi->interface_number; | ||||
| } | ||||
|  | ||||
| bool generic_interface_get_interface_id(const libusbp_generic_interface * gi, uint64_t * id) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     if (gi->has_interface_id) | ||||
|     { | ||||
|         *id = gi->interface_id; | ||||
|         return 1; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         *id = 0; | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_id( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     // Some information is being lost here, unfortunately. | ||||
|     uint64_t idnum = gi->has_interface_id ? gi->interface_id : gi->device_id; | ||||
|  | ||||
|     return iokit_id_to_string(idnum, id); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_filename( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     char ** filename) | ||||
| { | ||||
|     return libusbp_generic_interface_get_os_id(gi, filename); | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										306
									
								
								dep/libusbp/src/mac/iokit_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								dep/libusbp/src/mac/iokit_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,306 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * iokit_id_to_string(uint64_t id, char ** str) | ||||
| { | ||||
|     char buffer[17]; | ||||
|     snprintf(buffer, sizeof(buffer), "%" PRIx64, id); | ||||
|     return string_copy(buffer, str); | ||||
| } | ||||
|  | ||||
| libusbp_error * service_get_from_id(uint64_t id, io_service_t * service) | ||||
| { | ||||
|     assert(service != NULL); | ||||
|  | ||||
|     // Create a dictionary specifying this ID.  This dictionary will be | ||||
|     // CFReleased by IOServiceGetMatchingService. | ||||
|     CFMutableDictionaryRef dict = IORegistryEntryIDMatching(id); | ||||
|     if (dict == NULL) | ||||
|     { | ||||
|         return error_create("Failed to create a dictionary matching the ID."); | ||||
|     } | ||||
|  | ||||
|     *service = IOServiceGetMatchingService(kIOMasterPortDefault, dict); | ||||
|     if (*service == MACH_PORT_NULL) | ||||
|     { | ||||
|         return error_create("Failed to find service with ID 0x%" PRIx64 ".", id); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Takes a service representing a physical, composite USB device. | ||||
| // Returns a service representing the specified USB interface of the device. | ||||
| // The returned service will conform to the class IOUSBInterface. | ||||
| libusbp_error * service_get_usb_interface(io_service_t service, | ||||
|     uint8_t interface_number, io_service_t * interface_service) | ||||
| { | ||||
|     assert(service != MACH_PORT_NULL); | ||||
|     assert(interface_service != NULL); | ||||
|  | ||||
|     *interface_service = MACH_PORT_NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     io_iterator_t iterator = MACH_PORT_NULL; | ||||
|     kern_return_t result = IORegistryEntryGetChildIterator( | ||||
|         service, kIOServicePlane, &iterator); | ||||
|  | ||||
|     if (result != KERN_SUCCESS) | ||||
|     { | ||||
|         error = error_create_mach(result, "Failed to get child iterator."); | ||||
|     } | ||||
|  | ||||
|     // Loop through the devices to find the right one. | ||||
|     while (error == NULL) | ||||
|     { | ||||
|         io_service_t candidate = IOIteratorNext(iterator); | ||||
|         if (candidate == MACH_PORT_NULL) { break; } | ||||
|  | ||||
|         // Filter out candidates that are not of class IOUSBInterface. | ||||
|         bool conforms = (bool) IOObjectConformsTo(candidate, kIOUSBInterfaceClassName); | ||||
|         if (!conforms) | ||||
|         { | ||||
|             IOObjectRelease(candidate); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // Get bInterfaceNumber. | ||||
|         int32_t actual_num; | ||||
|         error = get_int32(candidate, CFSTR("bInterfaceNumber"), &actual_num); | ||||
|  | ||||
|         if (error == NULL && actual_num == interface_number) | ||||
|         { | ||||
|             // This is the right one.  Pass it to the caller. | ||||
|             *interface_service = candidate; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         IOObjectRelease(candidate); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && *interface_service == MACH_PORT_NULL) | ||||
|     { | ||||
|         error = error_create("Could not find interface %d.", interface_number); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|     } | ||||
|  | ||||
|     if (iterator != MACH_PORT_NULL) { IOObjectRelease(iterator); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * service_get_child_by_class(io_service_t service, | ||||
|     const char * class_name, io_service_t * interface_service) | ||||
| { | ||||
|     assert(service != MACH_PORT_NULL); | ||||
|     assert(interface_service != NULL); | ||||
|  | ||||
|     *interface_service = MACH_PORT_NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     io_iterator_t iterator = MACH_PORT_NULL; | ||||
|     kern_return_t result = IORegistryEntryCreateIterator( | ||||
|         service, kIOServicePlane, kIORegistryIterateRecursively, &iterator); | ||||
|  | ||||
|     if (result != KERN_SUCCESS) | ||||
|     { | ||||
|         error = error_create_mach(result, "Failed to get recursive iterator."); | ||||
|     } | ||||
|  | ||||
|     // Loop through the devices to find the right one. | ||||
|     while (error == NULL) | ||||
|     { | ||||
|         io_service_t candidate = IOIteratorNext(iterator); | ||||
|         if (candidate == MACH_PORT_NULL) { break; } | ||||
|  | ||||
|         // Filter out candidates that are not the right class. | ||||
|         bool conforms = (bool) IOObjectConformsTo(candidate, class_name); | ||||
|         if (!conforms) | ||||
|         { | ||||
|             IOObjectRelease(candidate); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // This is the right one.  Pass it to the caller. | ||||
|         *interface_service = candidate; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && *interface_service == MACH_PORT_NULL) | ||||
|     { | ||||
|         error = error_create("Could not find entry with class %s.", class_name); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|     } | ||||
|  | ||||
|     if (iterator != MACH_PORT_NULL) { IOObjectRelease(iterator); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * service_to_interface( | ||||
|     io_service_t service, | ||||
|     CFUUIDRef pluginType, | ||||
|     REFIID rid, | ||||
|     void ** object, | ||||
|     IOCFPlugInInterface *** plug_in) | ||||
| { | ||||
|     assert(service != MACH_PORT_NULL); | ||||
|     assert(object != NULL); | ||||
|  | ||||
|     *object = NULL; | ||||
|  | ||||
|     int32_t score; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Create the plug-in interface. | ||||
|     IOCFPlugInInterface ** new_plug_in = NULL; | ||||
|     kern_return_t kr = IOCreatePlugInInterfaceForService(service, | ||||
|         pluginType, kIOCFPlugInInterfaceID, | ||||
|         &new_plug_in, &score); | ||||
|  | ||||
|     if (kr != KERN_SUCCESS) | ||||
|     { | ||||
|         error = error_create_mach(kr, "Failed to create plug-in interface."); | ||||
|     } | ||||
|  | ||||
|     // Create the device interface and pass it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         HRESULT hr = (*new_plug_in)->QueryInterface(new_plug_in, rid, object); | ||||
|         if (hr) | ||||
|         { | ||||
|             error = error_create_hr(hr, "Failed to query interface."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Also pass the plug-in interface to the caller if they want it. | ||||
|     if (error == NULL && plug_in != NULL) | ||||
|     { | ||||
|         *plug_in = new_plug_in; | ||||
|         new_plug_in = NULL; | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     if (new_plug_in != NULL) | ||||
|     { | ||||
|         (*new_plug_in)->Release(new_plug_in); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
|  | ||||
| libusbp_error * get_id(io_registry_entry_t entry, uint64_t * id) | ||||
| { | ||||
|     assert(entry != MACH_PORT_NULL); | ||||
|     assert(id != NULL); | ||||
|  | ||||
|     *id = 0; | ||||
|  | ||||
|     kern_return_t result = IORegistryEntryGetRegistryEntryID(entry, id); | ||||
|     if (result != KERN_SUCCESS) | ||||
|     { | ||||
|         return error_create_mach(result, "Failed to get registry entry ID."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Returns NULL if the string is not present. | ||||
| // The returned string should be freed with libusbp_string_free. | ||||
| libusbp_error * get_string(io_registry_entry_t entry, CFStringRef name, char ** value) | ||||
| { | ||||
|     assert(entry != MACH_PORT_NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     *value = NULL; | ||||
|  | ||||
|     CFTypeRef cf_value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0); | ||||
|     if (cf_value == NULL) | ||||
|     { | ||||
|         // The string probably does not exist, so just return. | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (CFGetTypeID(cf_value) != CFStringGetTypeID()) | ||||
|     { | ||||
|         error = error_create("Property is not a string."); | ||||
|     } | ||||
|  | ||||
|     char buffer[256]; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         bool success = CFStringGetCString(cf_value, buffer, sizeof(buffer), | ||||
|             kCFStringEncodingASCII); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create("Failed to convert property to C string."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(buffer, value); | ||||
|     } | ||||
|  | ||||
|     CFRelease(cf_value); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * get_int32(io_registry_entry_t entry, CFStringRef name, int32_t * value) | ||||
| { | ||||
|     assert(entry != MACH_PORT_NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     *value = 0; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     CFTypeRef cf_value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0); | ||||
|  | ||||
|     if (cf_value == NULL) | ||||
|     { | ||||
|         error = error_create("Failed to get int32 property from IORegistryEntry."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && CFGetTypeID(cf_value) != CFNumberGetTypeID()) | ||||
|     { | ||||
|         error = error_create("Property is not a number."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         bool success = CFNumberGetValue(cf_value, kCFNumberSInt32Type, value); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create("Failed to convert property to C integer."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (cf_value != NULL) { CFRelease(cf_value); } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * get_uint16(io_registry_entry_t entry, CFStringRef name, uint16_t * value) | ||||
| { | ||||
|     assert(entry != MACH_PORT_NULL); | ||||
|     assert(name != NULL); | ||||
|     assert(value != NULL); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     int32_t tmp; | ||||
|     error = get_int32(entry, name, &tmp); | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         // There is an unchecked conversion of an int32_t to a uint16_t here but | ||||
|         // we don't expect any data to be lost. | ||||
|         *value = (uint16_t) tmp; | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
							
								
								
									
										103
									
								
								dep/libusbp/src/mac/list_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								dep/libusbp/src/mac/list_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| static void try_create_device(io_service_t service, libusbp_device ** device) | ||||
| { | ||||
|     libusbp_error * error = create_device(service, device); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         assert(*device == NULL); | ||||
|  | ||||
|         // Something went wrong.  To make the library more robust and usable, we | ||||
|         // ignore this error and continue. | ||||
|         #ifdef LIBUSBP_LOG | ||||
|         fprintf(stderr, "Problem creating device: %s\n", | ||||
|             libusbp_error_get_message(error)); | ||||
|         #endif | ||||
|  | ||||
|         libusbp_error_free(error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_list_connected_devices( | ||||
|     libusbp_device *** device_list, | ||||
|     size_t * device_count) | ||||
| { | ||||
|     if (device_count != NULL) | ||||
|     { | ||||
|         *device_count = 0; | ||||
|     } | ||||
|  | ||||
|     if (device_list == NULL) | ||||
|     { | ||||
|         return error_create("Device list output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Create a dictionary that says "IOProviderClass" => "IOUSBDevice" | ||||
|     // This dictionary is CFReleased by IOServiceGetMatchingServices. | ||||
|     CFMutableDictionaryRef dict = NULL; | ||||
|     dict = IOServiceMatching("IOUSBHostDevice"); | ||||
|     if (dict == NULL) | ||||
|     { | ||||
|         error = error_create("IOServiceMatching returned null."); | ||||
|     } | ||||
|  | ||||
|     // Create an iterator for all the connected USB devices. | ||||
|     io_iterator_t iterator = MACH_PORT_NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         // IOServiceGetMatchingServices consumes one reference to dict, | ||||
|         // so we don't have to CFRelease it. | ||||
|         kern_return_t result = IOServiceGetMatchingServices( | ||||
|             kIOMasterPortDefault, dict, &iterator); | ||||
|         dict = NULL; | ||||
|         if (result != KERN_SUCCESS) | ||||
|         { | ||||
|             error = error_create_mach(result, "Failed to get matching services."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Allocate a new list. | ||||
|     libusbp_device ** new_list = NULL; | ||||
|     size_t count = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_list_create(&new_list); | ||||
|     } | ||||
|  | ||||
|     // Loop through the devices and add them to our list. | ||||
|     while(error == NULL) | ||||
|     { | ||||
|         io_service_t service = IOIteratorNext(iterator); | ||||
|         if (service == MACH_PORT_NULL) { break; } | ||||
|  | ||||
|         libusbp_device * device = NULL; | ||||
|         try_create_device(service, &device); | ||||
|         if (device != NULL) | ||||
|         { | ||||
|             error = device_list_append(&new_list, &count, device); | ||||
|         } | ||||
|  | ||||
|         IOObjectRelease(service); | ||||
|     } | ||||
|  | ||||
|     // Pass the list and the count to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device_list = new_list; | ||||
|         new_list = NULL; | ||||
|  | ||||
|         if (device_count != NULL) | ||||
|         { | ||||
|             *device_count = count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     assert(dict == NULL); | ||||
|     if (dict != NULL) { CFRelease(dict); }  // makes the code less brittle | ||||
|     if (iterator != MACH_PORT_NULL) { IOObjectRelease(iterator); } | ||||
|     free_devices_and_list(new_list); | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										169
									
								
								dep/libusbp/src/mac/serial_port_mac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								dep/libusbp/src/mac/serial_port_mac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_serial_port | ||||
| { | ||||
|     // The I/O Registry ID of the IOBSDSerialClient. | ||||
|     uint64_t id; | ||||
|  | ||||
|     // A port filename like "/dev/cu.usbmodemFD123". | ||||
|     char * port_name; | ||||
| }; | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_serial_port ** port) | ||||
| { | ||||
|     LIBUSBP_UNUSED(composite); | ||||
|  | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *port = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     // Add one to the interface number because that is what we need for the | ||||
|     // typical case: The user specifies the lower of the two interface numbers, | ||||
|     // which corresponds to the control interface of a CDC ACM device.  We | ||||
|     // actually need the data interface because that is the one that the | ||||
|     // IOSerialBSDClient lives under.  If this +1 causes any problems, it is | ||||
|     // easy for the user to address it using an an ifdef.  Also, we might make | ||||
|     // this function more flexible in the future if we need to handle different | ||||
|     // types of serial devices with different drivers or interface layouts. | ||||
|     interface_number += 1; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_serial_port * new_port = calloc(1, sizeof(libusbp_serial_port)); | ||||
|  | ||||
|     if (new_port == NULL) | ||||
|     { | ||||
|         error = &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Get the ID for the physical device. | ||||
|     uint64_t device_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         device_id = device_get_id(device); | ||||
|     } | ||||
|  | ||||
|     // Get an io_service_t for the physical device. | ||||
|     io_service_t device_service = MACH_PORT_NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = service_get_from_id(device_id, &device_service); | ||||
|     } | ||||
|  | ||||
|     // Get an io_service_t for the interface. | ||||
|     io_service_t interface_service = MACH_PORT_NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = service_get_usb_interface(device_service, interface_number, &interface_service); | ||||
|     } | ||||
|  | ||||
|     // Get an io_service_t for the IOSerialBSDClient | ||||
|     io_service_t serial_service = MACH_PORT_NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = service_get_child_by_class(interface_service, | ||||
|             kIOSerialBSDServiceValue, &serial_service); | ||||
|     } | ||||
|  | ||||
|     // Get the port name. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_string(serial_service, CFSTR(kIOCalloutDeviceKey), &new_port->port_name); | ||||
|     } | ||||
|  | ||||
|     // Pass the new object to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *port = new_port; | ||||
|         new_port = NULL; | ||||
|     } | ||||
|  | ||||
|     if (serial_service != MACH_PORT_NULL) { IOObjectRelease(serial_service); } | ||||
|     if (interface_service != MACH_PORT_NULL) { IOObjectRelease(interface_service); } | ||||
|     if (device_service != MACH_PORT_NULL) { IOObjectRelease(device_service); } | ||||
|     libusbp_serial_port_free(new_port); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_serial_port_free(libusbp_serial_port * port) | ||||
| { | ||||
|     if (port != NULL) | ||||
|     { | ||||
|         libusbp_string_free(port->port_name); | ||||
|         free(port); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_copy(const libusbp_serial_port * source, | ||||
|     libusbp_serial_port ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the new object. | ||||
|     libusbp_serial_port * new_port = calloc(1, sizeof(libusbp_serial_port)); | ||||
|  | ||||
|     if (new_port == NULL) | ||||
|     { | ||||
|         error = &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Copy the port name. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->port_name, &new_port->port_name); | ||||
|     } | ||||
|  | ||||
|     // Pass the new object to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_port; | ||||
|         new_port = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_serial_port_free(new_port); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_get_name( | ||||
|     const libusbp_serial_port * port, | ||||
|     char ** name) | ||||
| { | ||||
|     if (name == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *name = NULL; | ||||
|  | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port is null."); | ||||
|     } | ||||
|  | ||||
|     return string_copy(port->port_name, name); | ||||
| } | ||||
							
								
								
									
										33
									
								
								dep/libusbp/src/pipe_id.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								dep/libusbp/src/pipe_id.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| static libusbp_error * error_invalid_pipe_id(uint8_t pipe_id) | ||||
| { | ||||
|     return error_create("Invalid pipe ID 0x%02x.", pipe_id); | ||||
| } | ||||
|  | ||||
| libusbp_error * check_pipe_id(uint8_t pipe_id) | ||||
| { | ||||
|     if ((pipe_id & ~0x80) > MAX_ENDPOINT_NUMBER || pipe_id == 0x80) | ||||
|     { | ||||
|         return error_invalid_pipe_id(pipe_id); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * check_pipe_id_in(uint8_t pipe_id) | ||||
| { | ||||
|     if (!(pipe_id & 0x80)) | ||||
|     { | ||||
|         return error_invalid_pipe_id(pipe_id); | ||||
|     } | ||||
|     return check_pipe_id(pipe_id); | ||||
| } | ||||
|  | ||||
| libusbp_error * check_pipe_id_out(uint8_t pipe_id) | ||||
| { | ||||
|     if (pipe_id & 0x80) | ||||
|     { | ||||
|         return error_invalid_pipe_id(pipe_id); | ||||
|     } | ||||
|     return check_pipe_id(pipe_id); | ||||
| } | ||||
							
								
								
									
										25
									
								
								dep/libusbp/src/string.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								dep/libusbp/src/string.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| // Simple wrapper around strdup to make a copy of a string, either for internal | ||||
| // use or for returning the string to the user. | ||||
| libusbp_error * string_copy(const char * input_string, char ** output_string) | ||||
| { | ||||
|     assert(input_string != NULL); | ||||
|     assert(output_string != NULL); | ||||
|  | ||||
|     *output_string = NULL; | ||||
|  | ||||
|     char * new_string = strdup(input_string); | ||||
|     if (new_string == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     *output_string = new_string; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void libusbp_string_free(char * string) | ||||
| { | ||||
|     free(string); | ||||
| } | ||||
							
								
								
									
										239
									
								
								dep/libusbp/src/windows/async_in_transfer_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								dep/libusbp/src/windows/async_in_transfer_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct async_in_transfer | ||||
| { | ||||
|     HANDLE winusb_handle; | ||||
|     uint8_t pipe_id; | ||||
|     void * buffer; | ||||
|     size_t buffer_size; | ||||
|     OVERLAPPED overlapped; | ||||
|     ULONG transferred; | ||||
|     bool pending; | ||||
|     libusbp_error * error; | ||||
| }; | ||||
|  | ||||
| libusbp_error * async_in_pipe_setup(libusbp_generic_handle * handle, uint8_t pipe_id) | ||||
| { | ||||
|     assert(handle != NULL); | ||||
|  | ||||
|     HANDLE winusb_handle = libusbp_generic_handle_get_winusb_handle(handle); | ||||
|  | ||||
|     // Turn on raw I/O, because without it the transfers will not be efficient | ||||
|     // enough in Windows Vista and Windows 7, as tested by test_async_in. | ||||
|     UCHAR raw_io = 1; | ||||
|     BOOL success = WinUsb_SetPipePolicy(winusb_handle, pipe_id, RAW_IO, | ||||
|         sizeof(raw_io), &raw_io); | ||||
|     if (!success) | ||||
|     { | ||||
|         return error_create_winapi("Failed to enable raw I/O for pipe 0x%x.", pipe_id); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void async_in_transfer_free(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) { return; } | ||||
|  | ||||
|     if (transfer->pending) | ||||
|     { | ||||
|         // Unfortunately, this transfer is still pending, so we cannot free it; | ||||
|         // the kernel needs to be able to write to this transfer's memory when | ||||
|         // it completes.  We could just abort the process here, but instead we | ||||
|         // we choose to have a memory leak, since it can be easily detected and | ||||
|         // might be small enough to be harmless. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     CloseHandle(transfer->overlapped.hEvent); | ||||
|     free(transfer->buffer); | ||||
|     free(transfer); | ||||
| } | ||||
|  | ||||
| void async_in_transfer_submit(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(transfer->pending == false); | ||||
|  | ||||
|     libusbp_error_free(transfer->error); | ||||
|     transfer->error = NULL; | ||||
|     transfer->pending = true; | ||||
|     transfer->transferred = 0; | ||||
|  | ||||
|     BOOL success = WinUsb_ReadPipe( | ||||
|         transfer->winusb_handle, | ||||
|         transfer->pipe_id, | ||||
|         transfer->buffer, | ||||
|         transfer->buffer_size, | ||||
|         &transfer->transferred, | ||||
|         &transfer->overlapped); | ||||
|     if (success) | ||||
|     { | ||||
|         // The transfer completed immediately. | ||||
|         transfer->pending = false; | ||||
|     } | ||||
|     else if (GetLastError() == ERROR_IO_PENDING) | ||||
|     { | ||||
|         // The transfer is pending. | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // An error happened. | ||||
|         transfer->error = error_create_winapi("Failed to submit asynchronous in transfer."); | ||||
|         transfer->pending = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_create( | ||||
|     libusbp_generic_handle * handle, uint8_t pipe_id, size_t transfer_size, | ||||
|     async_in_transfer ** transfer) | ||||
| { | ||||
|     assert(transfer_size != 0); | ||||
|     assert(transfer != NULL); | ||||
|  | ||||
|     if (transfer_size > ULONG_MAX) | ||||
|     { | ||||
|         // WinUSB uses ULONGs to represent sizes. | ||||
|         return error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate the transfer struct. | ||||
|     async_in_transfer * new_transfer = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer = calloc(1, sizeof(async_in_transfer)); | ||||
|         if (new_transfer == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Allocate the buffer for the transfer. | ||||
|     void * new_buffer = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_buffer = malloc(transfer_size); | ||||
|         if (new_buffer == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Create an event. | ||||
|     HANDLE new_event = INVALID_HANDLE_VALUE; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_event = CreateEvent(NULL, false, false, NULL); | ||||
|         if (new_event == NULL) | ||||
|         { | ||||
|             error = error_create_winapi( | ||||
|                 "Failed to create event for asynchronous in transfer."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Assemble the transfer and pass it to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_transfer->winusb_handle = libusbp_generic_handle_get_winusb_handle(handle); | ||||
|         new_transfer->buffer_size = transfer_size; | ||||
|         new_transfer->pipe_id = pipe_id; | ||||
|  | ||||
|         new_transfer->buffer = new_buffer; | ||||
|         new_buffer = NULL; | ||||
|  | ||||
|         new_transfer->overlapped.hEvent = new_event; | ||||
|         new_event = INVALID_HANDLE_VALUE; | ||||
|  | ||||
|         *transfer = new_transfer; | ||||
|         new_transfer = NULL; | ||||
|     } | ||||
|  | ||||
|     if (new_event != INVALID_HANDLE_VALUE) { CloseHandle(new_event); } | ||||
|     free(new_buffer); | ||||
|     free(new_transfer); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * async_in_transfer_get_results(async_in_transfer * transfer, | ||||
|     void * buffer, size_t * transferred, libusbp_error ** transfer_error) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|     assert(!transfer->pending); | ||||
|  | ||||
|     size_t tmp_transferred = transfer->transferred; | ||||
|  | ||||
|     // Make sure we don't overflow the user's buffer. | ||||
|     if (tmp_transferred > transfer->buffer_size) | ||||
|     { | ||||
|         tmp_transferred = transfer->buffer_size; | ||||
|     } | ||||
|  | ||||
|     if (buffer != NULL) | ||||
|     { | ||||
|         memcpy(buffer, transfer->buffer, tmp_transferred); | ||||
|     } | ||||
|  | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = tmp_transferred; | ||||
|     } | ||||
|  | ||||
|     if (transfer_error != NULL) | ||||
|     { | ||||
|         *transfer_error = libusbp_error_copy(transfer->error); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Cancels all of the transfers on this pipe, given one of the transfers. | ||||
| libusbp_error * async_in_transfer_cancel(async_in_transfer * transfer) | ||||
| { | ||||
|     if (transfer == NULL) | ||||
|     { | ||||
|         return error_create("Transfer to cancel is null."); | ||||
|     } | ||||
|  | ||||
|     BOOL success = WinUsb_AbortPipe(transfer->winusb_handle, transfer->pipe_id); | ||||
|     if (!success) | ||||
|     { | ||||
|         return error_create_winapi("Failed to cancel transfers on pipe 0x%x.", | ||||
|             transfer->pipe_id); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| bool async_in_transfer_pending(async_in_transfer * transfer) | ||||
| { | ||||
|     assert(transfer != NULL); | ||||
|  | ||||
|     if (!transfer->pending) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     DWORD transferred = 0; | ||||
|     BOOL success = WinUsb_GetOverlappedResult( | ||||
|         transfer->winusb_handle, &transfer->overlapped, &transferred, false); | ||||
|  | ||||
|     transfer->transferred = transferred; | ||||
|  | ||||
|     if (success) | ||||
|     { | ||||
|         transfer->pending = false; | ||||
|     } | ||||
|     else if (GetLastError() == ERROR_IO_INCOMPLETE) | ||||
|     { | ||||
|         // The transfer is still pending. | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         transfer->error = error_create_overlapped("Asynchronous IN transfer failed."); | ||||
|         transfer->pending = false; | ||||
|     } | ||||
|  | ||||
|     return transfer->pending; | ||||
| } | ||||
							
								
								
									
										25
									
								
								dep/libusbp/src/windows/device_instance_id_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								dep/libusbp/src/windows/device_instance_id_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| libusbp_error * create_id_string(HDEVINFO list, PSP_DEVINFO_DATA info, char ** id) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(id != NULL); | ||||
|  | ||||
|     DWORD size = MAX_DEVICE_ID_LEN + 1; | ||||
|     char * new_id = malloc(size); | ||||
|     if (new_id == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     bool success = SetupDiGetDeviceInstanceId(list, info, new_id, size, NULL); | ||||
|     if (!success) | ||||
|     { | ||||
|         free(new_id); | ||||
|         return error_create_winapi("Error getting device instance ID."); | ||||
|     } | ||||
|  | ||||
|     *id = new_id; | ||||
|     return NULL; | ||||
| } | ||||
							
								
								
									
										362
									
								
								dep/libusbp/src/windows/device_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								dep/libusbp/src/windows/device_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,362 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_device | ||||
| { | ||||
|     // A string like: USB\VID_xxxx&PID_xxxx\idthing | ||||
|     char * device_instance_id; | ||||
|     uint16_t product_id; | ||||
|     uint16_t vendor_id; | ||||
|     uint16_t revision; | ||||
| }; | ||||
|  | ||||
| static libusbp_error * device_allocate(libusbp_device ** device) | ||||
| { | ||||
|     assert(device != NULL); | ||||
|     *device = calloc(1, sizeof(libusbp_device)); | ||||
|     if (*device == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // Gets the hardware IDs of the device, in ASCII REG_MULTI_SZ format.  If there | ||||
| // is no error, the returned IDs must be freed with libusbp_string_free. | ||||
| // This function converts the IDs to uppercase before returning them. | ||||
| static libusbp_error * device_get_hardware_ids( | ||||
|     HDEVINFO list, PSP_DEVINFO_DATA info, char ** ids) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(ids != NULL); | ||||
|  | ||||
|     *ids = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Get the size of the hardware IDs. | ||||
|     DWORD data_type = 0; | ||||
|     DWORD size = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         BOOL success = SetupDiGetDeviceRegistryProperty(list, info, | ||||
|             SPDRP_HARDWAREID, &data_type, NULL, 0, &size); | ||||
|         if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to get size of hardware IDs."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Allocate memory. | ||||
|     char * new_ids = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_ids = malloc(size); | ||||
|         if (new_ids == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the actual hardware IDs. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         BOOL success = SetupDiGetDeviceRegistryProperty(list, info, | ||||
|             SPDRP_HARDWAREID, &data_type, (unsigned char *)new_ids, size, NULL); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to get hardware IDs."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Check the data type. | ||||
|     if (error == NULL && data_type != REG_MULTI_SZ) | ||||
|     { | ||||
|         error = error_create("Hardware IDs are wrong data type: %ld.", data_type); | ||||
|     } | ||||
|     if (error == NULL && (size < 2 || new_ids[size - 2] != 0 || new_ids[size - 1] != 0)) | ||||
|     { | ||||
|         error = error_create("Hardware IDs are empty or not terminated correctly."); | ||||
|     } | ||||
|  | ||||
|     // Capitalize the hardware IDs because some drivers create USB IDs with the | ||||
|     // wrong capitalization (i.e. "Vid" instead of "VID"). | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         for (DWORD i = 0; i < size; i++) | ||||
|         { | ||||
|             if (new_ids[i] >= 'a' && new_ids[i] <= 'z') | ||||
|             { | ||||
|                 new_ids[i] -= 'a' - 'A'; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Pass the IDs to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *ids = new_ids; | ||||
|         new_ids = NULL; | ||||
|     } | ||||
|  | ||||
|     free(new_ids); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // This function extracts the product ID, vendor ID, and revision code | ||||
| // from the hardware IDs. | ||||
| // The "ids" parmaeter must be a pointer to a REG_MULTI_SZ data structure. | ||||
| // (null-terminated ASCII strings, ending with an empty string). | ||||
| static libusbp_error * device_take_info_from_hardware_ids( | ||||
|     libusbp_device * device, const char * ids) | ||||
| { | ||||
|     assert(ids != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     device->vendor_id = 0; | ||||
|     device->product_id = 0; | ||||
|     device->revision = 0; | ||||
|  | ||||
|     for(; *ids; ids += strlen(ids) + 1) | ||||
|     { | ||||
|         uint16_t vendor_id, product_id, revision; | ||||
|         int result = sscanf(ids, "USB\\VID_%4hx&PID_%4hx&REV_%4hx", | ||||
|             &vendor_id, &product_id, &revision); | ||||
|         if (result == 3) | ||||
|         { | ||||
|             device->vendor_id = vendor_id; | ||||
|             device->product_id = product_id; | ||||
|             device->revision = revision; | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return error_create("Device has no hardware ID with the correct format."); | ||||
| } | ||||
|  | ||||
| libusbp_error * create_device(HDEVINFO list, PSP_DEVINFO_DATA info, libusbp_device ** device) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(info->cbSize == sizeof(SP_DEVINFO_DATA)); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     *device = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the device itself. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_allocate(&new_device); | ||||
|     } | ||||
|  | ||||
|     // Get the device instance ID. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = create_id_string(list, info, &new_device->device_instance_id); | ||||
|     } | ||||
|  | ||||
|     // Get the vendor ID, product ID, and revision from the hardware IDs. | ||||
|     char * new_ids = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_get_hardware_ids(list, info, &new_ids); | ||||
|     } | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_take_info_from_hardware_ids(new_device, new_ids); | ||||
|     } | ||||
|  | ||||
|     // Return the device to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device = new_device; | ||||
|         new_device = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_string_free(new_ids); | ||||
|     libusbp_device_free(new_device); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_copy(const libusbp_device * source, libusbp_device ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Device output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the device itself. | ||||
|     libusbp_device * new_device = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_allocate(&new_device); | ||||
|     } | ||||
|  | ||||
|     // Copy the device instance ID. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         assert(source->device_instance_id != NULL); | ||||
|         size_t size = strlen(source->device_instance_id) + 1; | ||||
|         char * new_id = malloc(size); | ||||
|         if (new_id == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             memcpy(new_id, source->device_instance_id, size); | ||||
|             new_device->device_instance_id = new_id; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_device->vendor_id = source->vendor_id; | ||||
|         new_device->product_id = source->product_id; | ||||
|         new_device->revision = source->revision; | ||||
|     } | ||||
|  | ||||
|     // Pass the device to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_device; | ||||
|         new_device = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_device_free(new_device); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // Note: Some pointers in the device might be null when we are | ||||
| // freeing, because this can be called from the library to clean up a | ||||
| // failed device creation. | ||||
| void libusbp_device_free(libusbp_device * device) | ||||
| { | ||||
|     if (device != NULL) | ||||
|     { | ||||
|         free(device->device_instance_id); | ||||
|         free(device); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_vendor_id( | ||||
|     const libusbp_device * device, uint16_t * vendor_id) | ||||
| { | ||||
|     if (vendor_id == NULL) | ||||
|     { | ||||
|         return error_create("Vendor ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *vendor_id = device->vendor_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_product_id( | ||||
|     const libusbp_device * device, uint16_t * product_id) | ||||
| { | ||||
|     if (product_id == NULL) | ||||
|     { | ||||
|         return error_create("Product ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *product_id = device->product_id; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_revision( | ||||
|     const libusbp_device * device, uint16_t * revision) | ||||
| { | ||||
|     if (revision == NULL) | ||||
|     { | ||||
|         return error_create("Device revision output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = 0; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     *revision = device->revision; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_serial_number( | ||||
|     const libusbp_device * device, char ** serial_number) | ||||
| { | ||||
|     if (serial_number == NULL) | ||||
|     { | ||||
|         return error_create("Serial number output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *serial_number = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL && strlen(device->device_instance_id) < 22) | ||||
|     { | ||||
|         error = error_create("Device instance ID is too short."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(device->device_instance_id + 22, serial_number); | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to get serial number."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_device_get_os_id( | ||||
|     const libusbp_device * device, char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("Device OS ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     return string_copy(device->device_instance_id, id); | ||||
| } | ||||
|  | ||||
							
								
								
									
										111
									
								
								dep/libusbp/src/windows/error_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								dep/libusbp/src/windows/error_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* This file has functions for converting Windows error codes libusbp_error | ||||
|  * objects.  See error_message_test.cpp for documentation/justification of the | ||||
|  * conversions that are performed. */ | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| static libusbp_error * error_create_winapi_v( | ||||
|     DWORD error_code, const char * format, va_list ap) | ||||
| { | ||||
|     libusbp_error * error = error_create("Windows error code 0x%lx.", error_code); | ||||
|  | ||||
|     bool skip_windows_message = false; | ||||
|  | ||||
|     // Convert certain Windows error codes into libusbp error codes. | ||||
|     switch(error_code) | ||||
|     { | ||||
|     case ERROR_ACCESS_DENIED: | ||||
|         error = error_add(error, "Try closing all other programs that are using the device."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_ACCESS_DENIED); | ||||
|         break; | ||||
|     case ERROR_OUTOFMEMORY: | ||||
|     case ERROR_NOT_ENOUGH_MEMORY: | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_MEMORY); | ||||
|         break; | ||||
|     case ERROR_GEN_FAILURE: | ||||
|         skip_windows_message = true; | ||||
|         error = error_add(error, "The request was invalid or there was an I/O problem."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_STALL); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         break; | ||||
|     case ERROR_SEM_TIMEOUT: | ||||
|         skip_windows_message = true; | ||||
|         error = error_add(error, "The operation timed out."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_TIMEOUT); | ||||
|         break; | ||||
|     case ERROR_OPERATION_ABORTED: | ||||
|         skip_windows_message = true; | ||||
|         error = error_add(error, "The operation was cancelled."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_CANCELLED); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     // Get an English sentence from Windows to help describe the error. | ||||
|     if (!skip_windows_message) | ||||
|     { | ||||
|         char buffer[256]; | ||||
|         DWORD size = FormatMessage( | ||||
|             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, | ||||
|             NULL, error_code, | ||||
|             MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, | ||||
|             sizeof(buffer), NULL); | ||||
|         if (size != 0) | ||||
|         { | ||||
|             // Remove the leading space and then add the message to the error. | ||||
|             if (buffer[size - 1] == ' ') { buffer[--size] = 0; } | ||||
|             error = error_add(error, "%s", buffer); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Finally, add the context message provided by the caller. | ||||
|     return error_add_v(error, format, ap); | ||||
| } | ||||
|  | ||||
| libusbp_error * error_create_winapi(const char * format, ...) | ||||
| { | ||||
|     DWORD error_code = GetLastError(); | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     libusbp_error * error = error_create_winapi_v(error_code, format, ap); | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| // This is for reporting an error from WinUsb_GetOverlappedResult.  This | ||||
| // function is necessary because ERROR_FILE_NOT_FOUND has a special meaning if | ||||
| // it is the result of an asynchronous USB transfer. | ||||
| libusbp_error * error_create_overlapped(const char * format, ...) | ||||
| { | ||||
|     DWORD error_code = GetLastError(); | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     switch(error_code) | ||||
|     { | ||||
|     case ERROR_FILE_NOT_FOUND: | ||||
|         error = error_create("Windows error code 0x%lx.", error_code); | ||||
|         error = error_add(error, "The device was disconnected."); | ||||
|         error = error_add_code(error, LIBUSBP_ERROR_DEVICE_DISCONNECTED); | ||||
|         error = error_add_v(error, format, ap); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         error = error_create_winapi_v(error_code, format, ap); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * error_create_cr(CONFIGRET cr, const char * format, ...) | ||||
| { | ||||
|     libusbp_error * error = error_create("CONFIGRET error code 0x%lx.", cr); | ||||
|  | ||||
|     va_list ap; | ||||
|     va_start(ap, format); | ||||
|     error = error_add_v(error, format, ap); | ||||
|     va_end(ap); | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										316
									
								
								dep/libusbp/src/windows/generic_handle_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								dep/libusbp/src/windows/generic_handle_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,316 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| // TODO: add a test for the issue where WinUSB cannot send data from read-only | ||||
| // memory, then add a note about it to the documentation for | ||||
| // libusbp_control_transfer and PLATFORM_NOTES.md. | ||||
|  | ||||
| struct libusbp_generic_handle | ||||
| { | ||||
|     HANDLE file_handle; | ||||
|     WINUSB_INTERFACE_HANDLE winusb_handle; | ||||
| }; | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     libusbp_generic_handle ** gh | ||||
| ) | ||||
| { | ||||
|     if (gh == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *gh = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Allocate memory for the struct. | ||||
|     libusbp_generic_handle * new_gh = malloc(sizeof(libusbp_generic_handle)); | ||||
|     if (new_gh == NULL) | ||||
|     { | ||||
|         error = &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     // Initialize the handles so we can clean up properly in case an error | ||||
|     // happens. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gh->file_handle = INVALID_HANDLE_VALUE; | ||||
|         new_gh->winusb_handle = INVALID_HANDLE_VALUE; | ||||
|     } | ||||
|  | ||||
|     // Get the filename. | ||||
|     char * filename = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_generic_interface_get_os_filename(gi, &filename); | ||||
|     } | ||||
|  | ||||
|     // Open the file.  We have observed this step failing with error code | ||||
|     // ERROR_NOT_FOUND if the device was recently unplugged. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_gh->file_handle = CreateFile(filename, | ||||
|             GENERIC_READ | GENERIC_WRITE, | ||||
|             FILE_SHARE_READ | FILE_SHARE_WRITE, | ||||
|             NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | ||||
|  | ||||
|         if (new_gh->file_handle == INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to open generic handle."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Initialize WinUSB. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         BOOL success = WinUsb_Initialize(new_gh->file_handle, &new_gh->winusb_handle); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to initialize WinUSB."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         // Success. | ||||
|         *gh = new_gh; | ||||
|         new_gh = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_string_free(filename); | ||||
|     libusbp_generic_handle_close(new_gh); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_handle_close(libusbp_generic_handle * gh) | ||||
| { | ||||
|     if (gh != NULL) | ||||
|     { | ||||
|         if (gh->winusb_handle != INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             WinUsb_Free(gh->winusb_handle); | ||||
|         } | ||||
|  | ||||
|         if (gh->file_handle != INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             CloseHandle(gh->file_handle); | ||||
|         } | ||||
|  | ||||
|         free(gh); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_open_async_in_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     libusbp_async_in_pipe ** pipe) | ||||
| { | ||||
|     return async_in_pipe_create(handle, pipe_id, pipe); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_handle_set_timeout( | ||||
|     libusbp_generic_handle * gh, | ||||
|     uint8_t pipe_id, | ||||
|     uint32_t timeout) | ||||
| { | ||||
|     if (gh == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     ULONG winusb_timeout = timeout; | ||||
|     BOOL success = WinUsb_SetPipePolicy(gh->winusb_handle, pipe_id, | ||||
|         PIPE_TRANSFER_TIMEOUT, sizeof(winusb_timeout), &winusb_timeout); | ||||
|     if (!success) | ||||
|     { | ||||
|         return error_create_winapi("Failed to set timeout."); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_control_transfer( | ||||
|     libusbp_generic_handle * gh, | ||||
|     uint8_t bmRequestType, | ||||
|     uint8_t bRequest, | ||||
|     uint16_t wValue, | ||||
|     uint16_t wIndex, | ||||
|     void * data, | ||||
|     uint16_t wLength, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (gh == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     if (wLength != 0 && data == NULL) | ||||
|     { | ||||
|         return error_create("Control transfer buffer was null while wLength was non-zero."); | ||||
|     } | ||||
|  | ||||
|     WINUSB_SETUP_PACKET packet; | ||||
|     packet.RequestType = bmRequestType; | ||||
|     packet.Request = bRequest; | ||||
|     packet.Value = wValue; | ||||
|     packet.Index = wIndex; | ||||
|     packet.Length = wLength; | ||||
|  | ||||
|     ULONG winusb_transferred; | ||||
|     BOOL success = WinUsb_ControlTransfer(gh->winusb_handle, packet, data, | ||||
|         wLength, &winusb_transferred, NULL); | ||||
|     if (!success) | ||||
|     { | ||||
|         return error_create_winapi("Control transfer failed."); | ||||
|     } | ||||
|  | ||||
|     if (transferred) | ||||
|     { | ||||
|         *transferred = winusb_transferred; | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_write_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     const void * data, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_out(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && size > ULONG_MAX) | ||||
|     { | ||||
|         error = error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && data == NULL && size) | ||||
|     { | ||||
|         error = error_create("Buffer is null."); | ||||
|     } | ||||
|  | ||||
|     ULONG winusb_transferred = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         BOOL success = WinUsb_WritePipe(handle->winusb_handle, pipe_id, | ||||
|             (uint8_t *)data, size, &winusb_transferred, NULL); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create_winapi(""); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (transferred) | ||||
|     { | ||||
|         *transferred = winusb_transferred; | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to write to pipe."); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_read_pipe( | ||||
|     libusbp_generic_handle * handle, | ||||
|     uint8_t pipe_id, | ||||
|     void * data, | ||||
|     size_t size, | ||||
|     size_t * transferred) | ||||
| { | ||||
|     if (transferred != NULL) | ||||
|     { | ||||
|         *transferred = 0; | ||||
|     } | ||||
|  | ||||
|     if (handle == NULL) | ||||
|     { | ||||
|         return error_create("Generic handle is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = check_pipe_id_in(pipe_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && size == 0) | ||||
|     { | ||||
|         error = error_create("Transfer size 0 is not allowed."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && size > ULONG_MAX) | ||||
|     { | ||||
|         error = error_create("Transfer size is too large."); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL && data == NULL) | ||||
|     { | ||||
|         error = error_create("Buffer is null."); | ||||
|     } | ||||
|  | ||||
|     ULONG winusb_transferred = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         BOOL success = WinUsb_ReadPipe(handle->winusb_handle, pipe_id, data, | ||||
|             size, &winusb_transferred, NULL); | ||||
|         if (!success) | ||||
|         { | ||||
|             error = error_create_winapi(""); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (transferred) | ||||
|     { | ||||
|         *transferred = winusb_transferred; | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to read from pipe."); | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| HANDLE libusbp_generic_handle_get_winusb_handle(libusbp_generic_handle * handle) | ||||
| { | ||||
|     if (handle == NULL) { return INVALID_HANDLE_VALUE; } | ||||
|     return handle->winusb_handle; | ||||
| } | ||||
|  | ||||
| libusbp_error * generic_handle_events(libusbp_generic_handle * handle) | ||||
| { | ||||
|     LIBUSBP_UNUSED(handle); | ||||
|     return NULL; | ||||
| } | ||||
							
								
								
									
										366
									
								
								dep/libusbp/src/windows/generic_interface_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								dep/libusbp/src/windows/generic_interface_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_generic_interface | ||||
| { | ||||
|     uint8_t interface_number; | ||||
|     char * device_instance_id; | ||||
|     char * filename; | ||||
| }; | ||||
|  | ||||
| // Returns NULL via the driver_name parameter if no driver is installed. | ||||
| // Otherwise returns the driver name. | ||||
| static libusbp_error * get_driver_name( | ||||
|     HDEVINFO list, PSP_DEVINFO_DATA info, char ** driver_name) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(driver_name != NULL); | ||||
|  | ||||
|     *driver_name = NULL; | ||||
|  | ||||
|     // Get the "Service" key from the registry, which will say what | ||||
|     // Windows service is assigned to this device. | ||||
|     CHAR service[64]; | ||||
|     DWORD data_type; | ||||
|     bool success = SetupDiGetDeviceRegistryProperty(list, info, SPDRP_SERVICE, | ||||
|         &data_type, (BYTE *)&service, sizeof(service), NULL); | ||||
|     if (!success) | ||||
|     { | ||||
|         if (GetLastError() == ERROR_INVALID_DATA) | ||||
|         { | ||||
|             // The registry key isn't present, so the device does not | ||||
|             // have have a driver. | ||||
|             return NULL; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return error_create_winapi("Failed to get the service name."); | ||||
|         } | ||||
|     } | ||||
|     if (data_type != REG_SZ) | ||||
|     { | ||||
|         return error_create("Service name is the wrong data type: %ld.", data_type); | ||||
|     } | ||||
|  | ||||
|     return string_copy(service, driver_name); | ||||
| } | ||||
|  | ||||
| // Checks to see if a suitable driver for a USB generic interface is installed | ||||
| // by reading the Service registry key. | ||||
| static libusbp_error * check_driver_installation( | ||||
|     HDEVINFO list, PSP_DEVINFO_DATA info, | ||||
|     bool * good_driver_installed, bool * bad_driver_installed, | ||||
|     char ** driver_name) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(good_driver_installed != NULL); | ||||
|     assert(bad_driver_installed != NULL); | ||||
|     assert(driver_name != NULL); | ||||
|  | ||||
|     *good_driver_installed = false; | ||||
|     *bad_driver_installed = false; | ||||
|     *driver_name = NULL; | ||||
|  | ||||
|     libusbp_error * error; | ||||
|  | ||||
|     error = get_driver_name(list, info, driver_name); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     if (*driver_name == NULL) | ||||
|     { | ||||
|         // No driver installed. | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // We really don't want this comparison to be affected by the user's locale, | ||||
|     // since that will probably just mess things up. | ||||
|     int result = CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, | ||||
|         *driver_name, -1, "winusb", -1); | ||||
|     if (result == CSTR_EQUAL) | ||||
|     { | ||||
|         *good_driver_installed = true; | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     *bad_driver_installed = true; | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static libusbp_error * get_first_device_interface_guid(HDEVINFO list, | ||||
|     PSP_DEVINFO_DATA info, GUID * guid) | ||||
| { | ||||
|     assert(list != INVALID_HANDLE_VALUE); | ||||
|     assert(info != NULL); | ||||
|     assert(guid != NULL); | ||||
|  | ||||
|     HANDLE key = SetupDiOpenDevRegKey(list, info, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); | ||||
|     if (key == INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         return error_create_winapi( | ||||
|             "Failed to get device registry key in order to find its device interface GUIDs."); | ||||
|     } | ||||
|  | ||||
|     // Get the size of the DeviceInterfaceGUIDs key. | ||||
|     // (Partial reads are not allowed.) | ||||
|     DWORD size; | ||||
|     LONG reg_result; | ||||
|     reg_result = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, NULL, NULL, &size); | ||||
|     if (reg_result != ERROR_SUCCESS) | ||||
|     { | ||||
|         if (reg_result == ERROR_FILE_NOT_FOUND) | ||||
|         { | ||||
|             RegCloseKey(key); | ||||
|             return error_create("DeviceInterfaceGUIDs key does not exist."); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             RegCloseKey(key); | ||||
|             SetLastError(reg_result); | ||||
|             return error_create_winapi("Failed to get DeviceInterfaceGUIDs key size."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     WCHAR * guids = malloc(size); | ||||
|     if (guids == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     DWORD reg_type; | ||||
|     reg_result = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", | ||||
|         NULL, ®_type, (BYTE *)guids, &size); | ||||
|     RegCloseKey(key); | ||||
|     if (reg_result) | ||||
|     { | ||||
|         free(guids); | ||||
|         SetLastError(reg_result); | ||||
|         return error_create_winapi("Failed to get DeviceInterfaceGUIDs key."); | ||||
|     } | ||||
|  | ||||
|     if (reg_type != REG_MULTI_SZ) | ||||
|     { | ||||
|         free(guids); | ||||
|         return error_create( | ||||
|             "Expected DeviceInterfaceGUIDs key to be a REG_MULTI_SZ (0x%x), got 0x%lx.", | ||||
|             REG_MULTI_SZ, reg_type); | ||||
|     } | ||||
|  | ||||
|     HRESULT hr = IIDFromString(guids, guid); | ||||
|     free(guids); | ||||
|     if (FAILED(hr)) | ||||
|     { | ||||
|         return error_create_hr(hr, "Failed to parse device interface GUID."); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static libusbp_error * generic_interface_initialize(libusbp_generic_interface * gi, | ||||
|     const libusbp_device * device, uint8_t interface_number, bool composite) | ||||
| { | ||||
|     assert(gi != NULL); | ||||
|     assert(device != NULL); | ||||
|  | ||||
|     // First, initialize everything so that the struct can safely be | ||||
|     // freed if something goes wrong. | ||||
|     gi->interface_number = interface_number; | ||||
|     gi->device_instance_id = NULL; | ||||
|     gi->filename = NULL; | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Get the device instance ID of the overall USB device. | ||||
|     char * usb_device_id; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_os_id(device, &usb_device_id); | ||||
|     } | ||||
|  | ||||
|     // Access the generic interface device node with SetupAPI. | ||||
|     HDEVINFO list = INVALID_HANDLE_VALUE; | ||||
|     SP_DEVINFO_DATA device_info_data; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_interface(usb_device_id, interface_number, | ||||
|             composite, &list, &device_info_data); | ||||
|     } | ||||
|  | ||||
|     // Record the device instance ID. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = create_id_string(list, &device_info_data, &gi->device_instance_id); | ||||
|     } | ||||
|  | ||||
|     // Check the driver situation. | ||||
|     char * driver_name = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         bool good_driver_installed = false; | ||||
|         bool bad_driver_installed = false; | ||||
|         error = check_driver_installation(list, &device_info_data, | ||||
|             &good_driver_installed, &bad_driver_installed, &driver_name); | ||||
|         if (error == NULL && bad_driver_installed) | ||||
|         { | ||||
|             error = error_create("Device is attached to an incorrect driver: %s.", driver_name); | ||||
|         } | ||||
|         if (error == NULL && !good_driver_installed) | ||||
|         { | ||||
|             error = error_create("Device is not using any driver."); | ||||
|             error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the first device interface GUID. | ||||
|     GUID guid; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|       error = get_first_device_interface_guid(list, &device_info_data, &guid); | ||||
|     } | ||||
|  | ||||
|     // Use that GUID to get an actual filename that we can later open | ||||
|     // with CreateFile to access the device. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_filename_from_devinst_and_guid(device_info_data.DevInst, | ||||
|             &guid, &gi->filename); | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|  | ||||
|     if (list != INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|     } | ||||
|     libusbp_string_free(driver_name); | ||||
|     libusbp_string_free(usb_device_id); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to initialize generic interface."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_generic_interface ** gi) | ||||
| { | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *gi = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_generic_interface * new_gi = malloc(sizeof(libusbp_generic_interface)); | ||||
|     if (new_gi == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = generic_interface_initialize(new_gi, device, | ||||
|         interface_number, composite); | ||||
|     if (error) | ||||
|     { | ||||
|         libusbp_generic_interface_free(new_gi); | ||||
|         return error; | ||||
|     } | ||||
|     *gi = new_gi; | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_copy( | ||||
|     const libusbp_generic_interface * source, | ||||
|     libusbp_generic_interface ** dest | ||||
| ) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     assert(source->device_instance_id); | ||||
|     assert(source->filename); | ||||
|  | ||||
|     libusbp_generic_interface * new_gi = calloc(1, sizeof(libusbp_generic_interface)); | ||||
|     char * id = strdup(source->device_instance_id); | ||||
|     char * filename = strdup(source->filename); | ||||
|     if (new_gi == NULL || id == NULL || filename == NULL) | ||||
|     { | ||||
|         free(new_gi); | ||||
|         free(id); | ||||
|         free(filename); | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     new_gi->interface_number = source->interface_number; | ||||
|     new_gi->device_instance_id = id; | ||||
|     new_gi->filename = filename; | ||||
|     *dest = new_gi; | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void libusbp_generic_interface_free(libusbp_generic_interface * gi) | ||||
| { | ||||
|     if (gi != NULL) | ||||
|     { | ||||
|         free(gi->device_instance_id); | ||||
|         free(gi->filename); | ||||
|         free(gi); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_id( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     char ** id) | ||||
| { | ||||
|     if (id == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *id = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|     return string_copy(gi->device_instance_id, id); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_generic_interface_get_os_filename( | ||||
|     const libusbp_generic_interface * gi, | ||||
|     char ** filename) | ||||
| { | ||||
|     if (filename == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *filename = NULL; | ||||
|  | ||||
|     if (gi == NULL) | ||||
|     { | ||||
|         return error_create("Generic interface is null."); | ||||
|     } | ||||
|     return string_copy(gi->filename, filename); | ||||
| } | ||||
							
								
								
									
										322
									
								
								dep/libusbp/src/windows/interface_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								dep/libusbp/src/windows/interface_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,322 @@ | ||||
| // If we are compiling under MSVC, this is the file that will define GUID | ||||
| // symbols we need such as GUID_DEVINTERFACE_USB_DEVICE.  This prevents | ||||
| // an unresolved symbol error at link time. | ||||
| #ifdef _MSC_VER | ||||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| #include <initguid.h> | ||||
| #endif | ||||
|  | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| static libusbp_error * get_interface_non_composite( | ||||
|     const char * device_instance_id, | ||||
|     HDEVINFO * list, | ||||
|     PSP_DEVINFO_DATA info) | ||||
| { | ||||
|     *list = INVALID_HANDLE_VALUE; | ||||
|  | ||||
|     // This is a non-composite device, so we just want to find a device | ||||
|     // with the same device instance ID. | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     char id[MAX_DEVICE_ID_LEN + 1]; | ||||
|  | ||||
|     HDEVINFO new_list = INVALID_HANDLE_VALUE; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_list = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, | ||||
|             DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | ||||
|         if (list == INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to list all USB devices."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         for(DWORD i = 0; ; i++) | ||||
|         { | ||||
|             SP_DEVINFO_DATA device_info_data; | ||||
|             device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); | ||||
|             bool success = SetupDiEnumDeviceInfo(new_list, i, &device_info_data); | ||||
|             if (!success) | ||||
|             { | ||||
|                 if (GetLastError() == ERROR_NO_MORE_ITEMS) | ||||
|                 { | ||||
|                     error = error_create("Failed to find device node."); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     error = error_create_winapi("Failed to enumerate device info."); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             success = SetupDiGetDeviceInstanceId(new_list, &device_info_data, | ||||
|                 id, sizeof(id), NULL); | ||||
|             if (!success) | ||||
|             { | ||||
|                 error = error_create_winapi("Failed to get device instance ID."); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (strcmp(id, device_instance_id) == 0) | ||||
|             { | ||||
|                 // We found the device; success. | ||||
|                 *list = new_list; | ||||
|                 new_list = INVALID_HANDLE_VALUE; | ||||
|                 *info = device_info_data; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (new_list != INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         SetupDiDestroyDeviceInfoList(new_list); | ||||
|     } | ||||
|  | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         error = error_add(error, "Failed to find non-composite device node."); | ||||
|     } | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| static libusbp_error * get_interface_composite( | ||||
|     const char * device_instance_id, | ||||
|     uint8_t interface_number, | ||||
|     HDEVINFO * list, | ||||
|     PSP_DEVINFO_DATA info) | ||||
| { | ||||
|     // This is a composite device, and we need to find a device | ||||
|     // whose parent has the specified device_instance_id, and | ||||
|     // whose own device instance ID has "MI_xx" where xx is the | ||||
|     // hex representation of interface_number. | ||||
|  | ||||
|     *list = INVALID_HANDLE_VALUE; | ||||
|  | ||||
|     // Get the DEVINST for this device. | ||||
|     CONFIGRET cr; | ||||
|     DEVINST dev_inst; | ||||
|     cr = CM_Locate_DevNode(&dev_inst, (char *)device_instance_id, CM_LOCATE_DEVNODE_NORMAL); | ||||
|     if (cr != CR_SUCCESS) | ||||
|     { | ||||
|         libusbp_error * error = error_create_cr(cr, | ||||
|             "Failed to get device node in order to find an interface."); | ||||
|  | ||||
|         // NOTE: if cr == CR_NO_SUCH_DEVNODE, that means | ||||
|         // The device instance ID has a valid format, but either | ||||
|         // the device it was referring to is unplugged or it was | ||||
|         // never plugged into the computer in the first place. | ||||
|         // This error has been seen when unplugging a USB device. | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     // Get a list of all the USB-related devices. | ||||
|     HDEVINFO new_list = SetupDiGetClassDevs(NULL, "USB", NULL, | ||||
|         DIGCF_ALLCLASSES | DIGCF_PRESENT); | ||||
|     if (new_list == INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         return error_create_winapi( | ||||
|             "Failed to get list of all USB devices while finding an interface."); | ||||
|     } | ||||
|  | ||||
|     // Iterate through the list until we find a device whose | ||||
|     // parent device is ours and which controls the interface | ||||
|     // specified by the caller. | ||||
|     for (DWORD i = 0; ; i++) | ||||
|     { | ||||
|         SP_DEVINFO_DATA device_info_data; | ||||
|         device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); | ||||
|         bool success = SetupDiEnumDeviceInfo(new_list, i, &device_info_data); | ||||
|         if (!success) | ||||
|         { | ||||
|             libusbp_error * error; | ||||
|  | ||||
|             if (GetLastError() == ERROR_NO_MORE_ITEMS) | ||||
|             { | ||||
|                 // Could not find the child interface.  This could be | ||||
|                 // a temporary condition. | ||||
|                 error = error_create("Could not find interface %d.", | ||||
|                     interface_number); | ||||
|                 error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 error = error_create_winapi( | ||||
|                     "Failed to get device info while finding an interface."); | ||||
|             } | ||||
|             SetupDiDestroyDeviceInfoList(new_list); | ||||
|             return error; | ||||
|         } | ||||
|  | ||||
|         DEVINST parent_dev_inst; | ||||
|         cr = CM_Get_Parent(&parent_dev_inst, device_info_data.DevInst, 0); | ||||
|         if (cr != CR_SUCCESS) | ||||
|         { | ||||
|             SetupDiDestroyDeviceInfoList(new_list); | ||||
|             return error_create_cr(cr, "Failed to get parent of an interface."); | ||||
|         } | ||||
|  | ||||
|         if (parent_dev_inst != dev_inst) | ||||
|         { | ||||
|             // This device is not a child of our device. | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // Get the device instance ID. | ||||
|         char device_id[MAX_DEVICE_ID_LEN + 1]; | ||||
|         cr = CM_Get_Device_ID(device_info_data.DevInst, device_id, sizeof(device_id), 0); | ||||
|         if (cr != CR_SUCCESS) | ||||
|         { | ||||
|             libusbp_error * error = error_create_cr(cr, | ||||
|                 "Failed to get device instance ID while finding an interface."); | ||||
|             SetupDiDestroyDeviceInfoList(new_list); | ||||
|             return error; | ||||
|         } | ||||
|  | ||||
|         unsigned int actual_interface_number; | ||||
|         int result = sscanf(device_id, "USB\\VID_%*4x&PID_%*4x&MI_%2x\\", | ||||
|             &actual_interface_number); | ||||
|         if (result != 1 || actual_interface_number != interface_number) | ||||
|         { | ||||
|             // This is not the right interface. | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // Found the interface. | ||||
|         *list = new_list; | ||||
|         *info = device_info_data; | ||||
|         return NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * get_interface( | ||||
|     const char * device_instance_id, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     HDEVINFO * list, | ||||
|     PSP_DEVINFO_DATA info) | ||||
| { | ||||
|     assert(device_instance_id != NULL); | ||||
|     assert(list != NULL); | ||||
|     assert(info != NULL); | ||||
|  | ||||
|     if (composite) | ||||
|     { | ||||
|         return get_interface_composite(device_instance_id, interface_number, | ||||
|             list, info); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return get_interface_non_composite(device_instance_id, list, info); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * get_filename_from_devinst_and_guid( | ||||
|     DEVINST devinst, | ||||
|     const GUID * guid, | ||||
|     char ** filename | ||||
|     ) | ||||
| { | ||||
|     assert(guid != NULL); | ||||
|     assert(filename != NULL); | ||||
|  | ||||
|     BOOL success; | ||||
|  | ||||
|     // Make a list of devices that have the specified device interface GUID. | ||||
|     HDEVINFO list = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | ||||
|     if (list == INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         return error_create_winapi( | ||||
|             "Failed to create a list of devices to find a device filename."); | ||||
|     } | ||||
|  | ||||
|     // Iterate through the list looking for one that matches the given DEVINST. | ||||
|     SP_DEVINFO_DATA device_info_data; | ||||
|     for(DWORD index = 0; ; index++) | ||||
|     { | ||||
|         device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); | ||||
|         success = SetupDiEnumDeviceInfo(list, index, &device_info_data); | ||||
|         if (!success) | ||||
|         { | ||||
|             if (GetLastError() == ERROR_NO_MORE_ITEMS) | ||||
|             { | ||||
|                 // We reached the end of the list. | ||||
|                 SetupDiDestroyDeviceInfoList(list); | ||||
|                 libusbp_error * error = error_create( | ||||
|                     "Could not find matching device in order to get its filename."); | ||||
|  | ||||
|                 // This is one of the errors we see when plugging in a device, | ||||
|                 // so it could indicate that the devce is just not ready and | ||||
|                 // Windows is still setting it up. | ||||
|                 error = error_add_code(error, LIBUSBP_ERROR_NOT_READY); | ||||
|                 return error; | ||||
|             } | ||||
|  | ||||
|             libusbp_error * error = error_create_winapi( | ||||
|                 "Failed to enumerate list item to find matching device."); | ||||
|             SetupDiDestroyDeviceInfoList(list); | ||||
|             return error; | ||||
|         } | ||||
|  | ||||
|         if (device_info_data.DevInst == devinst) | ||||
|         { | ||||
|             // We found the matching device, so break. | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the DeviceInterfaceData struct. | ||||
|     SP_DEVICE_INTERFACE_DATA device_interface_data; | ||||
|     device_interface_data.cbSize = sizeof(device_interface_data); | ||||
|     success = SetupDiEnumDeviceInterfaces(list, &device_info_data, guid, 0, &device_interface_data); | ||||
|     if (!success) | ||||
|     { | ||||
|         libusbp_error * error = error_create_winapi("Failed to get device interface data."); | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     // Get the DeviceInterfaceDetailData struct size. | ||||
|     DWORD size; | ||||
|     success = SetupDiGetDeviceInterfaceDetail(list, &device_interface_data, NULL, 0, &size, NULL); | ||||
|     if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) | ||||
|     { | ||||
|         libusbp_error * error = error_create_winapi("Failed to get the size of the device interface details."); | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     // Get the DeviceInterfaceDetailData struct data | ||||
|     SP_DEVICE_INTERFACE_DETAIL_DATA_A * device_interface_detail_data = malloc(size); | ||||
|     if (device_interface_detail_data == NULL) | ||||
|     { | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|     device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); | ||||
|     success = SetupDiGetDeviceInterfaceDetail(list, &device_interface_data, | ||||
|         device_interface_detail_data, size, NULL, NULL); | ||||
|     if (!success) | ||||
|     { | ||||
|         libusbp_error * error = error_create_winapi("Failed to get the device interface details."); | ||||
|         free(device_interface_detail_data); | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|         return error; | ||||
|     } | ||||
|  | ||||
|     char * new_string = strdup(device_interface_detail_data->DevicePath); | ||||
|     if (new_string == NULL) | ||||
|     { | ||||
|         free(device_interface_detail_data); | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|         return &error_no_memory; | ||||
|     } | ||||
|  | ||||
|     *filename = new_string; | ||||
|  | ||||
|     free(device_interface_detail_data); | ||||
|     SetupDiDestroyDeviceInfoList(list); | ||||
|     return NULL; | ||||
| } | ||||
							
								
								
									
										109
									
								
								dep/libusbp/src/windows/list_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								dep/libusbp/src/windows/list_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| #if SIZE_MAX < DWORD_MAX | ||||
| #error The code in get_list_length assumes a size_t can hold any DWORD. | ||||
| #endif | ||||
|  | ||||
| static void try_create_device(HDEVINFO list, PSP_DEVINFO_DATA info, | ||||
|     libusbp_device ** device) | ||||
| { | ||||
|     libusbp_error * error = create_device(list, info, device); | ||||
|     if (error != NULL) | ||||
|     { | ||||
|         assert(*device == NULL); | ||||
|  | ||||
|         // Something went wrong.  For example, one of the devices might have | ||||
|         // lacked a hardware ID with the proper format.  To make the library | ||||
|         // more robust and usable, we ignore this error and continue. | ||||
|         #ifdef LIBUSBP_LOG | ||||
|         fprintf(stderr, "Problem creating device: %s\n", | ||||
|             libusbp_error_get_message(error)); | ||||
|         #endif | ||||
|  | ||||
|         libusbp_error_free(error); | ||||
|     } | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_list_connected_devices(libusbp_device *** device_list, | ||||
|     size_t * device_count) | ||||
| { | ||||
|     // Return 0 for the device count by default, just to be safe. | ||||
|     if (device_count != NULL) | ||||
|     { | ||||
|         *device_count = 0; | ||||
|     } | ||||
|  | ||||
|     if (device_list == NULL) | ||||
|     { | ||||
|         return error_create("Device list output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     // Get a list of all USB devices from Windows. | ||||
|     HDEVINFO handle = INVALID_HANDLE_VALUE; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         handle = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, | ||||
|             DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | ||||
|         if (handle == INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             error = error_create_winapi("Failed to list all USB devices."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Start a new device list and keep track of how many devices are in it. | ||||
|     libusbp_device ** new_list = NULL; | ||||
|     size_t count = 0; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = device_list_create(&new_list); | ||||
|     } | ||||
|  | ||||
|     // Each iteration of this loop attempts to set up a new device. | ||||
|     DWORD index = 0; | ||||
|     while(error == NULL) | ||||
|     { | ||||
|         // See if we have reached the end of the list. | ||||
|         SP_DEVINFO_DATA info; | ||||
|         info.cbSize = sizeof(info); | ||||
|         BOOL success = SetupDiEnumDeviceInfo(handle, index, &info); | ||||
|         if (!success) | ||||
|         { | ||||
|             if (GetLastError() == ERROR_NO_MORE_ITEMS) | ||||
|             { | ||||
|                 // We have reached the end of the list. | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // An unexpected error happened. | ||||
|             error = error_create_winapi("Failed to test for the end of the USB device list."); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         libusbp_device * device = NULL; | ||||
|         try_create_device(handle, &info, &device); | ||||
|         if (device != NULL) | ||||
|         { | ||||
|             error = device_list_append(&new_list, &count, device); | ||||
|         } | ||||
|         index++; | ||||
|     } | ||||
|  | ||||
|     // Give the list and count to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *device_list = new_list; | ||||
|         new_list = NULL; | ||||
|  | ||||
|         if (device_count != NULL) | ||||
|         { | ||||
|             *device_count = count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     if (handle != INVALID_HANDLE_VALUE) { SetupDiDestroyDeviceInfoList(handle); } | ||||
|     if (new_list != NULL) { free_devices_and_list(new_list); } | ||||
|     return error; | ||||
| } | ||||
							
								
								
									
										207
									
								
								dep/libusbp/src/windows/serial_port_windows.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								dep/libusbp/src/windows/serial_port_windows.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| #include <libusbp_internal.h> | ||||
|  | ||||
| struct libusbp_serial_port | ||||
| { | ||||
|     char * device_instance_id; | ||||
|     char * port_name;  // e.g. "COM4" | ||||
| }; | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_create( | ||||
|     const libusbp_device * device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     libusbp_serial_port ** port) | ||||
| { | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *port = NULL; | ||||
|  | ||||
|     if (device == NULL) | ||||
|     { | ||||
|         return error_create("Device is null."); | ||||
|     } | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_serial_port * new_sp = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_sp = calloc(1, sizeof(libusbp_serial_port)); | ||||
|         if (new_sp == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the device instance ID of the overall USB device. | ||||
|     char * usb_device_id = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = libusbp_device_get_os_id(device, &usb_device_id); | ||||
|     } | ||||
|  | ||||
|     // Access the serial port interface device node with SetupAPI. | ||||
|     HDEVINFO list = INVALID_HANDLE_VALUE; | ||||
|     SP_DEVINFO_DATA device_info_data; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = get_interface(usb_device_id, interface_number, | ||||
|             composite, &list, &device_info_data); | ||||
|     } | ||||
|  | ||||
|     // Record the device instance ID. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = create_id_string(list, &device_info_data, &new_sp->device_instance_id); | ||||
|     } | ||||
|  | ||||
|     // Open the registry key for device-specific configuration information. | ||||
|     HANDLE keyDev = INVALID_HANDLE_VALUE; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         keyDev = SetupDiOpenDevRegKey(list, &device_info_data, | ||||
|             DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); | ||||
|         if (keyDev == INVALID_HANDLE_VALUE) | ||||
|         { | ||||
|             error = error_create_winapi( | ||||
|                 "Failed to get device registry key in order to find its COM port name."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the port name from the registry (e.g. "COM8"). | ||||
|     char portName[128]; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         DWORD size = sizeof(portName); | ||||
|         DWORD reg_type; | ||||
|         LONG reg_result = RegQueryValueEx(keyDev, "PortName", | ||||
|             NULL, ®_type, (BYTE *)portName, &size); | ||||
|         if (reg_result != ERROR_SUCCESS) | ||||
|         { | ||||
|             if (reg_result == ERROR_FILE_NOT_FOUND) | ||||
|             { | ||||
|                 error = error_create("The PortName key was not found."); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 SetLastError(reg_result); | ||||
|                 error = error_create_winapi("Failed to get PortName key."); | ||||
|             } | ||||
|         } | ||||
|         else if (reg_type != REG_SZ) | ||||
|         { | ||||
|             error = error_create( | ||||
|                 "Expected PortName key to be a REG_SZ (0x%x), got 0x%lx.", | ||||
|                 REG_SZ, reg_type); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Copy the port name into the serial port object. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(portName, &new_sp->port_name); | ||||
|     } | ||||
|  | ||||
|     // Give the new serial port to the caller. | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *port = new_sp; | ||||
|         new_sp = NULL; | ||||
|     } | ||||
|  | ||||
|     // Clean up. | ||||
|     if (keyDev != INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         RegCloseKey(keyDev); | ||||
|     } | ||||
|     if (list != INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         SetupDiDestroyDeviceInfoList(list); | ||||
|     } | ||||
|     libusbp_string_free(usb_device_id); | ||||
|     libusbp_serial_port_free(new_sp); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| void libusbp_serial_port_free(libusbp_serial_port * port) | ||||
| { | ||||
|     if (port == NULL) { return; } | ||||
|     libusbp_string_free(port->device_instance_id); | ||||
|     libusbp_string_free(port->port_name); | ||||
|     free(port); | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_copy(const libusbp_serial_port * source, | ||||
|     libusbp_serial_port ** dest) | ||||
| { | ||||
|     if (dest == NULL) | ||||
|     { | ||||
|         return error_create("Serial port output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *dest = NULL; | ||||
|  | ||||
|     if (source == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     assert(source->device_instance_id); | ||||
|     assert(source->port_name); | ||||
|  | ||||
|     libusbp_error * error = NULL; | ||||
|  | ||||
|     libusbp_serial_port * new_sp = NULL; | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         new_sp = calloc(1, sizeof(libusbp_serial_port)); | ||||
|         if (new_sp == NULL) | ||||
|         { | ||||
|             error = &error_no_memory; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->device_instance_id, &new_sp->device_instance_id); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         error = string_copy(source->port_name, &new_sp->port_name); | ||||
|     } | ||||
|  | ||||
|     if (error == NULL) | ||||
|     { | ||||
|         *dest = new_sp; | ||||
|         new_sp = NULL; | ||||
|     } | ||||
|  | ||||
|     libusbp_serial_port_free(new_sp); | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| libusbp_error * libusbp_serial_port_get_name( | ||||
|     const libusbp_serial_port * port, | ||||
|     char ** name) | ||||
| { | ||||
|     if (name == NULL) | ||||
|     { | ||||
|         return error_create("String output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     *name = NULL; | ||||
|  | ||||
|     if (port == NULL) | ||||
|     { | ||||
|         return error_create("Serial port is null."); | ||||
|     } | ||||
|  | ||||
|     return string_copy(port->port_name, name); | ||||
| } | ||||
|  | ||||
							
								
								
									
										41
									
								
								dep/libusbp/test/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								dep/libusbp/test/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| INCLUDE (CheckIncludeFileCXX) | ||||
|  | ||||
| # If catch.hpp is not present, we want to simply skip compiling the tests.  This | ||||
| # allows someone to compile and install libusbp without having catch installed. | ||||
| # The header can either be installed in this directory or in a standard system | ||||
| # location. | ||||
| set (CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") | ||||
| CHECK_INCLUDE_FILE_CXX (catch.hpp HAVE_CATCH_FRAMEWORK) | ||||
| if (NOT HAVE_CATCH_FRAMEWORK) | ||||
|   message (STATUS "The test suite will not be built.") | ||||
|   return () | ||||
| endif () | ||||
|  | ||||
| use_cxx11 () | ||||
|  | ||||
| set(USE_TEST_DEVICE_A FALSE CACHE BOOL | ||||
|   "Run tests that require Test Device A.") | ||||
|  | ||||
| set(USE_TEST_DEVICE_B FALSE CACHE BOOL | ||||
|   "Run tests that require Test Device B.") | ||||
|  | ||||
| file(GLOB test_sources *.cpp) | ||||
|  | ||||
| if (APPLE) | ||||
|   set (link_flags "-framework CoreFoundation ${link_flags}") | ||||
| endif () | ||||
|  | ||||
| add_executable(run_test ${test_sources}) | ||||
|  | ||||
| set_target_properties(run_test PROPERTIES | ||||
|   LINK_FLAGS "${link_flags}" | ||||
| ) | ||||
|  | ||||
| include_directories ( | ||||
|   "${CMAKE_CURRENT_SOURCE_DIR}" | ||||
|   "${CMAKE_SOURCE_DIR}/include" | ||||
|   "${CMAKE_SOURCE_DIR}/src" | ||||
|   "${CMAKE_BINARY_DIR}/src" | ||||
| ) | ||||
|  | ||||
| target_link_libraries(run_test usbp) | ||||
							
								
								
									
										749
									
								
								dep/libusbp/test/async_in_pipe_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										749
									
								
								dep/libusbp/test/async_in_pipe_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,749 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| // Note: A lot of these tests make specific assumptions about the timing of | ||||
| // various operations.  If the tests fail intermittently, some parameters may | ||||
| // need to be adjusted. | ||||
|  | ||||
| const uint8_t pipe_id = 0x82; | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| static void check_error_for_cancelled_transfer(const libusbp::error & error) | ||||
| { | ||||
|     if (!error) | ||||
|     { | ||||
|         // The transfer actually completed successfully. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (error.has_code(LIBUSBP_ERROR_CANCELLED)) | ||||
|     { | ||||
|         // This is expected. | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Some other error happened that we didn't expect. | ||||
|         throw error; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void clean_up_async_in_pipe(libusbp::async_in_pipe & pipe) | ||||
| { | ||||
|     pipe.cancel_transfers(); | ||||
|     test_timeout timeout(500); | ||||
|     while(pipe.has_pending_transfers()) | ||||
|     { | ||||
|         pipe.handle_events(); | ||||
|         libusbp::error transfer_error; | ||||
|         while(pipe.handle_finished_transfer(NULL, NULL, &transfer_error)) | ||||
|         { | ||||
|             check_error_for_cancelled_transfer(transfer_error); | ||||
|         } | ||||
|         timeout.check(); | ||||
|         sleep_quick(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void clean_up_async_in_pipe_and_expect_a_success(libusbp::async_in_pipe & pipe) | ||||
| { | ||||
|     pipe.cancel_transfers(); | ||||
|  | ||||
|     test_timeout timeout(500); | ||||
|     uint32_t success_count = 0; | ||||
|     while(pipe.has_pending_transfers()) | ||||
|     { | ||||
|         pipe.handle_events(); | ||||
|         libusbp::error transfer_error; | ||||
|         uint8_t buffer[64] = {0}; | ||||
|         size_t transferred; | ||||
|         while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error)) | ||||
|         { | ||||
|             check_error_for_cancelled_transfer(transfer_error); | ||||
|             if (!transfer_error) | ||||
|             { | ||||
|                 REQUIRE(buffer[4] == 0xAB); | ||||
|                 REQUIRE(transferred == 5); | ||||
|                 success_count++; | ||||
|             } | ||||
|         } | ||||
|         timeout.check(); | ||||
|         sleep_quick(); | ||||
|     } | ||||
|     REQUIRE(success_count > 0); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| TEST_CASE("async_in_pipe traits") | ||||
| { | ||||
|     libusbp::async_in_pipe pipe, pipe2; | ||||
|  | ||||
|     SECTION("is not copy-constructible") | ||||
|     { | ||||
|         REQUIRE(std::is_copy_constructible<libusbp::async_in_pipe>::value == false); | ||||
|  | ||||
|         // Should not compile: | ||||
|         // libusbp::async_in_pipe pipe3(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("is not copy-assignable") | ||||
|     { | ||||
|         REQUIRE(std::is_copy_assignable<libusbp::async_in_pipe>::value == false); | ||||
|  | ||||
|         // Should not compile: | ||||
|         // pipe2 = pipe; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
|  | ||||
| TEST_CASE("async_in_pipe basic properties") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     libusbp::async_in_pipe pipe = handle.open_async_in_pipe(pipe_id); | ||||
|  | ||||
|     SECTION("is present") | ||||
|     { | ||||
|         REQUIRE(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("is movable") | ||||
|     { | ||||
|         libusbp::async_in_pipe pipe2 = std::move(pipe); | ||||
|         REQUIRE(pipe2); | ||||
|         REQUIRE_FALSE(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("is move-assignable") | ||||
|     { | ||||
|         libusbp::async_in_pipe pipe2; | ||||
|         pipe2 = std::move(pipe); | ||||
|         REQUIRE(pipe2); | ||||
|         REQUIRE_FALSE(pipe); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("null async_in_pipe") | ||||
| { | ||||
|     libusbp::async_in_pipe pipe; | ||||
|     std::string expected_message = "Pipe argument is null."; | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         CHECK_FALSE(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot allocate transfers") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             pipe.allocate_transfers(4, 5); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot start endless transfers") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             pipe.start_endless_transfers(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot handle events") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot say if it has pending transfers") | ||||
|     { | ||||
|         // Test it this way to make sure the C++ wrapper throws an exception | ||||
|         // instead of ignoring it. | ||||
|         try | ||||
|         { | ||||
|             pipe.has_pending_transfers(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|  | ||||
|         // Also test it this way so we can check the "result" output parameter | ||||
|         // in the C API. | ||||
|         bool result = true; | ||||
|         libusbp::error error(libusbp_async_in_pipe_has_pending_transfers(NULL, &result)); | ||||
|         REQUIRE(error.message() == expected_message); | ||||
|         REQUIRE_FALSE(result); | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot handle a finished transfer") | ||||
|     { | ||||
|         uint8_t buffer[] = "hi there"; | ||||
|         size_t transferred = 10; | ||||
|         libusbp::error error = get_some_error(); | ||||
|         try | ||||
|         { | ||||
|             pipe.handle_finished_transfer(buffer, &transferred, &error); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|         CHECK(buffer[0] == 'h'); | ||||
|         CHECK(transferred == 0); | ||||
|         CHECK_FALSE(error); | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot cancel all transfers") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             pipe.cancel_transfers(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == expected_message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("async_in_pipe parameter validation and state checks") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     libusbp::async_in_pipe pipe = handle.open_async_in_pipe(0x82); | ||||
|  | ||||
|     SECTION("allocate_transfers") | ||||
|     { | ||||
|         SECTION("cannot be called twice on the same pipe") | ||||
|         { | ||||
|             pipe.allocate_transfers(1, 64); | ||||
|             try | ||||
|             { | ||||
|                 pipe.allocate_transfers(1, 64); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Transfers were already allocated for this pipe."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("does not allow transfer_count to be 0") | ||||
|         { | ||||
|             // Set the size to 0 also so we can test that the count is checked | ||||
|             // before the size (it's nice if the little details stay consistent | ||||
|             // over time and across platforms). | ||||
|             try | ||||
|             { | ||||
|                 pipe.allocate_transfers(0, 0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Transfer count cannot be zero."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("does not allow transfer_size to be 0") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 pipe.allocate_transfers(64, 0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Transfer size cannot be zero."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("rejects transfer sizes too large for the underlying APIs") | ||||
|         { | ||||
|             #ifdef _WIN32 | ||||
|             // On Windows, URB buffer sizes are represented by ULONGs. | ||||
|             if (SIZE_MAX <= ULONG_MAX) { return; } | ||||
|             size_t too_large_size = (size_t)ULONG_MAX + 1; | ||||
|             #endif | ||||
|  | ||||
|             #ifdef __linux__ | ||||
|             // On Linux, URB buffer sizes are represented by ints. | ||||
|             if (SIZE_MAX <= INT_MAX) { return; } | ||||
|             size_t too_large_size = (size_t)INT_MAX + 1; | ||||
|             #endif | ||||
|  | ||||
|             #ifdef __APPLE__ | ||||
|             size_t too_large_size = (size_t)UINT32_MAX + 1; | ||||
|             #endif | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 pipe.allocate_transfers(1, too_large_size); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == | ||||
|                     "Failed to allocate transfers for asynchronous IN pipe.  " | ||||
|                     "Transfer size is too large."); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("start_endless_transfers") | ||||
|     { | ||||
|         SECTION("complains if transfers were not allocated") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 pipe.start_endless_transfers(); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Pipe transfers have not been allocated yet."); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("has_pending_transfers") | ||||
|     { | ||||
|         SECTION("works even if transfers were not allocated") | ||||
|         { | ||||
|             REQUIRE_FALSE(pipe.has_pending_transfers()); | ||||
|         } | ||||
|  | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             libusbp::error error(libusbp_async_in_pipe_has_pending_transfers( | ||||
|                     pipe.pointer_get(), NULL)); | ||||
|             REQUIRE(error.message() == "Boolean output pointer is null."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("async_in_pipe for an interrupt endpoint") | ||||
| { | ||||
|     // If it works for an IN endpoint, it should work for a bulk endpoint too | ||||
|     // because the underlying APIs that libusbp uses allow us to treat those | ||||
|     // types of endpoints the same. | ||||
|  | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     libusbp::async_in_pipe pipe = handle.open_async_in_pipe(0x82); | ||||
|     const size_t transfer_size = 5; | ||||
|  | ||||
|     test_timeout timeout(500); | ||||
|  | ||||
|     SECTION("can do continuous transfers and then cancel them") | ||||
|     { | ||||
|         pipe.allocate_transfers(5, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|  | ||||
|         size_t finish_count = 0; | ||||
|         while(finish_count < 12) | ||||
|         { | ||||
|             // Don't use REQUIRE or CATCH here because then the number of | ||||
|             // assertions printed at the end of the tests will be unpredictable. | ||||
|             if (!pipe.has_pending_transfers()) { throw "No pending transfers."; } | ||||
|  | ||||
|             uint8_t buffer[transfer_size] = {0}; | ||||
|             size_t transferred; | ||||
|             libusbp::error transfer_error; | ||||
|             if (pipe.handle_finished_transfer(buffer, &transferred, &transfer_error)) | ||||
|             { | ||||
|                 if (transfer_error) { throw transfer_error; } | ||||
|  | ||||
|                 REQUIRE(buffer[4] == 0xAB); | ||||
|                 finish_count++; | ||||
|             } | ||||
|  | ||||
|             pipe.handle_events(); | ||||
|             timeout.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(pipe.has_pending_transfers()); | ||||
|  | ||||
|         clean_up_async_in_pipe(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelling a transfer before it is completed") | ||||
|     { | ||||
|         // Event order tested here: submit, cancel, "reap" | ||||
|  | ||||
|         // Make a lot of transfers and cancel then immediately to make sure at | ||||
|         // least one was cancelled before it was completed. | ||||
|  | ||||
|         #ifdef __linux__ | ||||
|  | ||||
|         // On a normal Linux machine it takes about 1 ms per transfer to cancel | ||||
|         // transfers.  (Maybe that is because we are using an endpoint with a 1 | ||||
|         // ms polling interval.)  On a VirtualBox machine running on a Windows | ||||
|         // guest, it takes about much longer: 20 ms per transfer.  To work | ||||
|         // around this slowness, we only allocate 20 transfers at a time in | ||||
|         // Linux. | ||||
|  | ||||
|         // Maybe later we should we provide a workaround that lets | ||||
|         // people close the generic_handle and all its pipes at the same time, | ||||
|         // thus saving their users from this painful wait, without causing | ||||
|         // memory leaks.  If the generic handle fd gets closed and replaced with | ||||
|         // -1, then it would be safe to just free all the resources for the URBs | ||||
|         // without actually cancelling them. | ||||
|  | ||||
|         // We would like to make the transfer count larger (500) to make this | ||||
|         // test more resilient to lag caused by the operating system, but that | ||||
|         // slows down the development process too much. | ||||
|         const size_t transfer_count = 20; | ||||
|  | ||||
|         #else | ||||
|  | ||||
|         const size_t transfer_count = 500; | ||||
|  | ||||
|         #endif | ||||
|  | ||||
|         pipe.allocate_transfers(transfer_count, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|  | ||||
|         //printf("Cancelling %d transfers\n", (int)transfer_count); | ||||
|         test_timeout cancel_timer(10000); | ||||
|         pipe.cancel_transfers(); | ||||
|         //printf("Done cancelling, took %d ms\n", cancel_timer.get_milliseconds()); | ||||
|  | ||||
|         size_t cancel_count = 0; | ||||
|         test_timeout timeout(10000); | ||||
|         while(pipe.has_pending_transfers()) | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             libusbp::error transfer_error; | ||||
|             while(pipe.handle_finished_transfer(NULL, NULL, &transfer_error)) | ||||
|             { | ||||
|                 check_error_for_cancelled_transfer(transfer_error); | ||||
|  | ||||
|                 if (transfer_error.has_code(LIBUSBP_ERROR_CANCELLED)) | ||||
|                 { | ||||
|                     cancel_count++; | ||||
|                 } | ||||
|             } | ||||
|             timeout.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         // Make sure at least one error from the cancelled transfers had the | ||||
|         // right error code. | ||||
|         //printf("Cancel count: %d\n", (int)cancel_count); | ||||
|         REQUIRE(cancel_count > 5); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelling a transfer after it is completed but before the libusbp knows") | ||||
|     { | ||||
|         // Event order tested here: submit, complete, cancel, "reap" | ||||
|  | ||||
|         pipe.allocate_transfers(1, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|         sleep_ms(30); | ||||
|         clean_up_async_in_pipe_and_expect_a_success(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelling a transfer after it is completed and the event is handled") | ||||
|     { | ||||
|         // Event order tested here: submit, complete, "reap", cancel | ||||
|  | ||||
|         pipe.allocate_transfers(1, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|         sleep_ms(30); | ||||
|         pipe.handle_events(); | ||||
|         clean_up_async_in_pipe_and_expect_a_success(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelling a partially completed transfer") | ||||
|     { | ||||
|         // Note: This test seems to always fail on Windows Vista and Windows 7 | ||||
|         // because WinUSB reports that 0 bytes have been transferred instead of | ||||
|         // 10.  It might be that older versions of WinUSB or of the USB stack | ||||
|         // didn't support returning data from a cancelled, partially completed | ||||
|         // transfer. | ||||
|  | ||||
|         // Assumption: there will be two packets queued up by Test Device A in | ||||
|         // its ping-pong buffers.  So when we tell it to pause the ADC for 100 | ||||
|         // ms, a three-packet transfer will quickly receive those two packets | ||||
|         // and then keep waiting for more. | ||||
|  | ||||
|         // Pause the ADC for 100 ms. | ||||
|         handle.control_transfer(0x40, 0xA0, 100, 0); | ||||
|  | ||||
|         pipe.allocate_transfers(1, transfer_size * 3); | ||||
|         pipe.start_endless_transfers(); | ||||
|         sleep_ms(20); | ||||
|  | ||||
|         pipe.cancel_transfers(); | ||||
|         test_timeout timeout(500); | ||||
|         while(pipe.has_pending_transfers()) | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             libusbp::error transfer_error; | ||||
|             uint8_t buffer[transfer_size * 3]; | ||||
|             size_t transferred; | ||||
|             while(pipe.handle_finished_transfer(buffer, &transferred, &transfer_error)) | ||||
|             { | ||||
|                 CHECK(transfer_error); | ||||
|                 check_error_for_cancelled_transfer(transfer_error); | ||||
|  | ||||
|                 #if defined(VBOX_LINUX_ON_WINDOWS) | ||||
|                 CHECK(transferred == 0); | ||||
|                 #elif defined(__APPLE__) | ||||
|                 CHECK(transferred == transfer_size); | ||||
|                 CHECK(buffer[4] == 0xAB); | ||||
|                 #else | ||||
|                 CHECK(transferred == transfer_size * 2); | ||||
|                 CHECK(buffer[4] == 0xAB); | ||||
|                 CHECK(buffer[9] == 0xAB); | ||||
|                 #endif | ||||
|             } | ||||
|             timeout.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         // Unpause the ADC. | ||||
|         handle.control_transfer(0x40, 0xA0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelling transfers twice is okay") | ||||
|     { | ||||
|         pipe.allocate_transfers(1, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|         pipe.cancel_transfers(); | ||||
|         clean_up_async_in_pipe(pipe); | ||||
|     } | ||||
|  | ||||
|     SECTION("might respect the time out from set_timeout") | ||||
|     { | ||||
|         #ifdef __linux__ | ||||
|         // On Linux, asynchronous requests cannot have a timeout, so this test | ||||
|         // will not pass.  If we want to implement a timeout feature, we would | ||||
|         // need to make some kind of timer like libusb does, and cancel the | ||||
|         // requests when the time is up.  It's probably fine for the user to | ||||
|         // just implement a timeout themselves using a separate time library. | ||||
|         return; | ||||
|         #endif | ||||
|  | ||||
|         #ifdef __APPLE__ | ||||
|         // On Mac OS X, interrupt endpoints cannot have a timeout, and our | ||||
|         // library simply doesn't use a timeout (we use ReadPipeAsync instead of | ||||
|         // ReadPipeAsyncTO). | ||||
|         return; | ||||
|         #endif | ||||
|  | ||||
|         // Pause the ADC for 200 ms. | ||||
|         handle.control_transfer(0x40, 0xA0, 200, 0); | ||||
|  | ||||
|         handle.set_timeout(pipe_id, 10); | ||||
|         pipe.allocate_transfers(5, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|  | ||||
|         uint32_t timeout_count = 0; | ||||
|         uint32_t success_count = 0; | ||||
|         test_timeout timeout(500); | ||||
|         while(timeout_count < 8) | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             libusbp::error transfer_error; | ||||
|             while(pipe.handle_finished_transfer(NULL, NULL, &transfer_error)) | ||||
|             { | ||||
|                 // We expect there to be 0 to 2 successes, and the rest of the | ||||
|                 // transfers will be timeouts. | ||||
|                 if (transfer_error == false) | ||||
|                 { | ||||
|                     // Successful transfer. | ||||
|                     success_count++; | ||||
|  | ||||
|                     if (timeout_count != 0) | ||||
|                     { | ||||
|                         throw "Got a success after a timeout."; | ||||
|                     } | ||||
|                     if (success_count > 2) | ||||
|                     { | ||||
|                         throw "Got too many successes."; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (transfer_error.has_code(LIBUSBP_ERROR_TIMEOUT)) | ||||
|                 { | ||||
|                     // Timeout. | ||||
|  | ||||
|                     const char * expected = | ||||
|                         "Asynchronous IN transfer failed.  " | ||||
|                         "The operation timed out.  " | ||||
|                         #ifdef _WIN32 | ||||
|                         "Windows error code 0x79." | ||||
|                         #endif | ||||
|                         ; | ||||
|                     REQUIRE(transfer_error.message() == expected); | ||||
|                     timeout_count++; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Unexpected error. | ||||
|                     throw transfer_error; | ||||
|                 } | ||||
|             } | ||||
|             timeout.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         // Clean up the pipe, while accepting that there might be more transfers | ||||
|         // finishing with a cancellation error. | ||||
|         pipe.cancel_transfers(); | ||||
|         test_timeout timeout_cleanup(500); | ||||
|         while(pipe.has_pending_transfers()) | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             libusbp::error transfer_error; | ||||
|             while(pipe.handle_finished_transfer(NULL, NULL, &transfer_error)) | ||||
|             { | ||||
|                 if (transfer_error | ||||
|                     && !transfer_error.has_code(LIBUSBP_ERROR_CANCELLED) | ||||
|                     && !transfer_error.has_code(LIBUSBP_ERROR_TIMEOUT)) | ||||
|                 { | ||||
|                     throw transfer_error; | ||||
|                 } | ||||
|             } | ||||
|             timeout_cleanup.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         // Unpause the ADC. | ||||
|         handle.control_transfer(0x40, 0xA0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("overflow") | ||||
|     { | ||||
|         #ifdef VBOX_LINUX_ON_WINDOWS | ||||
|         // This test fails and then puts the USB device into a weird state | ||||
|         // if run on Linux inside VirtualBox on a Windows host. | ||||
|         std::cerr << "Skipping asynchronous IN pipe overflow test.\n"; | ||||
|         return; | ||||
|         #endif | ||||
|  | ||||
|         pipe.allocate_transfers(1, transfer_size + 1); | ||||
|         pipe.start_endless_transfers(); | ||||
|         sleep_ms(10); | ||||
|         pipe.handle_events(); | ||||
|  | ||||
| 	uint8_t buffer[transfer_size] = {0}; | ||||
| 	size_t transferred; | ||||
| 	libusbp::error transfer_error; | ||||
| 	bool finished = pipe.handle_finished_transfer(buffer, | ||||
|             &transferred, &transfer_error); | ||||
|         REQUIRE(finished); | ||||
|  | ||||
|         std::string expected_message; | ||||
|         size_t expected_transferred; | ||||
|         #ifdef _WIN32 | ||||
|         // This request is an error in WinUSB since we are using RAW_IO.  The | ||||
|         // error is detected before any data is transferred. | ||||
|         expected_transferred = 0; | ||||
|         expected_message = "Asynchronous IN transfer failed.  " | ||||
|             "Incorrect function.  Windows error code 0x1."; | ||||
|         #elif defined(__linux__) | ||||
|         // This request results in an error in Linux but it is only detected | ||||
|         // after some data is transferred. | ||||
|         expected_transferred = transfer_size + 1; | ||||
|         expected_message = "Asynchronous IN transfer failed.  " | ||||
|             "The transfer overflowed.  Error code 75."; | ||||
|         #elif defined(__APPLE__) | ||||
|         // On Mac OS X, this results in an error after some data is transferred. | ||||
|         expected_transferred = transfer_size; | ||||
|         expected_message = "Asynchronous IN transfer failed.  " | ||||
|             "The transfer overflowed.  Error code 0xe00002e8."; | ||||
|         #else | ||||
|         REQUIRE(0); | ||||
|         #endif | ||||
|  | ||||
|         CHECK(transferred == expected_transferred); | ||||
|         CHECK(transfer_error.message() == expected_message); | ||||
|  | ||||
|         // We can't just call clean_up_async_in_pipe because we might be running | ||||
|         // on a slower computer and the next transfer has actually already been | ||||
|         // queued. | ||||
|         pipe.cancel_transfers(); | ||||
|         test_timeout timeout(500); | ||||
|         while(pipe.has_pending_transfers()) | ||||
|         { | ||||
|             pipe.handle_events(); | ||||
|             libusbp::error transfer_error; | ||||
|             while(pipe.handle_finished_transfer(NULL, NULL, &transfer_error)) | ||||
|             { | ||||
|                 if (!transfer_error) | ||||
|                 { | ||||
|                     #if defined(__linux__) | ||||
|                     // Overflowing is always an error, none of the transfers | ||||
|                     // should have succeeded. | ||||
|                     throw "Expected to get a transfer error."; | ||||
|                     #endif | ||||
|                 } | ||||
|                 else if (transfer_error.message() == expected_message) | ||||
|                 { | ||||
|                     // This is fine; the error indicates an overflow. | ||||
|                 } | ||||
|                 #ifdef __APPLE__ | ||||
|                 else if (transfer_error.has_code(LIBUSBP_ERROR_STALL)) | ||||
|                 { | ||||
|                     // Mac OS X considers the pipe to be "stalled" after an | ||||
|                     // overflow happens and we cannot actually submit any more | ||||
|                     // transfers. | ||||
|                 } | ||||
|                 #endif | ||||
|                 else if (transfer_error.has_code(LIBUSBP_ERROR_CANCELLED)) | ||||
|                 { | ||||
|                     // This is fine; the transfer was cancelled. | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // Unexpected error. | ||||
|                     throw transfer_error; | ||||
|                 } | ||||
|             } | ||||
|             timeout.check(); | ||||
|             sleep_quick(); | ||||
|         } | ||||
|  | ||||
|         clean_up_async_in_pipe(pipe); | ||||
|     } | ||||
|  | ||||
|     #ifdef __linux__ | ||||
|     SECTION("does not prevent the creation of another generic_interface object") | ||||
|     { | ||||
|         // It seems that the "usbfs" driver gets attached to the device | ||||
|         // while we are doing asynchronous transfers.  This tests that our | ||||
|         // code is OK with that and we can still open up other handles | ||||
|         // to the device, at least in Linux. | ||||
|  | ||||
|         pipe.allocate_transfers(1, transfer_size); | ||||
|         pipe.start_endless_transfers(); | ||||
|         libusbp::generic_interface gi2(device, 0, true); | ||||
|         libusbp::generic_interface gi3(device, 0, true); | ||||
|         libusbp::generic_handle handle2(gi3); | ||||
|         handle2.control_transfer(0x40, 0x90, 1, 0); | ||||
|         clean_up_async_in_pipe(pipe); | ||||
|     } | ||||
|     #endif | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										204
									
								
								dep/libusbp/test/control_sync_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								dep/libusbp/test/control_sync_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,204 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| TEST_CASE("control transfer corner cases") | ||||
| { | ||||
|     SECTION("sets transferred to zero if possible") | ||||
|     { | ||||
|         size_t transferred = 1; | ||||
|         libusbp::error error(libusbp_control_transfer(NULL, | ||||
|                 0, 0, 0, 0, NULL, 0, &transferred)); | ||||
|         REQUIRE(transferred == 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
|  | ||||
| TEST_CASE("control transfers (synchronous) for Test Device A", "[ctstda]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle gih(gi); | ||||
|     size_t transferred = 0xF12F; | ||||
|     gih.set_timeout(0, 300); | ||||
|  | ||||
|     SECTION("request without a data stage") | ||||
|     { | ||||
|         // Turn on the LED. | ||||
|         gih.control_transfer(0x40, 0x90, 1, 0, NULL, 0, &transferred); | ||||
|         REQUIRE(transferred == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("writing and reading data") | ||||
|     { | ||||
|         char buffer1[40] = "hello there"; | ||||
|         size_t size = strlen(buffer1) + 1; | ||||
|  | ||||
|         // Transfer data to the device. | ||||
|         gih.control_transfer(0x40, 0x92, 0, 0, buffer1, size, &transferred); | ||||
|         REQUIRE(transferred == size); | ||||
|  | ||||
|         // Read the data back. | ||||
|         char buffer2[40]; | ||||
|         gih.control_transfer(0xC0, 0x91, 0, 12, buffer2, 20, &transferred); | ||||
|         REQUIRE(transferred == size); | ||||
|         REQUIRE(std::string(buffer2) == buffer1); | ||||
|     } | ||||
|  | ||||
|     SECTION("invalid request") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             gih.control_transfer(0x40, 0x48, 0, 0); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             const char * expected = | ||||
|                 "Control transfer failed.  " | ||||
|                 "The request was invalid or there was an I/O problem.  " | ||||
|                 #if defined(_WIN32) | ||||
|                 "Windows error code 0x1f." | ||||
|                 #elif defined(__linux__) | ||||
|                 "Error code 32." | ||||
|                 #elif defined(__APPLE__) | ||||
|                 "Error code 0xe000404f." | ||||
|                 #endif | ||||
|                 ; | ||||
|             REQUIRE(std::string(error.what()) == expected); | ||||
|             REQUIRE(error.has_code(LIBUSBP_ERROR_STALL)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("control transfers that time out for Test Device A") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle gih(gi); | ||||
|     size_t transferred = 0xFFFF; | ||||
|     gih.set_timeout(0, 1); | ||||
|  | ||||
|     // Figure out what delay is necessary to trigger a control transfer timeout. | ||||
|     #ifdef __APPLE__ | ||||
|     // Mac OS X seems to have really inaccurate timers and it does not detect a | ||||
|     // timeout unless the device delays for over roughly one second.  To detect it | ||||
|     // reliably, the delay needs to be even longer, definitely slows down the tests | ||||
|     // and makes our device not compliant with the USB specification during that | ||||
|     // time, because it cannot response to SETUP packets. | ||||
|     const uint32_t required_delay = 2000; | ||||
|     #else | ||||
|     const uint32_t required_delay = 100; | ||||
|     #endif | ||||
|  | ||||
|     std::string timeout_message = "Control transfer failed.  " | ||||
|         "The operation timed out.  " | ||||
|         #if defined(_WIN32) | ||||
|         "Windows error code 0x79." | ||||
|         #elif defined(__linux__) | ||||
|         "Error code 110." | ||||
|         #elif defined(__APPLE__) | ||||
|         "Error code 0xe0004051." | ||||
|         #endif | ||||
|     ; | ||||
|  | ||||
|     SECTION("write request that times out") | ||||
|     { | ||||
|         size_t transferred = 0xFFFF; | ||||
|         char buffer[] = "hi"; | ||||
|         try | ||||
|         { | ||||
|             gih.control_transfer(0x40, 0x92, required_delay, 0, | ||||
|                 buffer, sizeof(buffer), &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == timeout_message); | ||||
|             REQUIRE(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("read request that times out") | ||||
|     { | ||||
|         size_t transferred = 0xFFFF; | ||||
|         char buffer[3]; | ||||
|         try | ||||
|         { | ||||
|             gih.control_transfer(0xC0, 0x91, required_delay, 0, | ||||
|                 buffer, sizeof(buffer), &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == timeout_message); | ||||
|             REQUIRE(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #ifdef _WIN32 | ||||
|     SECTION("write request with no data stage that times out (no error raised)") | ||||
|     { | ||||
|         // For some reason, WinUSB does not seem to report timeouts for a | ||||
|         // control transfer with no data stage that times out, which is bad.  I | ||||
|         // am not sure why, because it used to work. | ||||
|         gih.control_transfer(0x40, 0x92, required_delay, 0, NULL, 0, &transferred); | ||||
|     } | ||||
|     #else | ||||
|  | ||||
|     SECTION("write request with no data stage that times out (error raised)") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             gih.control_transfer(0x40, 0x92, required_delay, 0, NULL, 0, &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == timeout_message); | ||||
|             REQUIRE(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|     #endif | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_B | ||||
|  | ||||
| TEST_CASE("control transfers (synchronous) for Test Device B", "[ctstda]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_b(); | ||||
|     libusbp::generic_interface gi(device, 0, false); | ||||
|     libusbp::generic_handle gih(gi); | ||||
|     size_t transferred; | ||||
|     gih.set_timeout(0, 300); | ||||
|  | ||||
|     SECTION("request without a data stage") | ||||
|     { | ||||
|         // Turn on the LED. | ||||
|         gih.control_transfer(0x40, 0x90, 1, 0, NULL, 0, &transferred); | ||||
|         REQUIRE(transferred == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("writing and reading data") | ||||
|     { | ||||
|         char buffer1[40] = "hello there"; | ||||
|         size_t size = strlen(buffer1) + 1; | ||||
|  | ||||
|         // Transfer data to the device. | ||||
|         gih.control_transfer(0x40, 0x92, 0, 0, buffer1, size, &transferred); | ||||
|         REQUIRE(transferred == size); | ||||
|  | ||||
|         // Read the data back. | ||||
|         char buffer2[40]; | ||||
|         gih.control_transfer(0xC0, 0x91, 0, 12, buffer2, 20, &transferred); | ||||
|         REQUIRE(transferred == size); | ||||
|         REQUIRE(std::string(buffer2) == buffer1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										251
									
								
								dep/libusbp/test/device_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								dep/libusbp/test/device_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| static void check_null_device_error(const libusbp::error & error) | ||||
| { | ||||
|     REQUIRE(error.message() == "Device is null."); | ||||
| } | ||||
|  | ||||
| TEST_CASE("null device") | ||||
| { | ||||
|     libusbp::device device; | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         REQUIRE_FALSE(device); | ||||
|     } | ||||
|  | ||||
|     SECTION("can be copied") | ||||
|     { | ||||
|         libusbp::device device2 = device; | ||||
|         REQUIRE_FALSE(device2); | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot return a vendor ID") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             device.get_vendor_id(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_device_error(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot return a product ID") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             device.get_product_id(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_device_error(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot return a revision") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             device.get_revision(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_device_error(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot return a serial number") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             device.get_serial_number(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_device_error(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot return an OS id") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             device.get_os_id(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_device_error(error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("device parameter checks and corner cases") | ||||
| { | ||||
|     SECTION("libusbp_device_copy complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_copy(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Device output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_copy sets the output pointer to 0 by default") | ||||
|     { | ||||
|         void * p = &p; | ||||
|         libusbp::error error(libusbp_device_copy(NULL, (libusbp_device**)&p)); | ||||
|         REQUIRE((p == NULL)); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_vendor_id complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_get_vendor_id(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Vendor ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_vendor_id sets the output to 0 by default") | ||||
|     { | ||||
|         uint16_t x = 1; | ||||
|         libusbp::error error(libusbp_device_get_vendor_id(NULL, &x)); | ||||
|         REQUIRE(x == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_product_id complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_get_product_id(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Product ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_product_id sets the output to 0 by default") | ||||
|     { | ||||
|         uint16_t x = 1; | ||||
|         libusbp::error error(libusbp_device_get_product_id(NULL, &x)); | ||||
|         REQUIRE(x == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_revision complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_get_revision(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Device revision output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_revision sets the output to 0 by default") | ||||
|     { | ||||
|         uint16_t x = 1; | ||||
|         libusbp::error error(libusbp_device_get_revision(NULL, &x)); | ||||
|         REQUIRE(x == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_serial_number complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_get_serial_number(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Serial number output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_serial_number sets the output to NULL by default") | ||||
|     { | ||||
|         void * p = &p; | ||||
|         libusbp::error error(libusbp_device_get_serial_number(NULL, (char **)&p)); | ||||
|         REQUIRE((p == NULL)); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_os_id complains about a null output pointer") | ||||
|     { | ||||
|         libusbp::error error(libusbp_device_get_os_id(NULL, NULL)); | ||||
|         REQUIRE(error.message() == "Device OS ID output pointer is null."); | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_device_get_os_id sets the output to NULL by default") | ||||
|     { | ||||
|         void * p = &p; | ||||
|         libusbp::error error(libusbp_device_get_os_id(NULL, (char **)&p)); | ||||
|         REQUIRE((p == NULL)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("basic checks on all devices", "[device_basic]") | ||||
| { | ||||
|     const bool print_devices = false; | ||||
|  | ||||
|     std::vector<libusbp::device> list = libusbp::list_connected_devices(); | ||||
|     for (auto it = list.begin(); it != list.end(); ++it) | ||||
|     { | ||||
|         libusbp::device device = *it; | ||||
|  | ||||
|         uint16_t vendor_id = device.get_vendor_id(); | ||||
|         uint16_t product_id = device.get_product_id(); | ||||
|         uint16_t revision = device.get_revision(); | ||||
|  | ||||
|         std::string serial; | ||||
|         try | ||||
|         { | ||||
|             serial = device.get_serial_number(); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             if (!error.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER)) { throw; } | ||||
|         } | ||||
|  | ||||
|         std::string id = device.get_os_id(); | ||||
|         CHECK_FALSE(id.empty()); | ||||
|  | ||||
|         if (print_devices) | ||||
|         { | ||||
|             printf("Device: %04x:%04x:%04x %-32s %s\n", | ||||
|                 vendor_id, product_id, revision, serial.c_str(), id.c_str()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("Test Device A", "[tda]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|  | ||||
|     SECTION("present") | ||||
|     { | ||||
|         REQUIRE(device); | ||||
|     } | ||||
|  | ||||
|     SECTION("revision code") | ||||
|     { | ||||
|         // If this test fails, you should probably update | ||||
|         // your Test Device A with the latest firmware. | ||||
|         REQUIRE(device.get_revision() == 0x0007); | ||||
|     } | ||||
|  | ||||
|     SECTION("device instance id") | ||||
|     { | ||||
|         std::string id = device.get_os_id(); | ||||
|         REQUIRE_FALSE(id.empty()); | ||||
|         #ifdef _WIN32 | ||||
|         REQUIRE(id.find("USB") == 0); | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     SECTION("serial number") | ||||
|     { | ||||
|         std::string serial_number = device.get_serial_number(); | ||||
|         CHECK(serial_number.size() == 11); | ||||
|         CHECK(serial_number[2] == '-'); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_B | ||||
| TEST_CASE("Test Device B", "[tdb]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_b(); | ||||
|  | ||||
|     SECTION("present") | ||||
|     { | ||||
|         REQUIRE(device); | ||||
|     } | ||||
|  | ||||
|     SECTION("revision code") | ||||
|     { | ||||
|         // If this test fails, you should probably update | ||||
|         // your Test Device B with the latest firmware. | ||||
|         REQUIRE(device.get_revision() == 0x0007); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										
											BIN
										
									
								
								dep/libusbp/test/drivers/pololu.cat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								dep/libusbp/test/drivers/pololu.cat
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										56
									
								
								dep/libusbp/test/drivers/usb_test_a_native.inf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								dep/libusbp/test/drivers/usb_test_a_native.inf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| ; Copyright (C) 2015 Pololu Corporation | ||||
|  | ||||
| ; This driver file is not needed on Windows 8.1 and later because each device | ||||
| ; implements Microsoft OS 2.0 Descriptors. | ||||
|  | ||||
| [Strings] | ||||
| DriverPackageDisplayName="USB Test Device A Native USB Interface Driver" | ||||
| ManufacturerName="Pololu Corporation" | ||||
| ClassName="Universal Serial Bus devices" | ||||
| DeviceInterfaceGUID="{99c4bbb0-e925-4397-afee-981cd0702163}" | ||||
| pDA01="USB Test Device A Interface 0" | ||||
|  | ||||
| [DefaultInstall] | ||||
| CopyINF=usb_test_a_native.inf | ||||
|  | ||||
| [Version] | ||||
| DriverVer=12/23/2015,2.0.5 | ||||
| Signature=$Windows NT$ | ||||
| Class=USBDevice | ||||
| ClassGuid={88BAE032-5A81-49F0-BC3D-A4FF138216D6} | ||||
| Provider=%ManufacturerName% | ||||
| CatalogFile=pololu.cat | ||||
| DriverPackageDisplayName=%DriverPackageDisplayName% | ||||
|  | ||||
| [Manufacturer] | ||||
| %ManufacturerName%=Models,NTamd64 | ||||
|  | ||||
| [ClassInstall32] | ||||
| AddReg=ClassInstall_AddReg | ||||
|  | ||||
| [ClassInstall_AddReg] | ||||
| HKR,,,0,%ClassName% | ||||
| HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20" | ||||
| HKR,,NoInstallClass,,1 | ||||
| HKR,,BootCritical,,0 | ||||
| HKR,,Configurable,,1 | ||||
|  | ||||
| [Models] | ||||
| %pDA01%=USB_Install, USB\VID_1FFB&PID_DA01&MI_00 | ||||
|  | ||||
| [Models.NTamd64] | ||||
| %pDA01%=USB_Install, USB\VID_1FFB&PID_DA01&MI_00 | ||||
|  | ||||
| [USB_Install] | ||||
| Include = Winusb.inf | ||||
| Needs = WINUSB.NT | ||||
|  | ||||
| [USB_Install.Services] | ||||
| Include = Winusb.inf | ||||
| Needs = WINUSB.NT.Services | ||||
|  | ||||
| [USB_Install.HW] | ||||
| AddReg = Dev_AddReg | ||||
|  | ||||
| [Dev_AddReg] | ||||
| HKR,,DeviceInterfaceGUIDs,0x00010000,%DeviceInterfaceGUID% | ||||
							
								
								
									
										54
									
								
								dep/libusbp/test/drivers/usb_test_a_serial.inf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								dep/libusbp/test/drivers/usb_test_a_serial.inf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| ; Copyright 2015 Pololu Corporation | ||||
|  | ||||
| [Strings] | ||||
| DriverPackageDisplayName="USB Test Device A Serial Port Driver" | ||||
| ManufacturerName="Pololu Corporation" | ||||
| ServiceName="USB RS-232 Emulation Driver" | ||||
| pDA01.Port.Name="USB Test Device A Port" | ||||
|  | ||||
| [DefaultInstall] | ||||
| CopyINF=usb_test_a_serial.inf | ||||
|  | ||||
| [Version] | ||||
| Class=Ports | ||||
| ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} | ||||
| Signature="$Windows NT$" | ||||
| Provider=%ManufacturerName% | ||||
| CatalogFile=pololu.cat | ||||
| DriverVer=07/09/2015,1.0.1.0 | ||||
| DriverPackageDisplayName=%DriverPackageDisplayName% | ||||
|  | ||||
| [Manufacturer] | ||||
| %ManufacturerName%=DeviceList, NTamd64 | ||||
|  | ||||
| [DestinationDirs] | ||||
| FakeModemCopyFileSection=12 | ||||
| DefaultDestDir=12 | ||||
|  | ||||
| [DeviceList] | ||||
| %pDA01.Port.Name%=DriverInstall, USB\VID_1FFB&PID_DA01&MI_02 | ||||
|  | ||||
| [DeviceList.NTamd64] | ||||
| %pDA01.Port.Name%=DriverInstall, USB\VID_1FFB&PID_DA01&MI_02 | ||||
|  | ||||
| [DriverInstall] | ||||
| include=mdmcpq.inf,usb.inf | ||||
| CopyFiles = FakeModemCopyFileSection | ||||
| AddReg=DriverAddReg | ||||
|  | ||||
| [DriverAddReg] | ||||
| HKR,,DevLoader,,*ntkern | ||||
| HKR,,NTMPDriver,,usbser.sys | ||||
| HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" | ||||
|  | ||||
| [DriverInstall.Services] | ||||
| include=mdmcpq.inf | ||||
| AddService=usbser, 0x00000002, DriverService | ||||
|  | ||||
| [DriverService] | ||||
| DisplayName=%ServiceName% | ||||
| ServiceType=1 | ||||
| StartType=3 | ||||
| ErrorControl=1 | ||||
| ServiceBinary=%12%\usbser.sys | ||||
| LoadOrderGroup=Base | ||||
							
								
								
									
										56
									
								
								dep/libusbp/test/drivers/usb_test_b_native.inf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								dep/libusbp/test/drivers/usb_test_b_native.inf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| ; Copyright (C) 2015 Pololu Corporation | ||||
|  | ||||
| ; This driver file is not needed on Windows 8.1 and later because each device | ||||
| ; implements Microsoft OS 2.0 Descriptors. | ||||
|  | ||||
| [Strings] | ||||
| DriverPackageDisplayName="USB Test Device B Driver" | ||||
| ManufacturerName="Pololu Corporation" | ||||
| ClassName="Universal Serial Bus devices" | ||||
| DeviceInterfaceGUID="{99c4bbb0-e925-4397-afee-981cd0702163}" | ||||
| pDA02="USB Test Device B" | ||||
|  | ||||
| [DefaultInstall] | ||||
| CopyINF=usb_test_b_native.inf | ||||
|  | ||||
| [Version] | ||||
| DriverVer=12/23/2015,1.0.1 | ||||
| Signature=$Windows NT$ | ||||
| Class=USBDevice | ||||
| ClassGuid={88BAE032-5A81-49F0-BC3D-A4FF138216D6} | ||||
| Provider=%ManufacturerName% | ||||
| CatalogFile=pololu.cat | ||||
| DriverPackageDisplayName=%DriverPackageDisplayName% | ||||
|  | ||||
| [Manufacturer] | ||||
| %ManufacturerName%=Models,NTamd64 | ||||
|  | ||||
| [ClassInstall32] | ||||
| AddReg=ClassInstall_AddReg | ||||
|  | ||||
| [ClassInstall_AddReg] | ||||
| HKR,,,0,%ClassName% | ||||
| HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20" | ||||
| HKR,,NoInstallClass,,1 | ||||
| HKR,,BootCritical,,0 | ||||
| HKR,,Configurable,,1 | ||||
|  | ||||
| [Models] | ||||
| %pDA02%=USB_Install, USB\VID_1FFB&PID_DA02 | ||||
|  | ||||
| [Models.NTamd64] | ||||
| %pDA02%=USB_Install, USB\VID_1FFB&PID_DA02 | ||||
|  | ||||
| [USB_Install] | ||||
| Include = Winusb.inf | ||||
| Needs = WINUSB.NT | ||||
|  | ||||
| [USB_Install.Services] | ||||
| Include = Winusb.inf | ||||
| Needs = WINUSB.NT.Services | ||||
|  | ||||
| [USB_Install.HW] | ||||
| AddReg = Dev_AddReg | ||||
|  | ||||
| [Dev_AddReg] | ||||
| HKR,,DeviceInterfaceGUIDs,0x00010000,%DeviceInterfaceGUID% | ||||
							
								
								
									
										456
									
								
								dep/libusbp/test/error_message_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								dep/libusbp/test/error_message_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,456 @@ | ||||
| /* This file tests the conversion of errors from libusbp's underlying APIs to | ||||
|  * libusbp_error objects.  It also documents and justifies the explicit mappings | ||||
|  * that are in the error handling code. */ | ||||
|  | ||||
| #include <test_helper.h> | ||||
|  | ||||
| #ifndef NDEBUG | ||||
|  | ||||
| // Here is a list of the messages we want to use consistently across platforms | ||||
| // to describe certain types of errors: | ||||
| #define STR_TIMEOUT "The operation timed out." | ||||
| #define STR_CANCELLED "The operation was cancelled." | ||||
| #define STR_GENERAL_FAILURE "The request was invalid or there was an I/O problem." | ||||
| #define STR_REMOVED "The device was removed." | ||||
| #define STR_OVERFLOW "The transfer overflowed." | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|  | ||||
| TEST_CASE("error_create_winapi", "[error_create_winapi]") | ||||
| { | ||||
|     libusbp::error error; | ||||
|  | ||||
|     SECTION("includes the error code from GetLastError and its message") | ||||
|     { | ||||
|         SetLastError(0x1F4); | ||||
|         error.pointer_reset(error_create_winapi("Something failed.")); | ||||
|         REQUIRE(error.message() == "Something failed.  " | ||||
|             "User profile cannot be loaded.  " | ||||
|             "Windows error code 0x1f4."); | ||||
|     } | ||||
|  | ||||
|     SECTION("still works if Windows does not have a message") | ||||
|     { | ||||
|         SetLastError(0x1F431892); | ||||
|         error.pointer_reset(error_create_winapi("Something failed.")); | ||||
|         REQUIRE(error.message() == "Something failed.  " | ||||
|             "Windows error code 0x1f431892."); | ||||
|     } | ||||
|  | ||||
|     SECTION("access denied errors") | ||||
|     { | ||||
|         // ERROR_ACCESS_DENIED can happen when running CreateFile to open a | ||||
|         // handle to a WinUSB device node that is already being used by another | ||||
|         // application.  This is one of the few cases where a libusbp error | ||||
|         // message actually contains a troubleshooting step for the user to try. | ||||
|         // It is included in the library because it is a universally useful piece | ||||
|         // of advice for any WinUSB device. | ||||
|         // | ||||
|         // This feature is part of error_create_winapi, which means we are | ||||
|         // assuming that we will only ERROR_ACCESS_DENIED as a result of trying | ||||
|         // to open a device that is being used.  If we expand the library to do | ||||
|         // other things that might have their access denied, this feature should | ||||
|         // maybe move to a different function. | ||||
|         SetLastError(ERROR_ACCESS_DENIED); | ||||
|         error.pointer_reset(error_create_winapi("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|         CHECK(error.message() == | ||||
|             "Hi.  " | ||||
|             "Access is denied.  " | ||||
|             "Try closing all other programs that are using the device.  " | ||||
|             "Windows error code 0x5."); | ||||
|     } | ||||
|  | ||||
|     SECTION("out of memory errors") | ||||
|     { | ||||
|         // We haven't specifically seen these errors happen before, but it seems | ||||
|         // like either of them might be used to indicate that the system is out | ||||
|         // memory. | ||||
|         SetLastError(ERROR_OUTOFMEMORY); | ||||
|         error.pointer_reset(error_create_winapi("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_MEMORY)); | ||||
|  | ||||
|         SetLastError(ERROR_NOT_ENOUGH_MEMORY); | ||||
|         error.pointer_reset(error_create_winapi("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_MEMORY)); | ||||
|     } | ||||
|  | ||||
|     SECTION("general failure") | ||||
|     { | ||||
|         // We have seen ERROR_GEN_FAILURE occur in multiple different | ||||
|         // situations. | ||||
|         // | ||||
|         // It could mean that the device was disconnected from the computer | ||||
|         // in the middle of or during a synchronous USB transfer. | ||||
|         // | ||||
|         // It could mean that the host just sent a control transfer | ||||
|         // request that the device does not support, and the device has returned | ||||
|         // a STALL packet, which is a perfectly valid way for the device to | ||||
|         // behave. | ||||
|         // | ||||
|         // It probably also applies to STALL packets during IN and OUT | ||||
|         // transfers. | ||||
|         // | ||||
|         // The default message is "A device attached to the system is not | ||||
|         // functioning."  This places too much blame on the USB device, so we | ||||
|         // definitely want libusbp to use STR_GENERAL_FAILURE instead. | ||||
|         SetLastError(ERROR_GEN_FAILURE); | ||||
|         error.pointer_reset(error_create_winapi("Hey.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_STALL)); | ||||
|         CHECK(error.message() == "Hey.  " | ||||
|             STR_GENERAL_FAILURE | ||||
|             "  Windows error code 0x1f."); | ||||
|     } | ||||
|  | ||||
|     SECTION("timeout error") | ||||
|     { | ||||
|         // The have seen WinUSB return ERROR_SEM_TIMEOUT for both synchronous | ||||
|         // and asynchronous operations that time out. | ||||
|         SetLastError(ERROR_SEM_TIMEOUT); | ||||
|         error.pointer_reset(error_create_winapi("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|         CHECK(error.message() ==  "Hi.  " STR_TIMEOUT "  Windows error code 0x79."); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancelled error") | ||||
|     { | ||||
|         // GetOverlappedResult returns this if the asynchronous operation was | ||||
|         // cancelled. | ||||
|         SetLastError(ERROR_OPERATION_ABORTED); | ||||
|         error.pointer_reset(error_create_winapi("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_CANCELLED)); | ||||
|         CHECK(error.message() == "Hi.  " STR_CANCELLED "  Windows error code 0x3e3."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_create_overlapped") | ||||
| { | ||||
|     libusbp::error error; | ||||
|  | ||||
|     SECTION("usually just calls error_create_winapi") | ||||
|     { | ||||
|         SetLastError(ERROR_SEM_TIMEOUT); | ||||
|         error.pointer_reset(error_create_overlapped("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|     } | ||||
|  | ||||
|     SECTION("device disconnect error") | ||||
|     { | ||||
|         // WinUsb_GetOverlappedResult (and presumably also GetOverlappedResult) | ||||
|         // returns this error code when you are checking the status of an | ||||
|         // asynchronous operation for a device that has been disconnected from | ||||
|         // the system.  This is inconsistent with the synchronous operations, | ||||
|         // which return ERROR_GEN_FAILURE, but it makes sense because | ||||
|         // GetOverlappedResult is a more general thing that applies to any type | ||||
|         // of file that can have asynchronous operations performed on it. | ||||
|         // | ||||
|         // It might be OK to put this feature into error_create_winapi, but in | ||||
|         // general, ERROR_FILE_NOT_FOUND does not mean a device was | ||||
|         // disconnected, so we have put this behavior in a special function | ||||
|         // named error_create_overlapped (instead of error_create_winapi). | ||||
|         SetLastError(ERROR_FILE_NOT_FOUND); | ||||
|         error.pointer_reset(error_create_overlapped("Hey.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.message() == "Hey.  " | ||||
|             "The device was disconnected.  " | ||||
|             "Windows error code 0x2."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_create_cr", "[error_create_cr]") | ||||
| { | ||||
|     SECTION("returns the right message") | ||||
|     { | ||||
|         libusbp::error error(error_create_cr(CR_NO_SUCH_DEVNODE, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  CONFIGRET error code 0xd."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef __linux__ | ||||
|  | ||||
| TEST_CASE("error_create_errno", "[error_create_errno]") | ||||
| { | ||||
|     libusbp::error error; | ||||
|  | ||||
|     SECTION("returns the right message") | ||||
|     { | ||||
|         errno = ENOENT; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.message() == | ||||
|             "Hi.  No such file or directory.  Error code 2."); | ||||
|     } | ||||
|  | ||||
|     SECTION("still works when no message is available") | ||||
|     { | ||||
|         errno = -122344; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.message() == "Hi.  Error code -122344."); | ||||
|     } | ||||
|  | ||||
|     SECTION("access denied error") | ||||
|     { | ||||
|         // EACCES is the error we see when calling open() on a USB device file | ||||
|         // that we don't have read-write permissions for. | ||||
|         errno = EACCES; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|  | ||||
|         // One reason we might not have permissions is that the udev rules might | ||||
|         // not have been applied yet, so usbfd_open adds the | ||||
|         // LIBUSBP_ERROR_NOT_READY code to EACCES errors.  (That is NOT tested | ||||
|         // here though.) | ||||
|         CHECK_FALSE(error.has_code(LIBUSBP_ERROR_NOT_READY)); | ||||
|     } | ||||
|  | ||||
|     SECTION("EPERM error") | ||||
|     { | ||||
|         // EPERM is described in errno-base.h as "operation not permitted".  The | ||||
|         // file error-codes.txt says "submission failed because urb->reject was | ||||
|         // set".  libusb does not do anything special with this error, and I am | ||||
|         // not sure when we would receive it, so for now let's NOT map it to the | ||||
|         // LIBUSBP_ERROR_ACCESS_DENIED code. | ||||
|         errno = EPERM; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK_FALSE(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|     } | ||||
|  | ||||
|     SECTION("memory errors") | ||||
|     { | ||||
|         // This is described by error-codes.txt as "no memory for allocation of | ||||
|         // internal structures", and it will probably be used by other non-USB | ||||
|         // system calls as well. | ||||
|         errno = ENOMEM; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_MEMORY)); | ||||
|     } | ||||
|  | ||||
|     SECTION("general failure") | ||||
|     { | ||||
|         // error-codes.txt says that EPIPE (broken pipe) can either mean | ||||
|         // "endpoint stalled", a device disconnect, or the pipe type specified | ||||
|         // in the URB doesn't match the endpoint's actual type. | ||||
|         // | ||||
|         // It's interesting that both Windows and Linux return just a single | ||||
|         // error code which could either mean a STALL packet or a device | ||||
|         // disconnect.  Maybe it's hard to tell the difference between those two | ||||
|         // cases because of the design of the host controller interface. | ||||
|         errno = EPIPE; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_STALL)); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.message() == "Hi.  " STR_GENERAL_FAILURE "  Error code 32."); | ||||
|     } | ||||
|  | ||||
|     SECTION("device disconnects") | ||||
|     { | ||||
|         // error-codes.txt documents ENODEV (no such device) to mean "specified | ||||
|         // USB-device or bus doesn't exist" or "device was removed".  In | ||||
|         // devio.c, we can see that most ioctls will return ENODEV if the device | ||||
|         // is not connected.  Also read() will return ENODEV if the device is | ||||
|         // not connected, which might be a handy way to check if the device is | ||||
|         // connected. | ||||
|         errno = ENODEV; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.message() == "Hi.  " STR_REMOVED "  Error code 19."); | ||||
|  | ||||
|         // error-codes.txt says ESHUTDOWN (cannot send after transport endpoint | ||||
|         // shutdown) means "The device or host controller has been disabled due | ||||
|         // to some problem that could not be worked around, such as a physical | ||||
|         // disconnect."  libusb has four places where it handles ESHUTDOWN, and | ||||
|         // all four of them say that the device was removed.  ESHUTDOWN is the | ||||
|         // error we typically see from a synchronous operation when the device | ||||
|         // is removed in the middle of the operation. | ||||
|         errno = ESHUTDOWN; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.message() == "Hi.  " STR_REMOVED "  Error code 108."); | ||||
|  | ||||
|         // error-codes.txt says EPROTO (protocol error) and ETIME (timer | ||||
|         // expired) (and EPIPE and EILSEQ) are codes that different kinds of | ||||
|         // host controller use to indicate a transfer has failed because of | ||||
|         // device disconnect. | ||||
|  | ||||
|         errno = EPROTO; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|  | ||||
|         errno = ETIME; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|     } | ||||
|  | ||||
|     SECTION("timeout errors") | ||||
|     { | ||||
|         // error-codes.txt says that ETIMEDOUT indicates a timeout in a | ||||
|         // synchronous USB message. | ||||
|         errno = ETIMEDOUT; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|         CHECK(error.message() == "Hi.  " STR_TIMEOUT  "  Error code 110."); | ||||
|     } | ||||
|  | ||||
|     SECTION("overflow errors") | ||||
|     { | ||||
|         // error-codes.txt says that EOVERFLOW (Value too large for defined data | ||||
|         // type) means the amount of data reutnred by the endpoint was greater | ||||
|         // than either the max packet size or the remaining buffer size.  We | ||||
|         // don't have a libusbp error code for that, but we want to fix the | ||||
|         // misleading message from Linux. | ||||
|         errno = EOVERFLOW; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.message() == "Hi.  " STR_OVERFLOW "  Error code 75."); | ||||
|     } | ||||
|  | ||||
|     SECTION("EILSEQ") | ||||
|     { | ||||
|         // error-codes.txt says that EILSEQ is one of "several codes that | ||||
|         // different kinds of host controller use to indicate a transfer has | ||||
|         // failed because of device disconnect". | ||||
|         // | ||||
|         // We have also seen EILSEQ happen sometimes in VirtualBox when | ||||
|         // cancelling an URB, or when removing a device. | ||||
|         // | ||||
|         // The default message for EILSEQ is "Invalid or incomplete multibyte or | ||||
|         // wide character." which is atrocious because it makes me think there | ||||
|         // is some bug in the software, instead of this being a USB hardware | ||||
|         // thing. | ||||
|         errno = EILSEQ; | ||||
|         error.pointer_reset(error_create_errno("Hi.")); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_CANCELLED)); | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_DEVICE_DISCONNECTED)); | ||||
|         CHECK(error.message() == "Hi.  Illegal byte sequence: " | ||||
|             "the device may have been disconnected or " | ||||
|             "the request may have been cancelled.  " | ||||
|             "Error code 84."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_from_urb_status") | ||||
| { | ||||
|     struct usbdevfs_urb urb; | ||||
|     memset(&urb, 0, sizeof(urb)); | ||||
|     libusbp::error error; | ||||
|  | ||||
|     SECTION("gives a good message for cancellation (ENOENT)") | ||||
|     { | ||||
|         // error-codes.txt says the ENOENT in an URB status means that the URB | ||||
|         // was synchronously unlinked with usb_unlink_urb.  This is the error we | ||||
|         // typically see for a cancelled asynchronous request (which actually | ||||
|         // uses usb_kill_urb, which is similar but probably slower). | ||||
|         urb.status = -ENOENT; | ||||
|         error.pointer_reset(error_from_urb_status(&urb)); | ||||
|         REQUIRE(error.message() == STR_CANCELLED "  Error code 2."); | ||||
|     } | ||||
|  | ||||
|     SECTION("does report an error for EREMOTEIO") | ||||
|     { | ||||
|         // error-codes.txt says that EREMOTEIO means that the data read from the | ||||
|         // endpoint did not fill the specified buffer, and URB_SHORT_NOT_OK was | ||||
|         // set in urb->transfer_flags.  We don't indend to set that flag, and if | ||||
|         // we did set that flag, then we should probably interpret a short | ||||
|         // transfer as an error. | ||||
|         // | ||||
|         // libusb does NOT treat EREMOTEIO as an error but that is probably | ||||
|         // because libusb could actually submit multiple URBs per transfer and | ||||
|         // some of them might not be used. | ||||
|         urb.status = -EREMOTEIO; | ||||
|         error.pointer_reset(error_from_urb_status(&urb)); | ||||
|         REQUIRE(error.message() == "Remote I/O error.  Error code 121."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_create_udev", "[error_create_errno]") | ||||
| { | ||||
|     SECTION("returns the right message") | ||||
|     { | ||||
|         libusbp::error error(error_create_udev(123, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  Error from libudev: 123."); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
|  | ||||
| TEST_CASE("error_create_mach") | ||||
| { | ||||
|     SECTION("returns the right message") | ||||
|     { | ||||
|         libusbp::error error(error_create_mach(1, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  (os/kern) invalid address.  Error code 0x1."); | ||||
|     } | ||||
|  | ||||
|     SECTION("stall error") | ||||
|     { | ||||
|         // We encounter this error if a control transfer ends with a STALL | ||||
|         // packet.  This is tested for in control_sync_test.cpp. | ||||
|         kern_return_t kr = kIOUSBPipeStalled; | ||||
|         libusbp::error error(error_create_mach(kr, "Hey.")); | ||||
|         REQUIRE(error.message() == "Hey.  " STR_GENERAL_FAILURE  "  Error code 0xe000404f."); | ||||
|         REQUIRE(error.has_code(LIBUSBP_ERROR_STALL)); | ||||
|     } | ||||
|  | ||||
|     SECTION("timeout") | ||||
|     { | ||||
|         // We encounter this error if a control transfer times out. | ||||
|         // This is tested for in control_sync_test.cpp. | ||||
|         kern_return_t kr = kIOUSBTransactionTimeout; | ||||
|         libusbp::error error(error_create_mach(kr, "Hey.")); | ||||
|         REQUIRE(error.message() == "Hey.  " STR_TIMEOUT  "  Error code 0xe0004051."); | ||||
|         REQUIRE(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|     } | ||||
|  | ||||
|     SECTION("access denied") | ||||
|     { | ||||
|         // We encounter this error if an IOUSBInterface is already open for | ||||
|         // exclusive access when we try to open it for exclusive access. | ||||
|         // This is tested for in generic_handle_test.cpp. | ||||
|         kern_return_t kr = kIOReturnExclusiveAccess; | ||||
|         libusbp::error error(error_create_mach(kr, "Hey.")); | ||||
|         REQUIRE(error.message() == "Hey.  Access is denied.  " | ||||
|             "Try closing all other programs that are using the device.  " | ||||
|             "Error code 0xe00002c5."); | ||||
|         REQUIRE(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|     } | ||||
|  | ||||
|     SECTION("overflow") | ||||
|     { | ||||
|         // This is an error we see when we read from a pipe using an | ||||
|         // IOUSBInterface and the device returns more data than can fit in our | ||||
|         // buffer. | ||||
|         kern_return_t kr = kIOReturnOverrun; | ||||
|         libusbp::error error(error_create_mach(kr, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  " STR_OVERFLOW "  Error code 0xe00002e8."); | ||||
|     } | ||||
|  | ||||
|     SECTION("cancellation") | ||||
|     { | ||||
|         // This is an error we see when we call ReadPipeAsync and | ||||
|         // then cancel it with AbortPipe(). | ||||
|         kern_return_t kr = kIOReturnAborted; | ||||
|         libusbp::error error(error_create_mach(kr, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  " STR_CANCELLED "  Error code 0xe00002eb."); | ||||
|         REQUIRE(error.has_code(LIBUSBP_ERROR_CANCELLED)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) || defined(__APPLE__) | ||||
|  | ||||
| TEST_CASE("error_create_hr", "[error_create_hr]") | ||||
| { | ||||
|     SECTION("returns the right message") | ||||
|     { | ||||
|         libusbp::error error(error_create_hr(0x80070057, "Hi.")); | ||||
|         REQUIRE(error.message() == "Hi.  HRESULT error code 0x80070057."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif  // !NDEBUG | ||||
							
								
								
									
										166
									
								
								dep/libusbp/test/error_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								dep/libusbp/test/error_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | ||||
| /* Tests the generic code for dealing with error objects. | ||||
|  * These tests don't really have anything to do with USB. */ | ||||
|  | ||||
| #include <test_helper.h> | ||||
|  | ||||
| #ifndef NDEBUG | ||||
|  | ||||
| TEST_CASE("error class basic properties") | ||||
| { | ||||
|     SECTION("can be caught as a const std::exception &") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             throw libusbp::error(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const std::exception & e) | ||||
|         { | ||||
|             REQUIRE(1); | ||||
|             CHECK(std::string(e.what()) == "No error."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("can be moved without getting copied") | ||||
|     { | ||||
|         // This isn't really necessary for a light-weight error object, but it | ||||
|         // is a feature that unique_pointer_wrapper_with_copy is supposed to | ||||
|         // provide, so we want to test it on at least one of the classes that | ||||
|         // uses unique_pointer_wrapper. | ||||
|  | ||||
|         libusbp::error error(error_create("hi")); | ||||
|         libusbp_error * p = error.pointer_get(); | ||||
|         REQUIRE(p); | ||||
|  | ||||
|         // Move constructor. | ||||
|         libusbp::error error2 = std::move(error); | ||||
|         REQUIRE(error2.pointer_get() == p); | ||||
|         REQUIRE_FALSE(error); | ||||
|  | ||||
|         // Move assignment. | ||||
|         error = std::move(error2); | ||||
|         REQUIRE(error.pointer_get() == p); | ||||
|         REQUIRE_FALSE(error2); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| TEST_CASE("null error", "[null_error]") | ||||
| { | ||||
|     libusbp::error error; | ||||
|  | ||||
|     SECTION("can also be constructed by passing in NULL") | ||||
|     { | ||||
|         libusbp::error error(NULL); | ||||
|     } | ||||
|  | ||||
|     SECTION("has a default message") | ||||
|     { | ||||
|         REQUIRE(error.message() == "No error."); | ||||
|     } | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         REQUIRE_FALSE(error); | ||||
|     } | ||||
|  | ||||
|     SECTION("can be copied") | ||||
|     { | ||||
|         libusbp::error error2 = error; | ||||
|         REQUIRE_FALSE(error2); | ||||
|     } | ||||
|  | ||||
|     SECTION("has no error codes, and you can not add codes to it") | ||||
|     { | ||||
|         REQUIRE_FALSE(error.has_code(1)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_create", "[error_create]") | ||||
| { | ||||
|     SECTION("creates a non-null error") | ||||
|     { | ||||
|         libusbp::error error(error_create("Error1 %d.", 123)); | ||||
|         REQUIRE(error); | ||||
|     } | ||||
|  | ||||
|     SECTION("properly formats its input") | ||||
|     { | ||||
|         libusbp::error error(error_create("Error1 %d.", 123)); | ||||
|         REQUIRE(error.message() == "Error1 123."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_add", "[error_add]") | ||||
| { | ||||
|     SECTION("works with NULL") | ||||
|     { | ||||
|         libusbp::error error(error_add(NULL, "hi")); | ||||
|         REQUIRE(error.message() == "hi"); | ||||
|     } | ||||
|  | ||||
|     SECTION("preserves the message and error codes of errors passed to it") | ||||
|     { | ||||
|         libusbp::error error(error_add(error_add_code(error_create("hi1"), 7), "hi2")); | ||||
|         CHECK(error.message() == "hi2  hi1"); | ||||
|         CHECK(error.has_code(7)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_add_code", "[error_add_code]") | ||||
| { | ||||
|     SECTION("works with NULL") | ||||
|     { | ||||
|         libusbp::error error(error_add_code(NULL, 4)); | ||||
|         CHECK(error.message() == ""); | ||||
|         CHECK(error.has_code(4)); | ||||
|     } | ||||
|  | ||||
|     SECTION("preserves the message and error codes of errors passed to it") | ||||
|     { | ||||
|         libusbp::error error(error_add_code(error_add_code(error_create("hi1"), 7), 9)); | ||||
|         CHECK(error.message() == "hi1"); | ||||
|         CHECK(error.has_code(7)); | ||||
|         CHECK(error.has_code(9)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("error_no_memory") | ||||
| { | ||||
|     libusbp::error error(&error_no_memory); | ||||
|  | ||||
|     SECTION("has the right message") | ||||
|     { | ||||
|         REQUIRE(error.message() == "Failed to allocate memory."); | ||||
|     } | ||||
|  | ||||
|     SECTION("has the right code") | ||||
|     { | ||||
|         REQUIRE(error.has_code(LIBUSBP_ERROR_MEMORY)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("libusbp_error_copy") | ||||
| { | ||||
|     libusbp_error * error = error_create("X."); | ||||
|     error = error_add(error, "Y."); | ||||
|     error = error_add_code(error, 7); | ||||
|     error = error_add_code(error, 8); | ||||
|     libusbp::error error1(error); | ||||
|  | ||||
|     // Test the C++ copy constructor and libusbp_error_copy at the same time. | ||||
|     libusbp::error error2 = error1; | ||||
|  | ||||
|     SECTION("copies the message") | ||||
|     { | ||||
|         REQUIRE(error2.message() == "Y.  X."); | ||||
|     } | ||||
|  | ||||
|     SECTION("copies codes") | ||||
|     { | ||||
|         CHECK(error2.has_code(7)); | ||||
|         CHECK(error2.has_code(8)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										16
									
								
								dep/libusbp/test/firmware/wixel/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								dep/libusbp/test/firmware/wixel/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /wixel-sdk | ||||
| *.hex | ||||
| *.ihx | ||||
| *.rel | ||||
| *.d | ||||
| *.sym | ||||
| *.cdb | ||||
| *.mem | ||||
| *.rst | ||||
| *.map | ||||
| *.lst | ||||
| *.lk | ||||
| *.asm | ||||
| *.omf | ||||
| *.adb | ||||
| *.cdb | ||||
							
								
								
									
										52
									
								
								dep/libusbp/test/firmware/wixel/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								dep/libusbp/test/firmware/wixel/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| APP_NAME = test_firmware | ||||
| APP_LIBS = gpio usb wixel dma adc | ||||
|  | ||||
| # Path to a Wixel SDK where the libraries have been built. | ||||
| WIXEL_SDK := wixel-sdk | ||||
|  | ||||
| # Command-line utilities | ||||
| CC := sdcc | ||||
| MV := mv | ||||
| WIXELCMD := wixelcmd | ||||
|  | ||||
| INCDIRS += $(WIXEL_SDK)/libraries/include | ||||
|  | ||||
| # C_FLAGS += -DTEST_DEVICE_B | ||||
| C_FLAGS += -Wp,-MD,$(@:%.rel=%.d),-MT,$@,-MP | ||||
| C_FLAGS += --disable-warning 110 | ||||
| C_FLAGS += $(patsubst %,-I%,$(INCDIRS)) | ||||
| C_FLAGS += -Wa,-p | ||||
| C_FLAGS += --model-medium | ||||
| LD_FLAGS += --model-medium | ||||
| C_FLAGS += --debug | ||||
| LD_FLAGS += --debug | ||||
| LD_FLAGS += --code-loc 0x0400 --code-size 0x7400 | ||||
| LD_FLAGS += --iram-size 0x0100 --xram-loc 0xF000 --xram-size 0xF00 | ||||
| LD_FLAGS += -o $(@:%.hex=%.ihx) | ||||
| LD_FLAGS += -L$(WIXEL_SDK)/libraries/lib | ||||
| LD_FLAGS += $(foreach lib,$(APP_LIBS),-l$(lib)) | ||||
| LD_FLAGS += $(WIXEL_SDK)/libraries/xpage/xpage.rel | ||||
|  | ||||
| RELs := $(patsubst %.c,%.rel, $(wildcard *.c)) | ||||
|  | ||||
| .DEFAULT_GOAL = app | ||||
| .PHONY: app | ||||
| app: $(APP_NAME).hex | ||||
|  | ||||
| $(APP_NAME).hex: $(RELs) | ||||
| 	$(CC) $(LD_FLAGS) $^ | ||||
| 	$(MV) -f $(@:%.hex=%.ihx) $@ | ||||
|  | ||||
| %.rel: %.c | ||||
| 	$(CC) -c $< $(C_FLAGS) -o $@ | ||||
|  | ||||
| .PHONY: load | ||||
| load: $(APP_NAME).hex | ||||
| 	$(WIXELCMD) write $< | ||||
|  | ||||
| clean: | ||||
| 	@rm -fv *.hex *.ihx *.rel *.d *.sym *.cdb *.mem *.rst *.map *.lst *.lk *.asm *.omf *.adb *.cdb | ||||
|  | ||||
| # Include all the dependency files generated during compilation so that Make | ||||
| # knows which .rel files to recompile when a .h file changes. | ||||
| -include $(RELs:%.rel=%.d) | ||||
							
								
								
									
										34
									
								
								dep/libusbp/test/firmware/wixel/cdc_acm_constants.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								dep/libusbp/test/firmware/wixel/cdc_acm_constants.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #pragma once | ||||
|  | ||||
| // USB Class Codes | ||||
| #define CDC_CLASS 2                  // (CDC 1.20 Section 4.1: Communications Device Class Code). | ||||
| #define CDC_DATA_INTERFACE_CLASS 0xA // (CDC 1.20 Section 4.5: Data Class Interface Codes). | ||||
|  | ||||
| // USB Subclass Codes | ||||
| #define CDC_SUBCLASS_ACM  2           // (CDC 1.20 Section 4.3: Communications Class Subclass Codes).  Refer to USBPSTN1.2. | ||||
|  | ||||
| // USB Protocol Codes | ||||
| #define CDC_PROTOCOL_V250 1          // (CDC 1.20 Section 4.4: Communications Class Protocol Codes). | ||||
|  | ||||
| // USB Descriptor types from CDC 1.20 Section 5.2.3, Table 12 | ||||
| #define CDC_DESCRIPTOR_TYPE_CS_INTERFACE 0x24 | ||||
| #define CDC_DESCRIPTOR_TYPE_CS_ENDPOINT  0x25 | ||||
|  | ||||
| // USB Descriptor sub-types from CDC 1.20 Table 13: bDescriptor SubType in Communications Class Functional Descriptors | ||||
| #define CDC_DESCRIPTOR_SUBTYPE_HEADER                       0 | ||||
| #define CDC_DESCRIPTOR_SUBTYPE_CALL_MANAGEMENT              1 | ||||
| #define CDC_DESCRIPTOR_SUBTYPE_ABSTRACT_CONTROL_MANAGEMENT  2 | ||||
| #define CDC_DESCRIPTOR_SUBTYPE_UNION                        6 | ||||
|  | ||||
| // Request Codes from CDC 1.20 Section 6.2: Management Element Requests. | ||||
| #define ACM_GET_ENCAPSULATED_RESPONSE 0 | ||||
| #define ACM_SEND_ENCAPSULATED_COMMAND 1 | ||||
|  | ||||
| // Request Codes from PSTN 1.20 Table 13. | ||||
| #define ACM_REQUEST_SET_LINE_CODING 0x20 | ||||
| #define ACM_REQUEST_GET_LINE_CODING 0x21 | ||||
| #define ACM_REQUEST_SET_CONTROL_LINE_STATE 0x22 | ||||
|  | ||||
| // Notification Codes from PSTN 1.20 Table 30. | ||||
| #define ACM_NOTIFICATION_RESPONSE_AVAILABLE 0x01 | ||||
| #define ACM_NOTIFICATION_SERIAL_STATE 0x20 | ||||
							
								
								
									
										736
									
								
								dep/libusbp/test/firmware/wixel/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										736
									
								
								dep/libusbp/test/firmware/wixel/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,736 @@ | ||||
| // This is test firmware that runs on a Wixel and can be used for | ||||
| // testing generic USB PC software. | ||||
| // | ||||
| // By default, it compiles USB Test Device A, a composite device with two | ||||
| // vendor-defined USB interfaces and a serial port. | ||||
| // | ||||
| // When compiled with the TEST_DEVICE_B option, it compiles USB Test Device B, | ||||
| // which is a non-composite device with a single vendor-defined USB interface. | ||||
|  | ||||
| #include <board.h> | ||||
| #include <usb.h> | ||||
| #include <gpio.h> | ||||
| #include <time.h> | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <adc.h> | ||||
| #include "cdc_acm_constants.h" | ||||
|  | ||||
| #ifdef TEST_DEVICE_B | ||||
| #define TEST_DEVICE_LETTER 'B' | ||||
| #else | ||||
| #define TEST_DEVICE_LETTER 'A' | ||||
| #define COMPOSITE | ||||
| #endif | ||||
|  | ||||
| //#define USE_MS_OS_10 | ||||
| #define USE_MS_OS_20 | ||||
|  | ||||
| #define NATIVE_INTERFACE_0           0 | ||||
| #define ADC_DATA_ENDPOINT            2 | ||||
| #define ADC_DATA_FIFO                USBF2 | ||||
| #define ADC_DATA_PACKET_SIZE         5 | ||||
| #define CMD_ENDPOINT                 3 | ||||
| #define CMD_PACKET_SIZE              32 | ||||
| #define CMD_FIFO                     USBF3 | ||||
|  | ||||
| #ifdef COMPOSITE | ||||
| #define NATIVE_INTERFACE_1           1 | ||||
|  | ||||
| #define CDC_OUT_PACKET_SIZE          64 | ||||
| #define CDC_IN_PACKET_SIZE           64 | ||||
| #define CDC_CONTROL_INTERFACE        2 | ||||
| #define CDC_DATA_INTERFACE           3 | ||||
| #define CDC_NOTIFICATION_ENDPOINT    1 | ||||
| #define CDC_NOTIFICATION_FIFO        USBF1 | ||||
| #define CDC_NOTIFICATION_PACKET_SIZE 10 | ||||
| #define CDC_DATA_ENDPOINT            4 | ||||
| #define CDC_DATA_FIFO                USBF4 | ||||
| #endif | ||||
|  | ||||
| #define REQUEST_GET_MS_DESCRIPTOR    0x20 | ||||
|  | ||||
| // Wireless USB Specification 1.1, Table 7-1 | ||||
| #define USB_DESCRIPTOR_TYPE_SECURITY 12 | ||||
| #define USB_DESCRIPTOR_TYPE_KEY 13 | ||||
| #define USB_DESCRIPTOR_TYPE_ENCRYPTION_TYPE 14 | ||||
| #define USB_DESCRIPTOR_TYPE_BOS 15 | ||||
| #define USB_DESCRIPTOR_TYPE_DEVICE_CAPABILITY 16 | ||||
| #define USB_DESCRIPTOR_TYPE_WIRELESS_ENDPOINT_COMPANION 17 | ||||
|  | ||||
| // Microsoft OS 2.0 Descriptors, Table 1 | ||||
| #define USB_DEVICE_CAPABILITY_TYPE_PLATFORM 5 | ||||
|  | ||||
| // Microsoft OS 2.0 Descriptors, Table 8 | ||||
| #define MS_OS_20_DESCRIPTOR_INDEX 7 | ||||
| #define MS_OS_20_SET_ALT_ENUMERATION 8 | ||||
|  | ||||
| // Microsoft OS 2.0 Descriptors, Table 9 | ||||
| #define MS_OS_20_SET_HEADER_DESCRIPTOR 0x00 | ||||
| #define MS_OS_20_SUBSET_HEADER_CONFIGURATION 0x01 | ||||
| #define MS_OS_20_SUBSET_HEADER_FUNCTION 0x02 | ||||
| #define MS_OS_20_FEATURE_COMPATIBLE_ID 0x03 | ||||
| #define MS_OS_20_FEATURE_REG_PROPERTY 0x04 | ||||
| #define MS_OS_20_FEATURE_MIN_RESUME_TIME 0x05 | ||||
| #define MS_OS_20_FEATURE_MODEL_ID 0x06 | ||||
| #define MS_OS_20_FEATURE_CCGP_DEVICE 0x07 | ||||
|  | ||||
| uint8_t CODE usbStringDescriptorCount = 7; | ||||
| DEFINE_STRING_DESCRIPTOR(languagesString, 1, USB_LANGUAGE_EN_US) | ||||
| DEFINE_STRING_DESCRIPTOR(manufacturerString, 18, 'P','o','l','o','l','u',' ','C','o','r','p','o','r','a','t','i','o','n') | ||||
| DEFINE_STRING_DESCRIPTOR(productString, 17, 'U','S','B',' ','T','e','s','t',' ','D','e','v','i','c','e',' ',TEST_DEVICE_LETTER) | ||||
| DEFINE_STRING_DESCRIPTOR(interface0String, 29, 'U','S','B',' ','T','e','s','t',' ','D','e','v','i','c','e',' ',TEST_DEVICE_LETTER,' ','I','n','t','e','r','f','a','c','e',' ','0') | ||||
| DEFINE_STRING_DESCRIPTOR(interface1String, 29, 'U','S','B',' ','T','e','s','t',' ','D','e','v','i','c','e',' ',TEST_DEVICE_LETTER,' ','I','n','t','e','r','f','a','c','e',' ','1') | ||||
| DEFINE_STRING_DESCRIPTOR(interface2String, 22, 'U','S','B',' ','T','e','s','t',' ','D','e','v','i','c','e',' ',TEST_DEVICE_LETTER,' ','P','o','r','t') | ||||
| #ifdef USE_MS_OS_10 | ||||
| DEFINE_STRING_DESCRIPTOR(osString, 8, 'M','S','F','T','1','0','0', REQUEST_GET_MS_DESCRIPTOR) | ||||
| #endif | ||||
|  | ||||
| uint16_t CODE * CODE usbStringDescriptors[] = | ||||
| { | ||||
|     languagesString, | ||||
|     manufacturerString, | ||||
|     productString, | ||||
|     serialNumberStringDescriptor, | ||||
|     interface0String, | ||||
|     interface1String, | ||||
|     interface2String | ||||
| }; | ||||
|  | ||||
| // See https://msdn.microsoft.com/en-us/library/ff540054.aspx | ||||
| USB_DESCRIPTOR_DEVICE CODE usbDeviceDescriptor = | ||||
| { | ||||
|     sizeof(USB_DESCRIPTOR_DEVICE), | ||||
|     USB_DESCRIPTOR_TYPE_DEVICE, | ||||
| #ifdef USE_MS_OS_20 | ||||
|     0x0201,                 // USB version: 2.0 with LPM ECN | ||||
| #else | ||||
|     0x0200,                 // USB version: 2.0 | ||||
| #endif | ||||
| #ifdef COMPOSITE | ||||
|     0xEF,                   // Class Code | ||||
|     0x02,                   // Subclass code | ||||
|     0x01,                   // Protocol code | ||||
| #else | ||||
|     0xFF,                   // Class Code | ||||
|     0x00,                   // Subclass code | ||||
|     0x00,                   // Protocol code | ||||
| #endif | ||||
|     USB_EP0_PACKET_SIZE,    // Max packet size for Endpoint 0 | ||||
|     USB_VENDOR_ID_POLOLU,   // Vendor ID | ||||
| #ifdef TEST_DEVICE_B | ||||
|     0xDA02,                 // Product ID: USB Test Device B | ||||
| #else | ||||
|     0xDA01,                 // Product ID: USB Test Device A | ||||
| #endif | ||||
|     0x0007,                 // Device release number in BCD format | ||||
|     1,                      // Index of Manufacturer String Descriptor | ||||
|     2,                      // Index of Product String Descriptor | ||||
|     3,                      // Index of Serial Number String Descriptor | ||||
|     1                       // Number of possible configurations. | ||||
| }; | ||||
|  | ||||
| #ifdef COMPOSITE | ||||
|  | ||||
| // Composite configuration descriptor. | ||||
| struct CONFIG1 { | ||||
|     USB_DESCRIPTOR_CONFIGURATION configuration; | ||||
|     USB_DESCRIPTOR_INTERFACE nativeInterface0; | ||||
|     USB_DESCRIPTOR_ENDPOINT adcDataIn; | ||||
|     USB_DESCRIPTOR_ENDPOINT cmdOut; | ||||
|     USB_DESCRIPTOR_ENDPOINT cmdIn; | ||||
|     USB_DESCRIPTOR_INTERFACE nativeInterface1; | ||||
|     USB_DESCRIPTOR_INTERFACE_ASSOCIATION portFunction; | ||||
|     USB_DESCRIPTOR_INTERFACE portCommunicationInterface; | ||||
|     uint8_t portClassSpecific[19]; | ||||
|     USB_DESCRIPTOR_ENDPOINT portNotificationElement; | ||||
|     USB_DESCRIPTOR_INTERFACE portDataInterface; | ||||
|     USB_DESCRIPTOR_ENDPOINT portDataOut; | ||||
|     USB_DESCRIPTOR_ENDPOINT portDataIn; | ||||
| } usbConfigurationDescriptor = | ||||
| { | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_CONFIGURATION), | ||||
|         USB_DESCRIPTOR_TYPE_CONFIGURATION, | ||||
|         sizeof(struct CONFIG1),  // wTotalLength | ||||
|         4,                       // bNumInterfaces | ||||
|         1,                       // bConfigurationValue | ||||
|         0,                       // STRING: iConfiguration | ||||
|         0xC0,                    // bmAttributes: self power capable | ||||
|         50,                      // bMaxPower (in units of 2 mA) | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE, | ||||
|         NATIVE_INTERFACE_0,  // bInterfaceNumber | ||||
|         0,                   // bAlternateSetting | ||||
|         3,                   // bNumEndpoints | ||||
|         0xFF,                // bInterfaceClass: Vendor Specific | ||||
|         0x00,                // bInterfaceSubClass | ||||
|         0x00,                // bInterfaceProtocol | ||||
|         4                    // STRING: iInterface | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_IN | ADC_DATA_ENDPOINT, | ||||
|         USB_TRANSFER_TYPE_INTERRUPT, | ||||
|         ADC_DATA_PACKET_SIZE, | ||||
|         1, | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_OUT | CMD_ENDPOINT, | ||||
|         USB_TRANSFER_TYPE_BULK, | ||||
|         CMD_PACKET_SIZE, | ||||
|         0, | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_IN | CMD_ENDPOINT, | ||||
|         USB_TRANSFER_TYPE_BULK, | ||||
|         CMD_PACKET_SIZE, | ||||
|         0, | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE, | ||||
|         NATIVE_INTERFACE_1,  // bInterfaceNumber | ||||
|         0,                   // bAlternateSetting | ||||
|         0,                   // bNumEndpoints | ||||
|         0xFF,                // bInterfaceClass: Vendor Specific | ||||
|         0x00,                // bInterfaceSubClass | ||||
|         0x00,                // bInterfaceProtocol | ||||
|         5                    // STRING: iInterface | ||||
|     }, | ||||
|  | ||||
|     { // Port Interface Association Descriptor | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE_ASSOCIATION), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, | ||||
|         CDC_CONTROL_INTERFACE,  // first interface number | ||||
|         2,                      // interface count | ||||
|         CDC_CLASS,              // class | ||||
|         CDC_SUBCLASS_ACM,       // subclass | ||||
|         CDC_PROTOCOL_V250,      // protocol (enables automatic Linux support) | ||||
|         6,                      // STRING | ||||
|     }, | ||||
|  | ||||
|     { // Communications Interface: Defines a virtual COM port. | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE, | ||||
|         CDC_CONTROL_INTERFACE,  // bInterfaceNumber | ||||
|         0,                      // bAlternateSetting | ||||
|         1,                      // bNumEndpoints | ||||
|         CDC_CLASS,              // bInterfaceClass | ||||
|         CDC_SUBCLASS_ACM,       // bInterfaceSubClass | ||||
|         CDC_PROTOCOL_V250,      // bInterfaceProtocol | ||||
|         0                       // STRING: iInterface | ||||
|     }, | ||||
|     { // CDC Class-Specific Descriptors describing the virtual COM port. | ||||
|         5, | ||||
|     	CDC_DESCRIPTOR_TYPE_CS_INTERFACE, | ||||
|     	CDC_DESCRIPTOR_SUBTYPE_HEADER, | ||||
|     	0x20,0x01,  // bcdCDC.  We conform to CDC 1.20 | ||||
|  | ||||
|         4, | ||||
|         CDC_DESCRIPTOR_TYPE_CS_INTERFACE, | ||||
|         CDC_DESCRIPTOR_SUBTYPE_ABSTRACT_CONTROL_MANAGEMENT, | ||||
|     	2,  // bmCapabilities.  See USBPSTN1.2 Table 4. | ||||
|  | ||||
|     	5, | ||||
|     	CDC_DESCRIPTOR_TYPE_CS_INTERFACE, | ||||
|     	CDC_DESCRIPTOR_SUBTYPE_UNION, | ||||
|     	CDC_CONTROL_INTERFACE,  // index of the control interface | ||||
|     	CDC_DATA_INTERFACE,     // index of the subordinate interface | ||||
|  | ||||
|     	5, | ||||
|     	CDC_DESCRIPTOR_TYPE_CS_INTERFACE, | ||||
|     	CDC_DESCRIPTOR_SUBTYPE_CALL_MANAGEMENT, | ||||
|     	0x00,  // bmCapabilities.  USBPSTN1.2 Table 3. | ||||
|     	CDC_DATA_INTERFACE | ||||
|     }, | ||||
|     { // USB Command Port notification endpoint. | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_IN | CDC_NOTIFICATION_ENDPOINT,  // bEndpointAddress | ||||
|         USB_TRANSFER_TYPE_INTERRUPT,                          // bmAttributes | ||||
|         CDC_NOTIFICATION_PACKET_SIZE,                         // wMaxPacketSize | ||||
|         1,                                                    // bInterval | ||||
|     }, | ||||
|     { // Data interface: for sending data on the virtual COM port. | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE, | ||||
|         CDC_DATA_INTERFACE,             // bInterfaceNumber | ||||
|         0,                              // bAlternateSetting | ||||
|         2,                              // bNumEndpoints | ||||
|         CDC_DATA_INTERFACE_CLASS,       // bInterfaceClass | ||||
|         0,                              // bInterfaceSubClass | ||||
|         0,                              // bInterfaceProtocol | ||||
|         0                               // STIRNG: iInterface | ||||
|     }, | ||||
|     { // Command port data OUT. | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_OUT | CDC_DATA_ENDPOINT,  // bEndpointAddress | ||||
|         USB_TRANSFER_TYPE_BULK,                        // bmAttributes | ||||
|         CDC_OUT_PACKET_SIZE,                           // wMaxPacketSize | ||||
|         0,                                             // bInterval | ||||
|     }, | ||||
|     { // Command port data IN. | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_IN | CDC_DATA_ENDPOINT,  // bEndpointAddress | ||||
|         USB_TRANSFER_TYPE_BULK,                       // bmAttributes | ||||
|         CDC_IN_PACKET_SIZE,                           // wMaxPacketSize | ||||
|         0,                                            // bInterval | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| #else | ||||
|  | ||||
| // Non-composite configuration descriptor | ||||
| struct CONFIG1 { | ||||
|     USB_DESCRIPTOR_CONFIGURATION configuration; | ||||
|     USB_DESCRIPTOR_INTERFACE nativeInterface0; | ||||
|     USB_DESCRIPTOR_ENDPOINT adcDataIn; | ||||
| } usbConfigurationDescriptor = | ||||
| { | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_CONFIGURATION), | ||||
|         USB_DESCRIPTOR_TYPE_CONFIGURATION, | ||||
|         sizeof(struct CONFIG1),  // wTotalLength | ||||
|         1,                       // bNumInterfaces | ||||
|         1,                       // bConfigurationValue | ||||
|         0,                       // STRING: iConfiguration | ||||
|         0xC0,                    // bmAttributes: self power capable | ||||
|         50,                      // bMaxPower (in units of 2 mA) | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_INTERFACE), | ||||
|         USB_DESCRIPTOR_TYPE_INTERFACE, | ||||
|         NATIVE_INTERFACE_0,  // bInterfaceNumber | ||||
|         0,                   // bAlternateSetting | ||||
|         1,                   // bNumEndpoints | ||||
|         0xFF,                // bInterfaceClass: Vendor Specific | ||||
|         0x00,                // bInterfaceSubClass | ||||
|         0x00,                // bInterfaceProtocol | ||||
|         4                    // STRING: iInterface | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|         sizeof(USB_DESCRIPTOR_ENDPOINT), | ||||
|         USB_DESCRIPTOR_TYPE_ENDPOINT, | ||||
|         USB_ENDPOINT_ADDRESS_IN | ADC_DATA_ENDPOINT, | ||||
|         USB_TRANSFER_TYPE_INTERRUPT, | ||||
|         ADC_DATA_PACKET_SIZE, | ||||
|         1, | ||||
|     }, | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MS_OS_10 | ||||
|  | ||||
| XDATA uint8 compatIdDescriptor[0x28] = | ||||
| { | ||||
|     0x28, 0x00, 0x00, 0x00,    // dwLength | ||||
|     0x00, 0x01,                // bcdVersion: 1.00 | ||||
|     0x04, 0x00,                // wIndex: Compatibility ID | ||||
|     0x01,                      // bCount (number of sections) | ||||
|     0x00, 0x00, 0x00, 0x00,    // reserved | ||||
|     0x00, 0x00, 0x00,          // reserved | ||||
|     0x00,                      // bFirstInterfaceNumber | ||||
|     0x01,                      // reserved | ||||
|     'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,        // compatibleID | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // subCompatibleID | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              // reserved | ||||
| }; | ||||
|  | ||||
| XDATA uint8 extendedPropertiesDescriptor[0x92] = | ||||
| { | ||||
|     0x92, 0x00, 0x00, 0x00,    // dwLength | ||||
|     0x00, 0x01,                // bcdVersion: 1.00 | ||||
|     0x05, 0x00,                // wIndex: extended properties | ||||
|     0x01, 0x00,                // wCount (number of sections) | ||||
|     0x88, 0x00, 0x00, 0x00,    // dwSize of first section | ||||
|     0x07, 0x00, 0x00, 0x00,    // dwPropertyDataType: REG_MULTI_SZ | ||||
|     0x2a, 0x00,                // wPropertyNameLength | ||||
|     'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0, | ||||
|     'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,'s',0,0,0, | ||||
|     0x50, 0x00, 0x00, 0x00,    // dwPropertyDataLength | ||||
|     '{',0,'9',0,'9',0,'c',0,'4',0,'b',0,'b',0,'b',0,'0',0,'-',0, | ||||
|     'e',0,'9',0,'2',0,'5',0,'-',0,'4',0,'3',0,'9',0,'7',0,'-',0, | ||||
|     'a',0,'f',0,'e',0,'e',0,'-',0,'9',0,'8',0,'1',0,'c',0,'d',0, | ||||
|     '0',0,'7',0,'0',0,'2',0,'1',0,'6',0,'3',0,'}',0,0,0,0,0, | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MS_OS_20 | ||||
|  | ||||
| #ifdef COMPOSITE | ||||
|  | ||||
| #define MS_OS_20_LENGTH 0xB2 | ||||
|  | ||||
| // Micrsoft OS 2.0 Descriptor Set for a composite device. | ||||
| XDATA uint8 msOs20DescriptorSet[MS_OS_20_LENGTH] = | ||||
| { | ||||
|     // Microsoft OS 2.0 Descriptor Set header (Table 10) | ||||
|     0x0A, 0x00,  // wLength | ||||
|     MS_OS_20_SET_HEADER_DESCRIPTOR, 0x00, | ||||
|     0x00, 0x00, 0x03, 0x06,  // dwWindowsVersion: Windows 8.1 (NTDDI_WINBLUE) | ||||
|     MS_OS_20_LENGTH, 0x00,  // wTotalLength | ||||
|  | ||||
|     // Microsoft OS 2.0 configuration subset (Table 11) | ||||
|     0x08, 0x00,  // wLength of this header | ||||
|     MS_OS_20_SUBSET_HEADER_CONFIGURATION, 0x00,  // wDescriptorType | ||||
|     0,            // configuration index | ||||
|     0x00,         // bReserved | ||||
|     0xA8, 0x00,   // wTotalLength of this subset | ||||
|  | ||||
|     // Microsoft OS 2.0 function subset header (Table 12) | ||||
|     0x08, 0x00,  // wLength | ||||
|     MS_OS_20_SUBSET_HEADER_FUNCTION, 0x00,  // wDescriptorType | ||||
|     0,           // bFirstInterface | ||||
|     0x00,        // bReserved, | ||||
|     0xA0, 0x00,  // wSubsetLength | ||||
|  | ||||
|     // Microsoft OS 2.0 compatible ID descriptor (Table 13) | ||||
|     0x14, 0x00,                                      // wLength | ||||
|     MS_OS_20_FEATURE_COMPATIBLE_ID, 0x00,            // wDescriptorType | ||||
|     'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,        // compatibleID | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // subCompatibleID | ||||
|  | ||||
|     // Microsoft OS 2.0 registry property descriptor (Table 14) | ||||
|     0x84, 0x00,   // wLength | ||||
|     MS_OS_20_FEATURE_REG_PROPERTY, 0x00, | ||||
|     0x07, 0x00,   // wPropertyDataType: REG_MULTI_SZ | ||||
|     0x2a, 0x00,   // wPropertyNameLength | ||||
|     'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0, | ||||
|     'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,'s',0,0,0, | ||||
|     0x50, 0x00,   // wPropertyDataLength | ||||
|     '{',0,'9',0,'9',0,'c',0,'4',0,'b',0,'b',0,'b',0,'0',0,'-',0, | ||||
|     'e',0,'9',0,'2',0,'5',0,'-',0,'4',0,'3',0,'9',0,'7',0,'-',0, | ||||
|     'a',0,'f',0,'e',0,'e',0,'-',0,'9',0,'8',0,'1',0,'c',0,'d',0, | ||||
|     '0',0,'7',0,'0',0,'2',0,'1',0,'6',0,'3',0,'}',0,0,0,0,0, | ||||
| }; | ||||
|  | ||||
| #else | ||||
|  | ||||
| #define MS_OS_20_LENGTH 0xA2 | ||||
|  | ||||
| // Micrsoft OS 2.0 Descriptor Set for a non-composite device. | ||||
| XDATA uint8 msOs20DescriptorSet[MS_OS_20_LENGTH] = | ||||
| { | ||||
|     // Microsoft OS 2.0 Descriptor Set header (Table 10) | ||||
|     0x0A, 0x00,  // wLength | ||||
|     MS_OS_20_SET_HEADER_DESCRIPTOR, 0x00, | ||||
|     0x00, 0x00, 0x03, 0x06,  // dwWindowsVersion: Windows 8.1 (NTDDI_WINBLUE) | ||||
|     MS_OS_20_LENGTH, 0x00,  // wTotalLength | ||||
|  | ||||
|     // Microsoft OS 2.0 compatible ID descriptor (Table 13) | ||||
|     0x14, 0x00,                                      // wLength | ||||
|     MS_OS_20_FEATURE_COMPATIBLE_ID, 0x00,            // wDescriptorType | ||||
|     'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,        // compatibleID | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // subCompatibleID | ||||
|  | ||||
|     // Microsoft OS 2.0 registry property descriptor (Table 14) | ||||
|     0x84, 0x00,   // wLength | ||||
|     MS_OS_20_FEATURE_REG_PROPERTY, 0x00, | ||||
|     0x07, 0x00,   // wPropertyDataType: REG_MULTI_SZ | ||||
|     0x2a, 0x00,   // wPropertyNameLength | ||||
|     'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0, | ||||
|     'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,'s',0,0,0, | ||||
|     0x50, 0x00,   // wPropertyDataLength | ||||
|     '{',0,'9',0,'9',0,'c',0,'4',0,'b',0,'b',0,'b',0,'0',0,'-',0, | ||||
|     'e',0,'9',0,'2',0,'5',0,'-',0,'4',0,'3',0,'9',0,'7',0,'-',0, | ||||
|     'a',0,'f',0,'e',0,'e',0,'-',0,'9',0,'8',0,'1',0,'c',0,'d',0, | ||||
|     '0',0,'7',0,'0',0,'2',0,'1',0,'6',0,'3',0,'}',0,0,0,0,0, | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| XDATA uint8 bosDescriptor[0x21] = | ||||
| { | ||||
|     0x05,       // bLength of this descriptor | ||||
|     USB_DESCRIPTOR_TYPE_BOS, | ||||
|     0x21, 0x00, // wLength | ||||
|     0x01,       // bNumDeviceCaps | ||||
|  | ||||
|     0x1C,       // bLength of this first device capability descriptor | ||||
|     USB_DESCRIPTOR_TYPE_DEVICE_CAPABILITY, | ||||
|     USB_DEVICE_CAPABILITY_TYPE_PLATFORM, | ||||
|     0x00,       // bReserved | ||||
|     // Microsoft OS 2.0 descriptor platform capability UUID | ||||
|     // from Microsoft OS 2.0 Descriptors,  Table 3. | ||||
|     0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, | ||||
|     0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, | ||||
|  | ||||
|     0x00, 0x00, 0x03, 0x06,  // dwWindowsVersion: Windows 8.1 (NTDDI_WINBLUE) | ||||
|     MS_OS_20_LENGTH, 0x00,              // wMSOSDescriptorSetTotalLength | ||||
|     REQUEST_GET_MS_DESCRIPTOR, | ||||
|     0,                       // bAltEnumCode | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| // Make a buffer that takes 4 packets to transfer. | ||||
| XDATA uint8_t dataBuffer[USB_EP0_PACKET_SIZE * 3 + 4]; | ||||
|  | ||||
| bool adcPaused = 0; | ||||
| uint16_t adcPauseStartTime; | ||||
| uint16_t adcPauseDuration; | ||||
|  | ||||
| bool yellowOn = 0; | ||||
|  | ||||
| void usbCallbackInitEndpoints() | ||||
| { | ||||
|     usbInitEndpointIn(ADC_DATA_ENDPOINT, ADC_DATA_PACKET_SIZE); | ||||
|     usbInitEndpointOut(CMD_ENDPOINT, CMD_PACKET_SIZE); | ||||
|     usbInitEndpointIn(CMD_ENDPOINT, CMD_PACKET_SIZE); | ||||
| } | ||||
|  | ||||
| void usbCallbackSetupHandler() | ||||
| { | ||||
|     switch(usbSetupPacket.bRequest) | ||||
|     { | ||||
|     case REQUEST_GET_MS_DESCRIPTOR: | ||||
|         #ifdef USE_MS_OS_20 | ||||
|         if (usbSetupPacket.bmRequestType == 0xC0 && | ||||
|             usbSetupPacket.wIndex == MS_OS_20_DESCRIPTOR_INDEX) | ||||
|         { | ||||
|             // Request for Microsoft OS 2.0 Descriptor Set. | ||||
|             usbControlRead(sizeof(msOs20DescriptorSet), | ||||
|                 (uint8 XDATA *)&msOs20DescriptorSet); | ||||
|         } | ||||
|         #endif | ||||
|  | ||||
|         #ifdef USE_MS_OS_10 | ||||
|         if (usbSetupPacket.bmRequestType == 0xC0 && | ||||
|             usbSetupPacket.wIndex == 4) | ||||
|         { | ||||
|             // Request for an extended compat ID, as defined in Microsoft | ||||
|             // OS Descriptors 1.0.  This descriptor applies to the whole | ||||
|             // device, so we expect wValue to be 0 here but we do not | ||||
|             // check it. | ||||
|             usbControlRead(sizeof(compatIdDescriptor), | ||||
|                 (uint8 XDATA *)&compatIdDescriptor); | ||||
|         } | ||||
|  | ||||
|         if (usbSetupPacket.bmRequestType == 0xC1 && | ||||
|             usbSetupPacket.wValue == 0 && | ||||
|             usbSetupPacket.wIndex == 5) | ||||
|         { | ||||
|             // Request for extended properties on interface 0. | ||||
|             usbControlRead(sizeof(extendedPropertiesDescriptor), | ||||
|                 (uint8 XDATA *)&extendedPropertiesDescriptor); | ||||
|         } | ||||
|         #endif | ||||
|         return; | ||||
|  | ||||
|     case 0x90:  // Set LED | ||||
|         yellowOn = usbSetupPacket.wValue & 1; | ||||
|         usbControlAcknowledge(); | ||||
|         return; | ||||
|  | ||||
|     case 0x91:  // Read buffer | ||||
|         // The length of the device's response will be equal to wIndex | ||||
|         // so this requrest can be used to simulate what happens when | ||||
|         // the device returns less data than expected. | ||||
|         if (usbSetupPacket.wLength > sizeof(dataBuffer)) | ||||
|         { | ||||
|             return;  // bad size | ||||
|         } | ||||
|         if (usbSetupPacket.wIndex > usbSetupPacket.wLength) | ||||
|         { | ||||
|             return;  // bad size | ||||
|         } | ||||
|         delayMs(usbSetupPacket.wValue); | ||||
|         usbControlRead(usbSetupPacket.wIndex, (XDATA uint8_t *)&dataBuffer); | ||||
|         return; | ||||
|  | ||||
|     case 0x92:  // Write buffer | ||||
|         if (usbSetupPacket.wLength > sizeof(dataBuffer)) | ||||
|         { | ||||
|             return;  // bad size | ||||
|         } | ||||
|         delayMs(usbSetupPacket.wValue); | ||||
|  | ||||
|         if (usbSetupPacket.wLength > 0) | ||||
|         { | ||||
|             usbControlWrite(usbSetupPacket.wLength, (XDATA uint8_t *)&dataBuffer); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             usbControlAcknowledge(); | ||||
|         } | ||||
|         return; | ||||
|  | ||||
|     case 0xA0:  // Pause or unpause the ADC data stream. | ||||
|         if (usbSetupPacket.wValue == 0) | ||||
|         { | ||||
|             adcPaused = 0; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             adcPaused = 1; | ||||
|             adcPauseStartTime = getMs(); | ||||
|             adcPauseDuration = usbSetupPacket.wValue; | ||||
|         } | ||||
|         usbControlAcknowledge(); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void usbCallbackClassDescriptorHandler() | ||||
| { | ||||
| #ifdef USE_MS_OS_10 | ||||
|     if (usbSetupPacket.wValue == 0x03EE) | ||||
|     { | ||||
|         // Microsoft OS String descriptor | ||||
|         usbControlRead(sizeof(osString), (uint8 XDATA *)&osString); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MS_OS_20 | ||||
|     if (usbSetupPacket.wValue == 0x0f00) | ||||
|     { | ||||
|         // BOS Descriptor | ||||
|         usbControlRead(sizeof(bosDescriptor), bosDescriptor); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void usbCallbackControlWriteHandler() | ||||
| { | ||||
| } | ||||
|  | ||||
| // Sends ADC data to the computer with a USB endpoint. | ||||
| void adcDataTx() | ||||
| { | ||||
|     if (usbDeviceState != USB_STATE_CONFIGURED) | ||||
|     { | ||||
|         // We have not reached the Configured state yet, so we should not be | ||||
|         // touching the non-zero endpoints. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (adcPaused) | ||||
|     { | ||||
|         if ((uint16_t)(getMs() - adcPauseStartTime) >= adcPauseDuration) | ||||
|         { | ||||
|             adcPaused = 0; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     USBINDEX = ADC_DATA_ENDPOINT; | ||||
|  | ||||
|     if (!(USBCSIL & USBCSIL_INPKT_RDY)) | ||||
|     { | ||||
|         uint16_t reading; | ||||
|  | ||||
|         // There is buffer space available, so queue up an IN | ||||
|         // packet with some stuff in it. | ||||
|  | ||||
|         // Send the USB frame number. | ||||
|         ADC_DATA_FIFO = USBFRML; | ||||
|         ADC_DATA_FIFO = USBFRMH; | ||||
|  | ||||
|         // Send the ADC reading. | ||||
|         reading = adcRead(5); // P0_5 | ||||
|         ADC_DATA_FIFO = reading & 0xFF; | ||||
|         ADC_DATA_FIFO = reading >> 8 & 0xFF; | ||||
|  | ||||
|         // Send a constant byte so that we have some data that can actually be | ||||
|         // checked for correctness in an automated test. | ||||
|         ADC_DATA_FIFO = 0xAB; | ||||
|  | ||||
|         USBCSIL |= USBCSIL_INPKT_RDY; | ||||
|  | ||||
|         // Notify the USB library that some activity has occurred. | ||||
|         usbActivityFlag = 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmdService() | ||||
| { | ||||
|     uint8_t count, cmd, i; | ||||
|     uint16_t delay; | ||||
|  | ||||
|     if (usbDeviceState != USB_STATE_CONFIGURED) { return; } | ||||
|  | ||||
|     USBINDEX = CMD_ENDPOINT; | ||||
|     if (!(USBCSOL & USBCSOL_OUTPKT_RDY)) | ||||
|     { | ||||
|         // No command packet available right now. | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     count = USBCNTL; | ||||
|  | ||||
|     if (count == 0) | ||||
|     { | ||||
|         // Empty packet | ||||
|         dataBuffer[0] = 0x66; | ||||
|     } | ||||
|  | ||||
|     if (count >= 2) | ||||
|     { | ||||
|       cmd = CMD_FIFO; | ||||
|  | ||||
|       // Command 0x92: Set a byte in dataBuffer | ||||
|       if (cmd == 0x92) | ||||
|       { | ||||
|           dataBuffer[0] = CMD_FIFO; | ||||
|       } | ||||
|  | ||||
|       // Command 0xDE: Delay | ||||
|       if (cmd == 0xDE) | ||||
|       { | ||||
|           delay = CMD_FIFO; | ||||
|           delay += CMD_FIFO << 8; | ||||
|           delayMs(delay); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < count; i++) | ||||
|     { | ||||
|       CMD_FIFO; | ||||
|     } | ||||
|  | ||||
|     USBCSOL &= ~USBCSOL_OUTPKT_RDY;  // Done with this packet. | ||||
| } | ||||
|  | ||||
| void main() | ||||
| { | ||||
|     uint8_t x = 0; | ||||
|     setDigitalInput(0, 1); | ||||
|     systemInit(); | ||||
|     usbInit(); | ||||
|     while(1) | ||||
|     { | ||||
|         boardService(); | ||||
|         usbShowStatusWithGreenLed(); | ||||
|         usbPoll(); | ||||
|         adcDataTx(); | ||||
|         cmdService(); | ||||
|         LED_YELLOW(yellowOn); | ||||
|  | ||||
|         if (!isPinHigh(0)) | ||||
|         { | ||||
|             boardStartBootloader(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										4
									
								
								dep/libusbp/test/firmware/wixel/prepare_sdk.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								dep/libusbp/test/firmware/wixel/prepare_sdk.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| set -e | ||||
| git clone git@github.com:pololu/wixel-sdk | ||||
| cd wixel-sdk | ||||
| make libs | ||||
							
								
								
									
										290
									
								
								dep/libusbp/test/generic_handle_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								dep/libusbp/test/generic_handle_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,290 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| TEST_CASE("generic_handle traits") | ||||
| { | ||||
|     libusbp::generic_handle handle, handle2; | ||||
|  | ||||
|     SECTION("is not copy-constructible") | ||||
|     { | ||||
|         REQUIRE_FALSE(std::is_copy_constructible<libusbp::generic_handle>::value); | ||||
|  | ||||
|         // Should not compile: | ||||
|         // libusbp::generic_handle handle3(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("is not copy-assignable") | ||||
|     { | ||||
|         REQUIRE_FALSE(std::is_copy_assignable<libusbp::generic_handle>::value); | ||||
|  | ||||
|         // Should not compile: | ||||
|         // handle2 = handle; | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("generic handles cannot be created from a NULL generic interface") | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         libusbp::generic_interface gi; | ||||
|         libusbp::generic_handle handle(gi); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         REQUIRE(std::string(error.what()) == "Generic interface is null."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("null generic_handle") | ||||
| { | ||||
|     libusbp::generic_handle handle; | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         REQUIRE_FALSE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("can be closed (but it makes no difference)") | ||||
|     { | ||||
|         handle.close(); | ||||
|         REQUIRE_FALSE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot open async pipes") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.open_async_in_pipe(0x82); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Generic handle is null."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot set timeouts") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.set_timeout(0, 0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Generic handle is null."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot do control transfers") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.control_transfer(0x40, 0x90, 0, 0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Generic handle is null."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot read a pipe") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(0x82, NULL, 0, NULL); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Generic handle is null."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("cannot open an asynchronous IN pipe") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.open_async_in_pipe(0x82); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Generic handle is null."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("exports invalid underlying handles") | ||||
|     { | ||||
| #if defined(_WIN32) | ||||
|         REQUIRE(handle.get_winusb_handle() == INVALID_HANDLE_VALUE); | ||||
| #elif defined(__linux__) | ||||
|         REQUIRE(handle.get_fd() == -1); | ||||
| #elif defined(__APPLE__) | ||||
|         REQUIRE(handle.get_cf_plug_in() == NULL); | ||||
| #else | ||||
|         REQUIRE(0); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("generic handle parameter validation and corner cases") | ||||
| { | ||||
|     SECTION("libusbp_generic_handle_open") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is null") | ||||
|         { | ||||
|             libusbp::error error(libusbp_generic_handle_open(NULL, NULL)); | ||||
|             REQUIRE(error.message() == "Generic handle output pointer is null."); | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output to NULL is possible") | ||||
|         { | ||||
|             libusbp_generic_handle * p = (libusbp_generic_handle *)1; | ||||
|             libusbp::error error(libusbp_generic_handle_open(NULL, &p)); | ||||
|             REQUIRE((p == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_generic_handle_open_async_in_pipe") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is null") | ||||
|         { | ||||
|             libusbp::error error(libusbp_generic_handle_open_async_in_pipe(NULL, 0, NULL)); | ||||
|             REQUIRE(error.message() == "Pipe output pointer is null."); | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output pointer to NULL if possible") | ||||
|         { | ||||
|             libusbp_async_in_pipe * p = (libusbp_async_in_pipe *)1; | ||||
|             libusbp::error error(libusbp_generic_handle_open_async_in_pipe(NULL, 0, &p)); | ||||
|             REQUIRE((p == NULL)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("generic_handle instance", "[ghitda]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|  | ||||
|     SECTION("is present") | ||||
|     { | ||||
|         REQUIRE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("is movable") | ||||
|     { | ||||
|         libusbp::generic_handle handle2 = std::move(handle); | ||||
|         REQUIRE(handle2); | ||||
|         REQUIRE_FALSE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("is move-assignable") | ||||
|     { | ||||
|         libusbp::generic_handle handle2; | ||||
|         handle2 = std::move(handle); | ||||
|         REQUIRE(handle2); | ||||
|         REQUIRE_FALSE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("can be closed") | ||||
|     { | ||||
|         handle.close(); | ||||
|         REQUIRE_FALSE(handle); | ||||
|     } | ||||
|  | ||||
|     SECTION("can export its underlying handles") | ||||
|     { | ||||
| #if defined(_WIN32) | ||||
|         REQUIRE((handle.get_winusb_handle() != NULL)); | ||||
|         REQUIRE((handle.get_winusb_handle() != INVALID_HANDLE_VALUE)); | ||||
| #elif defined(__linux__) | ||||
|         REQUIRE(handle.get_fd() > 2); | ||||
| #elif defined(__APPLE__) | ||||
|         IOCFPlugInInterface ** plug_in = (IOCFPlugInInterface **)handle.get_cf_plug_in(); | ||||
|         REQUIRE(plug_in != NULL); | ||||
|         IOUSBInterfaceInterface197 ** ioh; | ||||
|         HRESULT hr = (*plug_in)->QueryInterface(plug_in, | ||||
|             CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID197), (void **)&ioh); | ||||
|         REQUIRE(!hr); | ||||
|         (*ioh)->Release(ioh); | ||||
| #else | ||||
|         REQUIRE(0);  // this platform is missing a way to export its underlying handles | ||||
| #endif | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("generic_handle creation for Test Device A") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|  | ||||
|     SECTION("can be created and closed quickly several times") | ||||
|     { | ||||
|         for(unsigned int i = 0; i < 10; i++) | ||||
|         { | ||||
|             libusbp::generic_handle handle(gi); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #if defined(_WIN32) || defined(__APPLE__) | ||||
|     SECTION("only one per interface can be open at a time") | ||||
|     { | ||||
|         libusbp::generic_handle handle1(gi); | ||||
|         try | ||||
|         { | ||||
|             libusbp::generic_handle handle2(gi); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             CHECK(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|         } | ||||
|     } | ||||
| #else | ||||
|     SECTION("multiple handles to the same interface can be open at one time") | ||||
|     { | ||||
|         libusbp::generic_handle handle1(gi); | ||||
|         libusbp::generic_handle handle2(gi); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_B | ||||
| TEST_CASE("generic_handle creation for Test Device B") | ||||
| { | ||||
|     libusbp::device device = find_test_device_b(); | ||||
|     libusbp::generic_interface gi(device, 0, false); | ||||
|  | ||||
|     SECTION("can be created and closed quickly several times") | ||||
|     { | ||||
|         for(unsigned int i = 0; i < 10; i++) | ||||
|         { | ||||
|             libusbp::generic_handle handle(gi); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #if defined(_WIN32) || defined(__APPLE__) | ||||
|     SECTION("only one per interface can be open at a time") | ||||
|     { | ||||
|         libusbp::generic_handle handle1(gi); | ||||
|         try | ||||
|         { | ||||
|             libusbp::generic_handle handle2(gi); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             CHECK(error.has_code(LIBUSBP_ERROR_ACCESS_DENIED)); | ||||
|         } | ||||
|     } | ||||
| #else | ||||
|     SECTION("multiple handles to the same interface can be open at one time") | ||||
|     { | ||||
|         libusbp::generic_handle handle1(gi); | ||||
|         libusbp::generic_handle handle2(gi); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										325
									
								
								dep/libusbp/test/generic_interface_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								dep/libusbp/test/generic_interface_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,325 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| TEST_CASE("generic interface cannot be created from a NULL device") | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         libusbp::device device; | ||||
|         libusbp::generic_interface gi(device, 0, true); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         REQUIRE(std::string(error.what()) == "Device is null."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void check_null_gi_error(const libusbp::error & error) | ||||
| { | ||||
|     CHECK(error.message() == "Generic interface is null."); | ||||
| } | ||||
|  | ||||
| TEST_CASE("null generic interface") | ||||
| { | ||||
|     libusbp::generic_interface gi; | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         REQUIRE_FALSE(gi); | ||||
|     } | ||||
|  | ||||
|     SECTION("is copyable") | ||||
|     { | ||||
|         libusbp::generic_interface gi2 = gi; | ||||
|         REQUIRE_FALSE(gi2); | ||||
|     } | ||||
|  | ||||
|     SECTION("get_os_id returns an error") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             gi.get_os_id(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_gi_error(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("get_os_filename returns an error") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             gi.get_os_filename(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_gi_error(error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("generic interface parameter validation and corner cases") | ||||
| { | ||||
|     SECTION("libusbp_generic_interface_create") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_generic_interface_create( | ||||
|                         NULL, 0, true, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Generic interface output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output to NULL if there is an error") | ||||
|         { | ||||
|             libusbp_generic_interface * ptr = (libusbp_generic_interface *)-1; | ||||
|             libusbp::error error(libusbp_generic_interface_create(NULL, 0, true, &ptr)); | ||||
|             REQUIRE((ptr == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_generic_interface_copy") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_generic_interface_copy(NULL, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Generic interface output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output pointer to NULL when copying NULL") | ||||
|         { | ||||
|             libusbp_generic_interface * gi = (libusbp_generic_interface *)-1; | ||||
|             libusbp::throw_if_needed(libusbp_generic_interface_copy(NULL, &gi)); | ||||
|             REQUIRE((gi == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_generic_interface_get_os_id") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_generic_interface_get_os_id(NULL, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "String output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output string to NULL if it can't return anything") | ||||
|         { | ||||
|             char * s = (char *)-1; | ||||
|             libusbp::error error(libusbp_generic_interface_get_os_id(NULL, &s)); | ||||
|             REQUIRE((s == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_generic_interface_get_os_filename") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed( | ||||
|                     libusbp_generic_interface_get_os_filename(NULL, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "String output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output string to NULL if it can't return anything") | ||||
|         { | ||||
|             char * s = (char *)-1; | ||||
|             libusbp::error error(libusbp_generic_interface_get_os_filename(NULL, &s)); | ||||
|             REQUIRE((s == NULL)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| static void assert_not_ready( | ||||
|     const libusbp::device & device, uint8_t interface_number, bool composite) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         libusbp::generic_interface gi(device, interface_number, composite); | ||||
|         CHECK(gi); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         if (!error.has_code(LIBUSBP_ERROR_NOT_READY)) { throw; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| __attribute__((__unused__)) | ||||
| static void assert_incorrect_driver( | ||||
|     const libusbp::device & device, | ||||
|     uint8_t interface_number, | ||||
|     bool composite, | ||||
|     std::string driver_name) | ||||
| { | ||||
|     std::string expected = | ||||
|         std::string("Failed to initialize generic interface.  ") + | ||||
|         "Device is attached to an incorrect driver: " | ||||
|         + driver_name + "."; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         libusbp::generic_interface gi(device, interface_number, composite); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         CHECK_FALSE(error.has_code(LIBUSBP_ERROR_NOT_READY)); | ||||
|         CHECK(error.message() == expected); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| static void assert_no_driver( | ||||
|     const libusbp::device & device, uint8_t interface_number, bool composite) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         libusbp::generic_interface gi(device, interface_number, composite); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         CHECK(error.has_code(LIBUSBP_ERROR_NOT_READY)); | ||||
|         CHECK(std::string(error.what()) == | ||||
|             "Failed to initialize generic interface.  " | ||||
|             "Device is not using any driver."); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("Test Device A generic interface ", "[tdagi]") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|  | ||||
|     SECTION("interface 0 with incorrect hint composite=false") | ||||
|     { | ||||
|         #ifdef _WIN32 | ||||
|         // Causes a problem in Windows. | ||||
|         assert_incorrect_driver(device, 0, false, "usbccgp"); | ||||
|         #else | ||||
|         // Works fine in Linux, which ignores that hint. | ||||
|         libusbp::generic_interface gi(device, 0, false); | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     SECTION("interface 0") | ||||
|     { | ||||
|         libusbp::generic_interface gi(device, 0, true); | ||||
|  | ||||
|         SECTION("has a good OS id") | ||||
|         { | ||||
|             std::string id = gi.get_os_id(); | ||||
|             #ifdef _WIN32 | ||||
|             REQUIRE(id.substr(0, 12) == "USB\\VID_1FFB"); | ||||
|             #elif defined(__linux__) | ||||
|             REQUIRE(id.substr(0, 13) == "/sys/devices/"); | ||||
|             #else | ||||
|             REQUIRE(id.size() > 1); | ||||
|             for(size_t i = 0; i < id.size(); i++) | ||||
|             { | ||||
|                 char c = id[i]; | ||||
|                 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) | ||||
|                 { | ||||
|                     throw id + " is a bad OS ID for a generic interface"; | ||||
|                 } | ||||
|             } | ||||
|             #endif | ||||
|             REQUIRE(id != device.get_os_id()); | ||||
|         } | ||||
|  | ||||
|         SECTION("has a good OS filename") | ||||
|         { | ||||
|             std::string filename = gi.get_os_filename(); | ||||
|             #ifdef _WIN32 | ||||
|             REQUIRE(filename.substr(0, 16) == "\\\\?\\usb#vid_1ffb"); | ||||
|             #elif defined(__linux__) | ||||
|             REQUIRE(filename.substr(0, 13) == "/dev/bus/usb/"); | ||||
|             #else | ||||
|             REQUIRE(filename.size() > 0); | ||||
|             for(size_t i = 0; i < filename.size(); i++) | ||||
|             { | ||||
|                 char c = filename[i]; | ||||
|                 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) | ||||
|                 { | ||||
|                     throw filename + " is a bad OS ID for a generic interface"; | ||||
|                 } | ||||
|             } | ||||
|             #endif | ||||
|  | ||||
|             REQUIRE(filename != device.get_os_id()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("interface 1 (native interface without Windows drivers)") | ||||
|     { | ||||
|         #ifdef _WIN32 | ||||
|         assert_no_driver(device, 1, true); | ||||
|         #else | ||||
|         libusbp::generic_interface gi(device, 1, true); | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     SECTION("interface 2 (serial port)") | ||||
|     { | ||||
|         // See comments for libusbp_generic_interface_create for information | ||||
|         // about why these are different. | ||||
|         #if defined(_WIN32) | ||||
|         assert_incorrect_driver(device, 2, true, "usbser"); | ||||
|         #elif defined(__linux__) | ||||
|         assert_incorrect_driver(device, 2, true, "cdc_acm"); | ||||
|         #elif defined(__APPLE__) | ||||
|         libusbp::generic_interface(device, 2, true); | ||||
|         #else | ||||
|         REQUIRE(0); | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     SECTION("interface 44 (does not exist)") | ||||
|     { | ||||
|         assert_not_ready(device, 44, true); | ||||
|     } | ||||
|  | ||||
|     SECTION("interfaces can be copied") | ||||
|     { | ||||
|         libusbp::generic_interface gi(device, 0, true); | ||||
|         libusbp::generic_interface gi2 = gi; | ||||
|         REQUIRE(gi2); | ||||
|         REQUIRE(gi.get_os_id() == gi2.get_os_id()); | ||||
|         REQUIRE(gi.get_os_filename() == gi2.get_os_filename()); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										99
									
								
								dep/libusbp/test/list_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								dep/libusbp/test/list_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| /* Tests the functions we provide for finding/listing devices. */ | ||||
|  | ||||
| #include <test_helper.h> | ||||
|  | ||||
| TEST_CASE("list_connected_device (C++)") | ||||
| { | ||||
|     #ifdef USE_TEST_DEVICE_A | ||||
|     SECTION("can find Test Device A") | ||||
|     { | ||||
|         libusbp::device found_device; | ||||
|         std::vector<libusbp::device> list = libusbp::list_connected_devices(); | ||||
|         for (auto it = list.begin(); it != list.end(); ++it) | ||||
|         { | ||||
|             libusbp::device device = *it; | ||||
|             if (!device) | ||||
|             { | ||||
|                 throw "A null device was returned by list_connected_devices."; | ||||
|             } | ||||
|             if (device.get_vendor_id() == 0x1FFB && device.get_product_id() == 0xDA01) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         throw "Test Device A not found."; | ||||
|     } | ||||
|     #endif | ||||
| } | ||||
|  | ||||
| TEST_CASE("list_connected_devices (C)") | ||||
| { | ||||
|     libusbp_device ** list = NULL; | ||||
|     size_t device_count = 4444; | ||||
|  | ||||
|     SECTION("gives a null-terminated list") | ||||
|     { | ||||
|         libusbp_error * error = libusbp_list_connected_devices(&list, &device_count); | ||||
|         if (error != NULL) { throw libusbp::error(error); } | ||||
|         for (size_t i = 0; i < device_count; i++) | ||||
|         { | ||||
|             CHECK(list[i]); | ||||
|         } | ||||
|         CHECK_FALSE(list[device_count]); | ||||
|     } | ||||
|  | ||||
|     SECTION("does not crash if called with a NULL device_list argument") | ||||
|     { | ||||
|         libusbp::error error(libusbp_list_connected_devices(NULL, &device_count)); | ||||
|         REQUIRE(error.message() == "Device list output pointer is null."); | ||||
|         REQUIRE(device_count == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("does not complain if called with a NULL device_count argument") | ||||
|     { | ||||
|         libusbp_error * error = libusbp_list_connected_devices(&list, NULL); | ||||
|         if (error != NULL) { throw libusbp::error(error); } | ||||
|     } | ||||
|  | ||||
|     if (list != NULL) | ||||
|     { | ||||
|         libusbp_device ** device = list; | ||||
|         while(*device) | ||||
|         { | ||||
|             libusbp_device_free(*device); | ||||
|             device++; | ||||
|         } | ||||
|         libusbp_list_free(list); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("find_device_with_vid_pid (C++)") | ||||
| { | ||||
|     #ifdef USE_TEST_DEVICE_A | ||||
|     SECTION("can find Test Device A") | ||||
|     { | ||||
|         libusbp::device device = libusbp::find_device_with_vid_pid(0x1FFB, 0xDA01); | ||||
|         REQUIRE(device); | ||||
|         REQUIRE(device.get_vendor_id() == 0x1FFB); | ||||
|         REQUIRE(device.get_product_id() == 0xDA01); | ||||
|     } | ||||
|     #endif | ||||
|  | ||||
|     SECTION("returns a null device if the specified one is not found") | ||||
|     { | ||||
|         // It's not an error when a device you are looking for isn't connected | ||||
|         // to the system, it's normal and expected, and you should be checking | ||||
|         // for it and handling it explicitly. | ||||
|         libusbp::device device = libusbp::find_device_with_vid_pid(0xABCD, 0x1234); | ||||
|         REQUIRE_FALSE(device); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("find_device_with_vid_pid (C)") | ||||
| { | ||||
|     SECTION("complains if the output pointer is NULL") | ||||
|     { | ||||
|         libusbp::error error(libusbp_find_device_with_vid_pid(0xABCD, 0x1234, NULL)); | ||||
|         REQUIRE(error.message() == "Device output pointer is null."); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								dep/libusbp/test/main_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								dep/libusbp/test/main_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #define CATCH_CONFIG_RUNNER | ||||
| #include <test_helper.h> | ||||
|  | ||||
| int main (int argc, char ** argv) | ||||
| { | ||||
|     #ifndef USE_TEST_DEVICE_A | ||||
|     std::cerr << "Warning: Tests involving Test Device A will be skipped," << std::endl; | ||||
|     std::cerr << "  so a lot of the library's features will not be tested." << std::endl; | ||||
|     #endif | ||||
|  | ||||
|     #ifndef USE_TEST_DEVICE_B | ||||
|     std::cerr << "Warning: Tests involving Test Device B will be skipped," << std::endl; | ||||
|     std::cerr << "  so bugs related to non-composite devices might be missed." << std::endl; | ||||
|     #endif | ||||
|  | ||||
|     #ifdef NDEBUG | ||||
|     std::cerr << "Warning: skipping unit tests because this is not a debug build.\n"; | ||||
|     #endif | ||||
|  | ||||
|     // If the last argument is "-p", then pause after the tests are run. | ||||
|     // This allows us to run "leaks" on Mac OS X to check for memory leaks. | ||||
|     bool pause_after_test = false; | ||||
|     if (argc && std::string(argv[argc - 1]) == "-p") | ||||
|     { | ||||
|         pause_after_test = true; | ||||
|         argc--; | ||||
|     } | ||||
|  | ||||
|     int result = Catch::Session().run(argc, argv); | ||||
|  | ||||
|     if (pause_after_test) | ||||
|     { | ||||
|         printf("Press enter to continue."); | ||||
|         std::string s; | ||||
|         std::cin >> s; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										256
									
								
								dep/libusbp/test/read_pipe_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								dep/libusbp/test/read_pipe_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("read_pipe parameter checking") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     const uint8_t pipe = 0x82; | ||||
|     size_t transferred = 0xFFFF; | ||||
|  | ||||
|     SECTION("sets transferred to zero if possible") | ||||
|     { | ||||
|         size_t transferred = 1; | ||||
|         libusbp::error error(libusbp_read_pipe(NULL, | ||||
|                 0, NULL, 0, &transferred)); | ||||
|         REQUIRE(transferred == 0); | ||||
|     } | ||||
|  | ||||
|     SECTION("requires the size to be non-zero") | ||||
|     { | ||||
|         // The corner case of the transfer size being zero seems to put the USB | ||||
|         // drivers in Linux in a weird state, so let's just not allow it. | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(pipe, NULL, 0, &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == | ||||
|                 "Failed to read from pipe.  " | ||||
|                 "Transfer size 0 is not allowed."); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("requires the buffer to be non-NULL") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(pipe, NULL, 2, &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == | ||||
|                 "Failed to read from pipe.  " | ||||
|                 "Buffer is null."); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("requires the direction bit to be correct") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             uint8_t buffer[5]; | ||||
|             handle.read_pipe(0x02, buffer, sizeof(buffer), &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(std::string(error.what()) == | ||||
|                 "Failed to read from pipe.  " | ||||
|                 "Invalid pipe ID 0x02."); | ||||
|             REQUIRE(transferred == 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("checks the size") | ||||
|     { | ||||
|         uint8_t buffer[5]; | ||||
|  | ||||
|         #if defined(_WIN32) | ||||
|         size_t too_large_size = (size_t)ULONG_MAX + 1; | ||||
|         #elif defined(__linux__) | ||||
|         size_t too_large_size = (size_t)UINT_MAX + 1; | ||||
|         #elif defined(__APPLE__) | ||||
|         size_t too_large_size = (size_t)UINT32_MAX + 1; | ||||
|         #else | ||||
|         #error add a case for this OS | ||||
|         #endif | ||||
|  | ||||
|         if (too_large_size == 0) { return; } | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(pipe, buffer, too_large_size, NULL); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             REQUIRE(error.message() == "Failed to read from pipe.  Transfer size is too large."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("read_pipe (synchronous) on an interrupt endpoint ", "[rpi]") | ||||
| { | ||||
|     // We assume that if read_pipe works on an interrupt endpoint, it will also | ||||
|     // work on a bulk endpoint because of the details of the underlying APIs | ||||
|     // that libusbp uses. | ||||
|  | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|     libusbp::generic_interface gi(device, 0, true); | ||||
|     libusbp::generic_handle handle(gi); | ||||
|     const uint8_t pipe = 0x82; | ||||
|     size_t transferred = 0xFFFF; | ||||
|     const size_t packet_size = 5; | ||||
|  | ||||
|     // Unpause the ADC if it was paused by some previous test. | ||||
|     handle.control_transfer(0x40, 0xA0, 0, 0); | ||||
|  | ||||
|     SECTION("can read one packet") | ||||
|     { | ||||
|         uint8_t buffer[packet_size]; | ||||
|         handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|         REQUIRE(transferred == packet_size); | ||||
|         REQUIRE(buffer[4] == 0xAB); | ||||
|     } | ||||
|  | ||||
|     SECTION("can read two packets") | ||||
|     { | ||||
|         uint8_t buffer[packet_size * 2]; | ||||
|         handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|         REQUIRE(transferred == packet_size * 2); | ||||
|         REQUIRE(buffer[4] == 0xAB); | ||||
|         REQUIRE(buffer[4 + packet_size] == 0xAB); | ||||
|     } | ||||
|  | ||||
|     SECTION("can read without returning the size transferred") | ||||
|     { | ||||
|         // But this is bad; you should be checking how many bytes are | ||||
|         // transferred. | ||||
|         uint8_t buffer[packet_size]; | ||||
|         handle.read_pipe(pipe, buffer, sizeof(buffer), NULL); | ||||
|         REQUIRE(buffer[4] == 0xAB); | ||||
|     } | ||||
|  | ||||
|     SECTION("overflows when transfer size is not a multiple of the packet size") | ||||
|     { | ||||
|         #ifdef VBOX_LINUX_ON_WINDOWS | ||||
|         // This test fails and then puts the USB device into a weird state | ||||
|         // if run on Linux inside VirtualBox on a Windows host. | ||||
|         std::cerr << "Skipping synchronous IN pipe overflow test.\n"; | ||||
|         return; | ||||
|         #endif | ||||
|  | ||||
|         uint8_t buffer[packet_size + 1]; | ||||
|         size_t transferred; | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             const char * expected = | ||||
|                 "Failed to read from pipe.  " | ||||
|                 "The transfer overflowed.  " | ||||
|                 #if defined(_WIN32) | ||||
|                 #elif defined(__linux__) | ||||
|                 "Error code 75." | ||||
|                 #elif defined(__APPLE__) | ||||
|                 "Error code 0xe00002e8." | ||||
|                 #endif | ||||
|                 ; | ||||
|             REQUIRE(error.message() == expected); | ||||
|  | ||||
|             #ifdef __APPLE__ | ||||
|             REQUIRE(transferred == packet_size + 1); | ||||
|             #else | ||||
|             REQUIRE(transferred == 0); | ||||
|             #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #ifdef __APPLE__ | ||||
|     SECTION("does not have an adjustable timeout for interrupt endpoints") | ||||
|     { | ||||
|         handle.set_timeout(pipe, 1); | ||||
|  | ||||
|         uint8_t buffer[packet_size]; | ||||
|         try | ||||
|         { | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), NULL); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             // A copy of this error message is in the README, so be sure | ||||
|             // to update the README if you have to update this. | ||||
|             REQUIRE(error.message() == "Failed to read from pipe.  " | ||||
|                 "(iokit/common) invalid argument.  Error code 0xe00002c2."); | ||||
|         } | ||||
|     } | ||||
|     #else | ||||
|     SECTION("has an adjustable timeout") | ||||
|     { | ||||
|         // Pause the ADC for 50 ms, then try to read data from it with a 1 ms | ||||
|         // timeout and observe that it fails. | ||||
|  | ||||
|         handle.set_timeout(pipe, 1); | ||||
|         handle.control_transfer(0x40, 0xA0, 50, 0); | ||||
|  | ||||
|         uint8_t buffer[packet_size]; | ||||
|         try | ||||
|         { | ||||
|             // We use two extra reads to clear out packets that might have been | ||||
|             // queued on the device before we stopped the pipe. | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|  | ||||
|             // This last read will hopefully time out. | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             const char * expected = | ||||
|                 "Failed to read from pipe.  " | ||||
|                 "The operation timed out.  " | ||||
|                 #ifdef _WIN32 | ||||
|                 "Windows error code 0x79." | ||||
|                 #elif defined(__linux__) | ||||
|                 "Error code 110." | ||||
|                 #endif | ||||
|                 ; | ||||
|             REQUIRE(std::string(error.what()) == expected); | ||||
|             REQUIRE(error.has_code(LIBUSBP_ERROR_TIMEOUT)); | ||||
|  | ||||
|             #ifdef _WIN32 | ||||
|             // Sometimes WinUSB transfers 5 bytes successfully but it still | ||||
|             // reports a timeout.  This seems to depend on what computer you are | ||||
|             // running the tests on; on some computers it happens about half the | ||||
|             // time, and on some computers it never happens.  When this was | ||||
|             // first observed, libusbp was not using RAW_IO mode. | ||||
|             REQUIRE((transferred == 0 || transferred == 5)); | ||||
|             #else | ||||
|             REQUIRE(transferred == 0); | ||||
|             #endif | ||||
|         } | ||||
|  | ||||
|         // Do the same thing with a higher timeout and observe that it works. | ||||
|         handle.set_timeout(pipe, 500); | ||||
|         handle.control_transfer(0x40, 0xA0, 50, 0); | ||||
|         for (uint8_t i = 0; i < 3; i++) | ||||
|         { | ||||
|             handle.read_pipe(pipe, buffer, sizeof(buffer), &transferred); | ||||
|             REQUIRE(transferred == 5); | ||||
|         } | ||||
|     } | ||||
|     #endif | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										181
									
								
								dep/libusbp/test/serial_port_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								dep/libusbp/test/serial_port_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| #include <test_helper.h> | ||||
|  | ||||
| TEST_CASE("serial port cannot be created from a NULL device") | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         libusbp::device device; | ||||
|         libusbp::serial_port sp(device, 0, true); | ||||
|         REQUIRE(0); | ||||
|     } | ||||
|     catch(const libusbp::error & error) | ||||
|     { | ||||
|         REQUIRE(error.message() == "Device is null."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void check_null_sp_error(const libusbp::error & error) | ||||
| { | ||||
|     CHECK(error.message() == "Serial port is null."); | ||||
| } | ||||
|  | ||||
| TEST_CASE("null serial port") | ||||
| { | ||||
|     libusbp::serial_port sp; | ||||
|  | ||||
|     SECTION("is not present") | ||||
|     { | ||||
|         REQUIRE_FALSE(sp); | ||||
|     } | ||||
|  | ||||
|     SECTION("is copyable") | ||||
|     { | ||||
|         libusbp::serial_port sp2 = sp; | ||||
|         REQUIRE_FALSE(sp2); | ||||
|     } | ||||
|  | ||||
|     SECTION("get_name returns an error") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             sp.get_name(); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             check_null_sp_error(error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("serial port parameter validation and corner cases") | ||||
| { | ||||
|     SECTION("libusbp_serial_port_create") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_serial_port_create( | ||||
|                         NULL, 0, true, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Serial port output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output to NULL if there is an error") | ||||
|         { | ||||
|             libusbp_serial_port * ptr = (libusbp_serial_port *)-1; | ||||
|             libusbp::error error(libusbp_serial_port_create(NULL, 0, true, &ptr)); | ||||
|             REQUIRE((ptr == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_serial_port_copy") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_serial_port_copy(NULL, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "Serial port output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output pointer to NULL when copying NULL") | ||||
|         { | ||||
|             libusbp_serial_port * sp = (libusbp_serial_port *)-1; | ||||
|             libusbp::throw_if_needed(libusbp_serial_port_copy(NULL, &sp)); | ||||
|             REQUIRE((sp == NULL)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("libusbp_serial_port_get_name") | ||||
|     { | ||||
|         SECTION("complains if the output pointer is NULL") | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 libusbp::throw_if_needed(libusbp_serial_port_get_name(NULL, NULL)); | ||||
|                 REQUIRE(0); | ||||
|             } | ||||
|             catch(const libusbp::error & error) | ||||
|             { | ||||
|                 REQUIRE(error.message() == "String output pointer is null."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SECTION("sets the output string to NULL if it can't return anything") | ||||
|         { | ||||
|             char * s = (char *)-1; | ||||
|             libusbp::error error(libusbp_serial_port_get_name(NULL, &s)); | ||||
|             REQUIRE((s == NULL)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef USE_TEST_DEVICE_A | ||||
| TEST_CASE("test device A serial port") | ||||
| { | ||||
|     libusbp::device device = find_test_device_a(); | ||||
|  | ||||
|     SECTION("interface 2") | ||||
|     { | ||||
|         libusbp::serial_port sp(device, 2, true); | ||||
|  | ||||
|         SECTION("has a name that looks good") | ||||
|         { | ||||
|             std::string name = sp.get_name(); | ||||
|  | ||||
|             #if defined(_WIN32) | ||||
|             REQUIRE(name.substr(0, 3) == "COM"); | ||||
|             REQUIRE(name.size() >= 4); | ||||
|             #elif defined(__linux__) | ||||
|             REQUIRE(name.substr(0, 11) == "/dev/ttyACM"); | ||||
|             REQUIRE(name.size() >= 11); | ||||
|             #elif defined(__APPLE__) | ||||
|             REQUIRE(name.substr(0, 16) == "/dev/cu.usbmodem"); | ||||
|             REQUIRE(name.size() >= 17); | ||||
|             #else | ||||
|             REQUIRE(0); | ||||
|             #endif | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     SECTION("incorrect parameters interface=0 and composite=false") | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             libusbp::serial_port sp(device, 0, true); | ||||
|             REQUIRE(0); | ||||
|         } | ||||
|         catch(const libusbp::error & error) | ||||
|         { | ||||
|             #if defined(_WIN32) | ||||
|             REQUIRE(error.message() == "The PortName key was not found."); | ||||
|             #elif defined(__linux__) | ||||
|             REQUIRE(error.message() == "Could not find tty device."); | ||||
|             #elif defined(__APPLE__) | ||||
|             REQUIRE(error.message() == "Could not find entry with class IOSerialBSDClient."); | ||||
|             #else | ||||
|             REQUIRE(error.message() == "?"); | ||||
|             #endif | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     SECTION("serial ports can be copied") | ||||
|     { | ||||
|         libusbp::serial_port sp(device, 2, true); | ||||
|         libusbp::serial_port sp2 = sp; | ||||
|         REQUIRE(sp2.get_name() == sp.get_name()); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user