├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── config.json ├── device-libusb.cpp ├── device-libusb.h ├── host-raw-gadget.cpp ├── host-raw-gadget.h ├── injection.json ├── misc.cpp ├── misc.h ├── proxy.cpp ├── proxy.h └── usb-proxy.cpp /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = tab 8 | indent_size = 8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | indent_style = space 14 | indent_size = 4 15 | trim_trailing_whitespace = false 16 | insert_final_newline = false 17 | 18 | # C coding style based on Linux kernel. 19 | # https://www.kernel.org/doc/html/v5.0/process/coding-style.html 20 | 21 | [*.sh] 22 | indent_style = tab 23 | indent_size = 4 24 | 25 | [*.c] 26 | indent_style = tab 27 | indent_size = 8 28 | 29 | [*.h] 30 | indent_style = tab 31 | indent_size = 8 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | usb-proxy 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LDFLAG=-lusb-1.0 -pthread -ljsoncpp 2 | 3 | ifndef CFLAGS 4 | ifeq ($(TARGET),Debug) 5 | CFLAGS=-Wall -Wextra -g 6 | else 7 | CFLAGS=-Wall -Wextra -O2 8 | endif 9 | endif 10 | 11 | .PHONY: all clean 12 | 13 | usb-proxy: usb-proxy.o host-raw-gadget.o device-libusb.o proxy.o misc.o 14 | g++ usb-proxy.o host-raw-gadget.o device-libusb.o proxy.o misc.o $(LDFLAG) -o usb-proxy 15 | 16 | %.o: %.cpp %.h 17 | g++ $(CFLAGS) -c $< 18 | 19 | %.o: %.cpp 20 | g++ $(CFLAGS) -c $< 21 | 22 | clean: 23 | -rm *.o 24 | -rm usb-proxy 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # usb-proxy 2 | 3 | This software is a USB proxy based on [raw-gadget](https://github.com/xairy/raw-gadget) and libusb. It is recommended to run this repo on a computer that has an USB OTG port, such as `Raspberry Pi 4` or other [hardware](https://github.com/xairy/raw-gadget/tree/master/tests#results) that can work with `raw-gadget`, otherwise might need to use `dummy_hcd` kernel module to set up virtual USB Device and Host controller that connected to each other inside the kernel. 4 | 5 | ``` 6 | ------------ ----------------------------------------------- ----------------------- 7 | | | | | | | 8 | | | |------------- -----------| |------------- | 9 | | USB <-----> USB | Host COMPUTER | USB <-----> USB | USB | 10 | | device | | host port | running usb-proxy | OTG port | | host port | Host | 11 | | | |------------- with raw-gadget -----------| |------------- | 12 | | | | | | | 13 | ------------ ----------------------------------------------- ----------------------- 14 | ``` 15 | 16 | ``` 17 | ------------ ------------------------------------ 18 | | | | | 19 | | | |------------- Host COMPUTER | 20 | | USB <-----> USB | running usb-proxy | 21 | | device | | host port | with raw-gadget | 22 | | | |------------- and dummy_hcd | 23 | | | | | 24 | ------------ ------------------------------------ 25 | ``` 26 | 27 | --- 28 | 29 | ## How to use 30 | 31 | ### Step 1: Prerequisite 32 | 33 | Please clone the [raw-gadget](https://github.com/xairy/raw-gadget), and compile the kernel modules(if you need `dummy_hcd` as well, please compile it, otherwise only need to compile `raw-gadget`) in the repo, then load `raw-gadget` kernel module, you will be able to access `/dev/raw-gadget` afterward. 34 | 35 | Install the package 36 | ```shell 37 | sudo apt install libusb-1.0-0-dev libjsoncpp-dev 38 | ``` 39 | 40 | ### Step 2: Check device and driver name 41 | 42 | Please check the name of `device` and `driver` on your hardware with the following command. If you are going to use `dummy_hcd`, then this step can be skipped, because `usb-proxy` will use `dummy_hcd` by default. 43 | 44 | ```shell 45 | # For device name 46 | $ ls /sys/class/udc/ 47 | fe980000.usb 48 | ``` 49 | 50 | ```shell 51 | # For driver name 52 | $ cat /sys/class/udc/fe980000.usb/uevent 53 | USB_UDC_NAME=fe980000.usb 54 | ``` 55 | 56 | Note: If you are not able to see the above on your `Raspberry Pi 4`, probably you didn't enable the `dwc2` kernel module, please execute the following command and try again after reboot. 57 | 58 | ```shell 59 | $ echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt 60 | $ echo "dwc2" | sudo tee -a /etc/modules 61 | $ sudo reboot 62 | ``` 63 | 64 | ### Step 3: Check vendor_id and product_id of USB device 65 | 66 | Please plug the USB device that you want to test into `Raspberry Pi 4`, then execute `lsusb` on terminal. 67 | 68 | ```shell 69 | $ lsusb 70 | Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 71 | Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 72 | Bus 001 Device 003: ID 1b3f:2247 Generalplus Technology Inc. GENERAL WEBCAM 73 | Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub 74 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 75 | ``` 76 | 77 | As you can see, There is a `Bus 001 Device 003: ID 1b3f:2247 Generalplus Technology Inc. GENERAL WEBCAM`, and `1b3f:2247` is the `vendor_id` and `product_id` with a colon between them. 78 | 79 | ### Step 4: Run 80 | 81 | ``` 82 | Usage: 83 | -h/--help: print this help message 84 | -v/--verbose: increase verbosity 85 | --device: use specific device 86 | --driver: use specific driver 87 | --vendor_id: use specific vendor_id(HEX) of USB device 88 | --product_id: use specific product_id(HEX) of USB device 89 | --enable_injection: enable the injection feature 90 | --injection_file: specify the file that contains injection rules 91 | ``` 92 | - If `device` not specified, `usb-proxy` will use `dummy_udc.0` as default device. 93 | - If `driver` not specified, `usb-proxy` will use `dummy_udc` as default driver. 94 | - If both `vendor_id` and `product_id` not specified, `usb-proxy` will connect the first USB device it can find. 95 | 96 | For example: 97 | ```shell 98 | $ ./usb-proxy --device=fe980000.usb --driver=fe980000.usb --vendor_id=1b3f --product_id=2247 99 | ``` 100 | 101 | Please replace `fe980000.usb` with the `device` that you have when running this software, and then replace the `driver` variable with the string after `USB_UDC_NAME=` in step 2. Please also modify the `vendor_id` and `product_id` variable that you have checked in step 3. 102 | 103 | --- 104 | 105 | ## How to do MITM attack with this project 106 | 107 | This feature is still very simple. Ideas or suggestions are very welcome. 108 | 109 | ### Step 1: Create rules 110 | 111 | Please edit the `injection.json` for the injection rules. The following is the default template. 112 | 113 | Note: The comment in the following template is only for explaining the meaning, please do not copy the comment, it is invalid in json. 114 | 115 | ```json 116 | { 117 | "control": { 118 | "modify": [ // For modify the control transfer data 119 | { 120 | "enable": false, // Enable this rule or not 121 | "bRequestType": 0, // Hex value 122 | "bRequest": 0, // Hex value 123 | "wValue": 0, // Hex value 124 | "wIndex": 0, // Hex value 125 | "wLength": 0, // Hex value 126 | "content_pattern": [], // If USB packet contains any data that match any patterns, the matched data will be replaced with the value in "replacement". Format is Hex string, for example: \\x01\\x00\\x00\\x00 127 | "replacement": "" // The content after modified. Format is Hex string, for example: \\x02\\x00\\x00\\x00 128 | } 129 | ], 130 | "ignore": [ // For ignoring control transfer packet, it won't be sent to Host/Device if match the rule 131 | { 132 | "enable": false, 133 | "bRequestType": 0, 134 | "bRequest": 0, 135 | "wValue": 0, 136 | "wIndex": 0, 137 | "wLength": 0, 138 | "content_pattern": [] 139 | } 140 | ], 141 | "stall": [ // For stalling Host if match the rule 142 | { 143 | "enable": false, 144 | "bRequestType": 0, 145 | "bRequest": 0, 146 | "wValue": 0, 147 | "wIndex": 0, 148 | "wLength": 0, 149 | "content_pattern": [] 150 | } 151 | ] 152 | }, 153 | "int": [ 154 | { 155 | "ep_address": 81, // Endpoint address in Hex 156 | "enable": false, 157 | "content_pattern": [], 158 | "replacement": "" 159 | } 160 | ], 161 | "bulk": [ 162 | { 163 | "ep_address": 81, 164 | "enable": false, 165 | "content_pattern": [], 166 | "replacement": "" 167 | } 168 | ], 169 | "isoc": [] // This transfer type is not supported yet 170 | } 171 | ``` 172 | 173 | For example, the following rules work with my USB mouse, and convert left click to right click, and convert right click to left click. 174 | ```json 175 | { 176 | "control": { 177 | "modify": [], 178 | "ignore": [], 179 | "stall": [] 180 | }, 181 | "int": [ 182 | { 183 | "ep_address": 81, 184 | "enable": true, 185 | "content_pattern": ["\\x01\\x00\\x00\\x00"], 186 | "replacement": "\\x02\\x00\\x00\\x00" 187 | }, 188 | { 189 | "ep_address": 81, 190 | "enable": true, 191 | "content_pattern": ["\\x02\\x00\\x00\\x00"], 192 | "replacement": "\\x01\\x00\\x00\\x00" 193 | } 194 | ], 195 | "bulk": [], 196 | "isoc": [] 197 | } 198 | ``` 199 | 200 | ### Step 2: Run 201 | 202 | Use the `--enable_injection` to enable this feature, and use `--injection_file` to specify the file path of your customized injection rules, if it is not specified, `usb-proxy` will use `injection.json` by default. 203 | 204 | For example 205 | ``` 206 | $ ./usb-proxy --device=fe980000.usb --driver=fe980000.usb --enable_injection --injection_file=myInjectionRules.json 207 | ``` 208 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "reset_device_before_proxy": true, 3 | "bmaxpacketsize0_must_greater_than_64": true 4 | } 5 | -------------------------------------------------------------------------------- /device-libusb.cpp: -------------------------------------------------------------------------------- 1 | #include "device-libusb.h" 2 | 3 | libusb_device **devs; 4 | libusb_device_handle *dev_handle; 5 | libusb_context *context = NULL; 6 | libusb_hotplug_callback_handle callback_handle = -1; 7 | 8 | struct libusb_device_descriptor device_device_desc; 9 | struct libusb_config_descriptor **device_config_desc; 10 | 11 | pthread_t hotplug_monitor_thread; 12 | 13 | int hotplug_callback(struct libusb_context *ctx __attribute__((unused)), 14 | struct libusb_device *dev __attribute__((unused)), 15 | libusb_hotplug_event envet __attribute__((unused)), 16 | void *user_data __attribute__((unused))) { 17 | printf("Hotplug event: device disconnected, stopping proxy...\n"); 18 | kill(0, SIGINT); 19 | return 0; 20 | } 21 | 22 | void *hotplug_monitor(void *arg __attribute__((unused))) { 23 | printf("Start hotplug_monitor thread, thread id(%d)\n", gettid()); 24 | while(true) { 25 | usleep(100 * 1000); 26 | libusb_handle_events_completed(NULL, NULL); 27 | } 28 | } 29 | 30 | int get_descriptor(libusb_device *device) { 31 | int result; 32 | result = libusb_get_device_descriptor(device, &device_device_desc); 33 | if (result != LIBUSB_SUCCESS) { 34 | if (verbose_level) { 35 | fprintf(stderr, "Error retrieving device descriptor: %s\n", 36 | libusb_strerror((libusb_error)result)); 37 | } 38 | return result; 39 | } 40 | 41 | device_config_desc = new struct libusb_config_descriptor *[device_device_desc.bNumConfigurations]; 42 | for (int i = 0; i < device_device_desc.bNumConfigurations; i++) { 43 | result = libusb_get_config_descriptor(device, i, &device_config_desc[i]); 44 | if (result != LIBUSB_SUCCESS) { 45 | if (verbose_level) { 46 | fprintf(stderr, "Error retrieving configuration(%d) descriptor: %s\n", 47 | i, libusb_strerror((libusb_error)result)); 48 | } 49 | return result; 50 | } 51 | } 52 | 53 | return LIBUSB_SUCCESS; 54 | } 55 | 56 | int connect_device(int vendor_id, int product_id) { 57 | int result; 58 | result = libusb_init(&context); 59 | if (result < 0) { 60 | fprintf(stderr, "Init error: %s\n", libusb_strerror((libusb_error)result)); 61 | return 1; 62 | } 63 | libusb_set_debug(context, 3); 64 | 65 | libusb_device **list = NULL; 66 | libusb_device *found = NULL; 67 | 68 | int cnt = libusb_get_device_list(context, &list); 69 | if (cnt < 0) { 70 | if (verbose_level) { 71 | fprintf(stderr, "Error retrieving device list: %s\n", 72 | libusb_strerror((libusb_error)cnt)); 73 | } 74 | return cnt; 75 | } 76 | 77 | while (found == NULL) { 78 | cnt = libusb_get_device_list(context, &devs); 79 | if (cnt < 0) { 80 | fprintf(stderr, "Get Device Error: %s\n", 81 | libusb_strerror((libusb_error)cnt)); 82 | return 1; 83 | } 84 | if (verbose_level) 85 | printf("%d Devices in list\n", cnt); 86 | 87 | for (int i = 0; i < cnt; i++) { 88 | libusb_device *dvc = devs[i]; 89 | result = get_descriptor(dvc); 90 | if (result != LIBUSB_SUCCESS) 91 | continue; 92 | 93 | if (device_device_desc.bDeviceClass == LIBUSB_CLASS_HUB) 94 | continue; 95 | 96 | if (vendor_id == -1 && product_id == -1) { 97 | found = dvc; 98 | break; 99 | } 100 | else if ((vendor_id == device_device_desc.idVendor || vendor_id == LIBUSB_HOTPLUG_MATCH_ANY) && 101 | (product_id == device_device_desc.idProduct || product_id == LIBUSB_HOTPLUG_MATCH_ANY)) { 102 | found = dvc; 103 | break; 104 | } 105 | } 106 | 107 | if (verbose_level && vendor_id != -1 && product_id != -1) 108 | printf("Target device not found\n"); 109 | libusb_free_device_list(devs, 1); 110 | sleep(1); 111 | } 112 | 113 | result = libusb_open(found, &dev_handle); 114 | if (result != LIBUSB_SUCCESS) { 115 | if (verbose_level) { 116 | fprintf(stderr, "Error opening device handle: %s\n", 117 | libusb_strerror((libusb_error)result)); 118 | } 119 | dev_handle = NULL; 120 | libusb_free_device_list(list, 1); 121 | return result; 122 | } 123 | 124 | result = libusb_set_auto_detach_kernel_driver(dev_handle, 0); 125 | if (result != LIBUSB_SUCCESS) { 126 | fprintf(stderr, "libusb_set_auto_detach_kernel_driver() failed: %s\n", 127 | libusb_strerror((libusb_error)result)); 128 | return result; 129 | } 130 | 131 | int config = 0; 132 | result = libusb_get_configuration(dev_handle, &config); 133 | if (result != LIBUSB_SUCCESS) { 134 | fprintf(stderr, "libusb_get_configuration() failed: %s\n", 135 | libusb_strerror((libusb_error)result)); 136 | return result; 137 | } 138 | 139 | for (int i = 0; i < device_device_desc.bNumConfigurations; i++) { 140 | if (device_config_desc[i]->bConfigurationValue != config) 141 | continue; 142 | for (int j = 0; j < device_config_desc[i]->bNumInterfaces; j++) 143 | libusb_detach_kernel_driver(dev_handle, j); 144 | } 145 | 146 | if (reset_device_before_proxy) { 147 | result = libusb_reset_device(dev_handle); 148 | if (result != LIBUSB_SUCCESS) { 149 | fprintf(stderr, "libusb_reset_device() failed: %s\n", 150 | libusb_strerror((libusb_error)result)); 151 | return result; 152 | } 153 | } 154 | 155 | //check that device is responsive 156 | unsigned char unused[4]; 157 | result = libusb_get_string_descriptor(dev_handle, 0, 0, unused, sizeof(unused)); 158 | if (result < 0) { 159 | fprintf(stderr, "Device unresponsive: %s\n", 160 | libusb_strerror((libusb_error)result)); 161 | return result; 162 | } 163 | 164 | if (callback_handle == -1) { 165 | result = libusb_hotplug_register_callback(context, 166 | (libusb_hotplug_event) (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), 167 | (libusb_hotplug_flag) 0, vendor_id, product_id, 168 | LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, &callback_handle); 169 | 170 | if (result != LIBUSB_SUCCESS) { 171 | fprintf(stderr, "Error registering callback\n"); 172 | libusb_exit(context); 173 | return result; 174 | } 175 | pthread_create(&hotplug_monitor_thread, 0, 176 | hotplug_monitor, nullptr); 177 | } 178 | 179 | return 0; 180 | } 181 | 182 | void reset_device() { 183 | int result = libusb_reset_device(dev_handle); 184 | if (result != LIBUSB_SUCCESS) { 185 | fprintf(stderr, "Error resetting device: %s\n", 186 | libusb_strerror((libusb_error)result)); 187 | } 188 | } 189 | 190 | void set_configuration(int configuration) { 191 | int result = libusb_set_configuration(dev_handle, configuration); 192 | if (result != LIBUSB_SUCCESS) { 193 | fprintf(stderr, "Error setting configuration(%d): %s\n", 194 | configuration, libusb_strerror((libusb_error)result)); 195 | } 196 | } 197 | 198 | void claim_interface(int interface) { 199 | int result = libusb_claim_interface(dev_handle, interface); 200 | if (result != LIBUSB_SUCCESS) { 201 | fprintf(stderr, "Error claiming interface(%d): %s\n", 202 | interface, libusb_strerror((libusb_error)result)); 203 | } 204 | } 205 | 206 | void release_interface(int interface) { 207 | int result = libusb_release_interface(dev_handle, interface); 208 | if (result != LIBUSB_SUCCESS && result != LIBUSB_ERROR_NOT_FOUND) { 209 | fprintf(stderr, "Error releasing interface(%d): %s\n", 210 | interface, libusb_strerror((libusb_error)result)); 211 | } 212 | } 213 | 214 | void set_interface_alt_setting(int interface, int altsetting) { 215 | int result = libusb_set_interface_alt_setting(dev_handle, interface, altsetting); 216 | if (result != LIBUSB_SUCCESS) { 217 | fprintf(stderr, "Error setting interface altsetting(%d, %d): %s\n", 218 | interface, altsetting, libusb_strerror((libusb_error)result)); 219 | } 220 | } 221 | 222 | int control_request(const usb_ctrlrequest *setup_packet, int *nbytes, 223 | unsigned char **dataptr, int timeout) { 224 | int result = libusb_control_transfer(dev_handle, 225 | setup_packet->bRequestType, setup_packet->bRequest, 226 | setup_packet->wValue, setup_packet->wIndex, *dataptr, 227 | setup_packet->wLength, timeout); 228 | 229 | if (result < 0) { 230 | if (verbose_level) { 231 | fprintf(stderr, "Error sending setup packet: %s\n", 232 | libusb_strerror((libusb_error)result)); 233 | } 234 | if (result == LIBUSB_ERROR_PIPE) 235 | return -1; 236 | return result; 237 | } 238 | else { 239 | if (verbose_level) 240 | printf("Control transfer succeed\n"); 241 | } 242 | 243 | *nbytes = result; 244 | return 0; 245 | } 246 | 247 | int send_data(uint8_t endpoint, uint8_t attributes, uint8_t *dataptr, 248 | int length, int timeout) { 249 | int transferred; 250 | int attempt = 0; 251 | int result = LIBUSB_SUCCESS; 252 | 253 | bool incomplete_transfer = false; 254 | 255 | switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { 256 | case USB_ENDPOINT_XFER_CONTROL: 257 | fprintf(stderr, "Can't send on a control endpoint.\n"); 258 | break; 259 | case USB_ENDPOINT_XFER_ISOC: 260 | if (verbose_level) 261 | fprintf(stderr, "Isochronous(write) endpoint EP%02x unhandled.\n", endpoint); 262 | break; 263 | case USB_ENDPOINT_XFER_BULK: 264 | do { 265 | result = libusb_bulk_transfer(dev_handle, endpoint, dataptr, length, &transferred, timeout); 266 | //TODO retry transfer if incomplete 267 | if (transferred != length) { 268 | fprintf(stderr, "Incomplete Bulk transfer on EP%02x for attempt %d. length(%d), transferred(%d)\n", 269 | endpoint, attempt, length, transferred); 270 | incomplete_transfer = true; 271 | } 272 | if (result == LIBUSB_SUCCESS) { 273 | if (incomplete_transfer) 274 | printf("Resent Bulk transfer on EP%02x for attempt %d. length(%d), transferred(%d)\n", 275 | endpoint, attempt, length, transferred); 276 | if (verbose_level > 2) 277 | printf("Sent %d bytes (Bulk) to EP%02x\n", transferred, endpoint); 278 | } 279 | if ((result == LIBUSB_ERROR_PIPE || result == LIBUSB_ERROR_TIMEOUT)) 280 | libusb_clear_halt(dev_handle, endpoint); 281 | 282 | attempt++; 283 | } while ((result == LIBUSB_ERROR_PIPE || result == LIBUSB_ERROR_TIMEOUT || transferred != length) 284 | && attempt < MAX_ATTEMPTS); 285 | break; 286 | case USB_ENDPOINT_XFER_INT: 287 | result = libusb_interrupt_transfer(dev_handle, endpoint, dataptr, length, &transferred, timeout); 288 | 289 | if (transferred != length) 290 | fprintf(stderr, "Incomplete Interrupt transfer on EP%02x\n", endpoint); 291 | if (result == LIBUSB_SUCCESS && verbose_level > 2) 292 | printf("Sent %d bytes (Int) to libusb EP%02x\n", transferred, endpoint); 293 | break; 294 | } 295 | if (result != LIBUSB_SUCCESS) { 296 | fprintf(stderr, "Transfer error sending on EP%02x: %s\n", 297 | endpoint, libusb_strerror((libusb_error)result)); 298 | } 299 | return result; 300 | } 301 | 302 | void iso_transfer_callback(struct libusb_transfer *transfer) { 303 | int *iso_completed = (int *)transfer->user_data; 304 | *iso_completed = 1; 305 | } 306 | 307 | int receive_data(uint8_t endpoint, uint8_t attributes, uint16_t maxPacketSize, 308 | uint8_t **dataptr, int *length, int timeout) { 309 | int result = LIBUSB_SUCCESS; 310 | struct libusb_transfer *transfer; 311 | int iso_completed, iso_packets; 312 | 313 | int attempt = 0; 314 | switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { 315 | case USB_ENDPOINT_XFER_CONTROL: 316 | fprintf(stderr, "Can't read on a control endpoint.\n"); 317 | break; 318 | case USB_ENDPOINT_XFER_ISOC: 319 | *dataptr = new uint8_t[maxPacketSize]; 320 | // We could retrieve multiple packets at a time, but then we 321 | // would need to split the received data submit each packet 322 | // separately via Raw Gadget. So retrieve only one packet for 323 | // simplicity. 324 | iso_packets = 1; 325 | transfer = libusb_alloc_transfer(iso_packets); 326 | if (!transfer) { 327 | fprintf(stderr, "Failed to allocate libusb_transfer.\n"); 328 | result = LIBUSB_ERROR_OTHER; 329 | } 330 | iso_completed = 0; 331 | libusb_fill_iso_transfer(transfer, dev_handle, endpoint, *dataptr, maxPacketSize, 332 | iso_packets, iso_transfer_callback, &iso_completed, timeout); 333 | libusb_set_iso_packet_lengths(transfer, maxPacketSize / iso_packets); 334 | result = libusb_submit_transfer(transfer); 335 | if (result != LIBUSB_SUCCESS) { 336 | libusb_free_transfer(transfer); 337 | break; 338 | } 339 | while (!iso_completed) 340 | libusb_handle_events_completed(NULL, &iso_completed); 341 | *length = 0; 342 | for (int i = 0; i < iso_packets; i++) 343 | *length += transfer->iso_packet_desc[i].actual_length; 344 | if (result == LIBUSB_SUCCESS && verbose_level > 2) 345 | printf("Received iso data(%d) bytes\n", *length); 346 | libusb_free_transfer(transfer); 347 | break; 348 | case USB_ENDPOINT_XFER_BULK: 349 | *dataptr = new uint8_t[maxPacketSize * 8]; 350 | do { 351 | result = libusb_bulk_transfer(dev_handle, endpoint, *dataptr, maxPacketSize, length, timeout); 352 | if (result == LIBUSB_SUCCESS && verbose_level > 2) 353 | printf("Received bulk data(%d) bytes\n", *length); 354 | if ((result == LIBUSB_ERROR_PIPE || result == LIBUSB_ERROR_TIMEOUT)) 355 | libusb_clear_halt(dev_handle, endpoint); 356 | 357 | attempt++; 358 | } while ((result == LIBUSB_ERROR_PIPE || result == LIBUSB_ERROR_TIMEOUT) && attempt < MAX_ATTEMPTS); 359 | break; 360 | case USB_ENDPOINT_XFER_INT: 361 | *dataptr = new uint8_t[maxPacketSize]; 362 | result = libusb_interrupt_transfer(dev_handle, endpoint, *dataptr, maxPacketSize, length, timeout); 363 | if (result == LIBUSB_SUCCESS && verbose_level > 2) 364 | printf("Received int data(%d) bytes\n", *length); 365 | break; 366 | } 367 | 368 | if (result != LIBUSB_SUCCESS) { 369 | fprintf(stderr, "Transfer error receiving on EP%02x: %s\n", 370 | endpoint, libusb_strerror((libusb_error)result)); 371 | } 372 | 373 | return result; 374 | } 375 | -------------------------------------------------------------------------------- /device-libusb.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "misc.h" 4 | 5 | #define USB_REQUEST_TIMEOUT 1000 6 | 7 | #define MAX_ATTEMPTS 5 8 | 9 | extern libusb_device **devs; 10 | extern libusb_device_handle *dev_handle; 11 | extern libusb_context *context; 12 | extern libusb_hotplug_callback_handle callback_handle; 13 | 14 | extern struct libusb_device_descriptor device_device_desc; 15 | extern struct libusb_config_descriptor **device_config_desc; 16 | 17 | extern pthread_t hotplug_monitor_thread; 18 | 19 | int connect_device(int vendorId, int productId); 20 | void reset_device(); 21 | void set_configuration(int configuration); 22 | void claim_interface(int interface); 23 | void release_interface(int interface); 24 | void set_interface_alt_setting(int interface, int altsetting); 25 | int control_request(const usb_ctrlrequest *setup_packet, int *nbytes, 26 | unsigned char **dataptr, int timeout); 27 | int send_data(uint8_t endpoint, uint8_t attributes, uint8_t *dataptr, 28 | int length, int timeout); 29 | int receive_data(uint8_t endpoint, uint8_t attributes, uint16_t maxPacketSize, 30 | uint8_t **dataptr, int *length, int timeout); 31 | -------------------------------------------------------------------------------- /host-raw-gadget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "host-raw-gadget.h" 18 | 19 | struct raw_gadget_device host_device_desc; 20 | 21 | /*----------------------------------------------------------------------*/ 22 | 23 | int usb_raw_open() { 24 | int fd = open("/dev/raw-gadget", O_RDWR); 25 | if (fd < 0) { 26 | perror("open() /dev/raw-gadget"); 27 | exit(EXIT_FAILURE); 28 | } 29 | return fd; 30 | } 31 | 32 | void usb_raw_init(int fd, enum usb_device_speed speed, 33 | const char *driver, const char *device) { 34 | struct usb_raw_init arg; 35 | strcpy((char *)&arg.driver_name[0], driver); 36 | strcpy((char *)&arg.device_name[0], device); 37 | arg.speed = speed; 38 | int rv = ioctl(fd, USB_RAW_IOCTL_INIT, &arg); 39 | if (rv < 0) { 40 | perror("ioctl(USB_RAW_IOCTL_INIT)"); 41 | exit(EXIT_FAILURE); 42 | } 43 | } 44 | 45 | void usb_raw_run(int fd) { 46 | int rv = ioctl(fd, USB_RAW_IOCTL_RUN, 0); 47 | if (rv < 0) { 48 | perror("ioctl(USB_RAW_IOCTL_RUN)"); 49 | exit(EXIT_FAILURE); 50 | } 51 | } 52 | 53 | void usb_raw_event_fetch(int fd, struct usb_raw_event *event) { 54 | int rv = ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event); 55 | if (rv < 0) { 56 | if (errno == EINTR) { 57 | event->length = 4294967295; 58 | return; 59 | } 60 | perror("ioctl(USB_RAW_IOCTL_EVENT_FETCH)"); 61 | exit(EXIT_FAILURE); 62 | } 63 | } 64 | 65 | int usb_raw_ep0_read(int fd, struct usb_raw_ep_io *io) { 66 | int rv = ioctl(fd, USB_RAW_IOCTL_EP0_READ, io); 67 | if (rv < 0) { 68 | if (errno == EBUSY) 69 | return rv; 70 | perror("ioctl(USB_RAW_IOCTL_EP0_READ)"); 71 | exit(EXIT_FAILURE); 72 | } 73 | return rv; 74 | } 75 | 76 | int usb_raw_ep0_write(int fd, struct usb_raw_ep_io *io) { 77 | int rv = ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io); 78 | if (rv < 0) { 79 | perror("ioctl(USB_RAW_IOCTL_EP0_WRITE)"); 80 | exit(EXIT_FAILURE); 81 | } 82 | return rv; 83 | } 84 | 85 | int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor *desc) { 86 | int rv = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc); 87 | if (rv < 0) { 88 | perror("ioctl(USB_RAW_IOCTL_EP_ENABLE)"); 89 | exit(EXIT_FAILURE); 90 | } 91 | return rv; 92 | } 93 | 94 | int usb_raw_ep_disable(int fd, uint32_t num) { 95 | int rv = ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, num); 96 | if (rv < 0) { 97 | perror("ioctl(USB_RAW_IOCTL_EP_DISABLE)"); 98 | exit(EXIT_FAILURE); 99 | } 100 | return rv; 101 | } 102 | 103 | int usb_raw_ep_read(int fd, struct usb_raw_ep_io *io) { 104 | int rv = ioctl(fd, USB_RAW_IOCTL_EP_READ, io); 105 | if (rv < 0) { 106 | if (errno == EINPROGRESS) { 107 | // Ignore failures caused by the test that halts endpoints. 108 | return rv; 109 | } 110 | else if (errno == ESHUTDOWN) { 111 | // Ignore failures caused by device reset. 112 | return rv; 113 | } 114 | else if (errno == EINTR) { 115 | // Ignore failures caused by sending a signal to the 116 | // endpoint threads when shutting them down. 117 | return rv; 118 | } 119 | else if (errno == EBUSY) 120 | return rv; 121 | perror("ioctl(USB_RAW_IOCTL_EP_READ)"); 122 | exit(EXIT_FAILURE); 123 | } 124 | return rv; 125 | } 126 | 127 | int usb_raw_ep_write(int fd, struct usb_raw_ep_io *io) { 128 | int rv = ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io); 129 | if (rv < 0) { 130 | if (errno == EINPROGRESS) { 131 | // Ignore failures caused by the test that halts endpoints. 132 | return rv; 133 | } 134 | else if (errno == ESHUTDOWN) { 135 | // Ignore failures caused by device reset. 136 | return rv; 137 | } 138 | else if (errno == EINTR) { 139 | // Ignore failures caused by sending a signal to the 140 | // endpoint threads when shutting them down. 141 | return rv; 142 | } 143 | else if (errno == EXDEV || errno == ENODATA) { 144 | // Ignore failures caused by sending an isochronous transfer 145 | // too late (dwc3 returns EXDEV, dwc2 returns ENODATA). 146 | return rv; 147 | } 148 | else if (errno == EBUSY) 149 | return rv; 150 | perror("ioctl(USB_RAW_IOCTL_EP_WRITE)"); 151 | exit(EXIT_FAILURE); 152 | } 153 | return rv; 154 | } 155 | 156 | void usb_raw_configure(int fd) { 157 | int rv = ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0); 158 | if (rv < 0) { 159 | perror("ioctl(USB_RAW_IOCTL_CONFIGURED)"); 160 | exit(EXIT_FAILURE); 161 | } 162 | } 163 | 164 | void usb_raw_vbus_draw(int fd, uint32_t power) { 165 | int rv = ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power); 166 | if (rv < 0) { 167 | perror("ioctl(USB_RAW_IOCTL_VBUS_DRAW)"); 168 | exit(EXIT_FAILURE); 169 | } 170 | } 171 | 172 | int usb_raw_eps_info(int fd, struct usb_raw_eps_info *info) { 173 | int rv = ioctl(fd, USB_RAW_IOCTL_EPS_INFO, info); 174 | if (rv < 0) { 175 | perror("ioctl(USB_RAW_IOCTL_EPS_INFO)"); 176 | exit(EXIT_FAILURE); 177 | } 178 | return rv; 179 | } 180 | 181 | void usb_raw_ep0_stall(int fd) { 182 | printf("ep0: stalling\n"); 183 | int rv = ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0); 184 | if (rv < 0) { 185 | if (errno == EBUSY) 186 | return; 187 | perror("ioctl(USB_RAW_IOCTL_EP0_STALL)"); 188 | exit(EXIT_FAILURE); 189 | } 190 | } 191 | 192 | void usb_raw_ep_set_halt(int fd, int ep) { 193 | int rv = ioctl(fd, USB_RAW_IOCTL_EP_SET_HALT, ep); 194 | if (rv < 0) { 195 | perror("ioctl(USB_RAW_IOCTL_EP_SET_HALT)"); 196 | exit(EXIT_FAILURE); 197 | } 198 | } 199 | 200 | /*----------------------------------------------------------------------*/ 201 | 202 | void log_control_request(struct usb_ctrlrequest *ctrl) { 203 | printf(" bRequestType: 0x%02x %5s, bRequest: 0x%02x, wValue: 0x%04x," 204 | " wIndex: 0x%04x, wLength: %d\n", ctrl->bRequestType, 205 | (ctrl->bRequestType & USB_DIR_IN) ? "(IN)" : "(OUT)", 206 | ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); 207 | 208 | switch (ctrl->bRequestType & USB_TYPE_MASK) { 209 | case USB_TYPE_STANDARD: 210 | printf(" type = USB_TYPE_STANDARD\n"); 211 | break; 212 | case USB_TYPE_CLASS: 213 | printf(" type = USB_TYPE_CLASS\n"); 214 | break; 215 | case USB_TYPE_VENDOR: 216 | printf(" type = USB_TYPE_VENDOR\n"); 217 | break; 218 | default: 219 | printf(" type = unknown = %d\n", (int)ctrl->bRequestType); 220 | break; 221 | } 222 | 223 | switch (ctrl->bRequestType & USB_TYPE_MASK) { 224 | case USB_TYPE_STANDARD: 225 | switch (ctrl->bRequest) { 226 | case USB_REQ_GET_DESCRIPTOR: 227 | printf(" req = USB_REQ_GET_DESCRIPTOR\n"); 228 | switch (ctrl->wValue >> 8) { 229 | case USB_DT_DEVICE: 230 | printf(" desc = USB_DT_DEVICE\n"); 231 | break; 232 | case USB_DT_CONFIG: 233 | printf(" desc = USB_DT_CONFIG\n"); 234 | break; 235 | case USB_DT_STRING: 236 | printf(" desc = USB_DT_STRING\n"); 237 | break; 238 | case USB_DT_INTERFACE: 239 | printf(" desc = USB_DT_INTERFACE\n"); 240 | break; 241 | case USB_DT_ENDPOINT: 242 | printf(" desc = USB_DT_ENDPOINT\n"); 243 | break; 244 | case USB_DT_DEVICE_QUALIFIER: 245 | printf(" desc = USB_DT_DEVICE_QUALIFIER\n"); 246 | break; 247 | case USB_DT_OTHER_SPEED_CONFIG: 248 | printf(" desc = USB_DT_OTHER_SPEED_CONFIG\n"); 249 | break; 250 | case USB_DT_INTERFACE_POWER: 251 | printf(" desc = USB_DT_INTERFACE_POWER\n"); 252 | break; 253 | case USB_DT_OTG: 254 | printf(" desc = USB_DT_OTG\n"); 255 | break; 256 | case USB_DT_DEBUG: 257 | printf(" desc = USB_DT_DEBUG\n"); 258 | break; 259 | case USB_DT_INTERFACE_ASSOCIATION: 260 | printf(" desc = USB_DT_INTERFACE_ASSOCIATION\n"); 261 | break; 262 | case USB_DT_SECURITY: 263 | printf(" desc = USB_DT_SECURITY\n"); 264 | break; 265 | case USB_DT_KEY: 266 | printf(" desc = USB_DT_KEY\n"); 267 | break; 268 | case USB_DT_ENCRYPTION_TYPE: 269 | printf(" desc = USB_DT_ENCRYPTION_TYPE\n"); 270 | break; 271 | case USB_DT_BOS: 272 | printf(" desc = USB_DT_BOS\n"); 273 | break; 274 | case USB_DT_DEVICE_CAPABILITY: 275 | printf(" desc = USB_DT_DEVICE_CAPABILITY\n"); 276 | break; 277 | case USB_DT_WIRELESS_ENDPOINT_COMP: 278 | printf(" desc = USB_DT_WIRELESS_ENDPOINT_COMP\n"); 279 | break; 280 | case USB_DT_PIPE_USAGE: 281 | printf(" desc = USB_DT_PIPE_USAGE\n"); 282 | break; 283 | case USB_DT_SS_ENDPOINT_COMP: 284 | printf(" desc = USB_DT_SS_ENDPOINT_COMP\n"); 285 | break; 286 | default: 287 | printf(" desc = unknown = 0x%x\n", 288 | ctrl->wValue >> 8); 289 | break; 290 | } 291 | break; 292 | case USB_REQ_SET_CONFIGURATION: 293 | printf(" req = USB_REQ_SET_CONFIGURATION\n"); 294 | break; 295 | case USB_REQ_GET_CONFIGURATION: 296 | printf(" req = USB_REQ_GET_CONFIGURATION\n"); 297 | break; 298 | case USB_REQ_SET_INTERFACE: 299 | printf(" req = USB_REQ_SET_INTERFACE\n"); 300 | break; 301 | case USB_REQ_GET_INTERFACE: 302 | printf(" req = USB_REQ_GET_INTERFACE\n"); 303 | break; 304 | case USB_REQ_GET_STATUS: 305 | printf(" req = USB_REQ_GET_STATUS\n"); 306 | break; 307 | case USB_REQ_CLEAR_FEATURE: 308 | printf(" req = USB_REQ_CLEAR_FEATURE\n"); 309 | break; 310 | case USB_REQ_SET_FEATURE: 311 | printf(" req = USB_REQ_SET_FEATURE\n"); 312 | break; 313 | default: 314 | printf(" req = unknown = 0x%x\n", ctrl->bRequest); 315 | break; 316 | } 317 | break; 318 | case USB_TYPE_CLASS: 319 | switch (ctrl->bRequest) { 320 | default: 321 | printf(" req = unknown = 0x%x\n", ctrl->bRequest); 322 | break; 323 | } 324 | break; 325 | default: 326 | printf(" req = unknown = 0x%x\n", ctrl->bRequest); 327 | break; 328 | } 329 | } 330 | 331 | void log_event(struct usb_raw_event *event) { 332 | switch (event->type) { 333 | case USB_RAW_EVENT_CONNECT: 334 | printf("event: connect, length: %u\n", event->length); 335 | break; 336 | case USB_RAW_EVENT_CONTROL: 337 | printf("event: control, length: %u\n", event->length); 338 | log_control_request((struct usb_ctrlrequest *)&event->data[0]); 339 | break; 340 | case USB_RAW_EVENT_SUSPEND: 341 | printf("event: suspend\n"); 342 | break; 343 | case USB_RAW_EVENT_RESUME: 344 | printf("event: resume\n"); 345 | break; 346 | case USB_RAW_EVENT_RESET: 347 | printf("event: reset\n"); 348 | break; 349 | case USB_RAW_EVENT_DISCONNECT: 350 | printf("event: disconnect\n"); 351 | break; 352 | default: 353 | printf("event: %d (unknown), length: %u\n", event->type, event->length); 354 | } 355 | } 356 | 357 | void print_eps_info(int fd) { 358 | struct usb_raw_eps_info info; 359 | memset(&info, 0, sizeof(info)); 360 | 361 | int num = usb_raw_eps_info(fd, &info); 362 | for (int i = 0; i < num; i++) { 363 | printf("ep #%d:\n", i); 364 | printf(" name: %s\n", &info.eps[i].name[0]); 365 | printf(" addr: %u\n", info.eps[i].addr); 366 | printf(" type: %s %s %s\n", 367 | info.eps[i].caps.type_iso ? "iso" : "___", 368 | info.eps[i].caps.type_bulk ? "blk" : "___", 369 | info.eps[i].caps.type_int ? "int" : "___"); 370 | printf(" dir : %s %s\n", 371 | info.eps[i].caps.dir_in ? "in " : "___", 372 | info.eps[i].caps.dir_out ? "out" : "___"); 373 | printf(" maxpacket_limit: %u\n", 374 | info.eps[i].limits.maxpacket_limit); 375 | printf(" max_streams: %u\n", info.eps[i].limits.max_streams); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /host-raw-gadget.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "misc.h" 6 | 7 | /*----------------------------------------------------------------------*/ 8 | 9 | #define UDC_NAME_LENGTH_MAX 128 10 | 11 | struct usb_raw_init { 12 | __u8 driver_name[UDC_NAME_LENGTH_MAX]; 13 | __u8 device_name[UDC_NAME_LENGTH_MAX]; 14 | __u8 speed; 15 | }; 16 | 17 | enum usb_raw_event_type { 18 | USB_RAW_EVENT_INVALID = 0, 19 | USB_RAW_EVENT_CONNECT = 1, 20 | USB_RAW_EVENT_CONTROL = 2, 21 | USB_RAW_EVENT_SUSPEND = 3, 22 | USB_RAW_EVENT_RESUME = 4, 23 | USB_RAW_EVENT_RESET = 5, 24 | USB_RAW_EVENT_DISCONNECT = 6, 25 | }; 26 | 27 | struct usb_raw_event { 28 | __u32 type; 29 | __u32 length; 30 | __u8 data[0]; 31 | }; 32 | 33 | struct usb_raw_ep_io { 34 | __u16 ep; 35 | __u16 flags; 36 | __u32 length; 37 | __u8 data[0]; 38 | }; 39 | 40 | #define USB_RAW_EPS_NUM_MAX 30 41 | #define USB_RAW_EP_NAME_MAX 16 42 | #define USB_RAW_EP_ADDR_ANY 0xff 43 | 44 | struct usb_raw_ep_caps { 45 | __u32 type_control : 1; 46 | __u32 type_iso : 1; 47 | __u32 type_bulk : 1; 48 | __u32 type_int : 1; 49 | __u32 dir_in : 1; 50 | __u32 dir_out : 1; 51 | }; 52 | 53 | struct usb_raw_ep_limits { 54 | __u16 maxpacket_limit; 55 | __u16 max_streams; 56 | __u32 reserved; 57 | }; 58 | 59 | struct usb_raw_ep_info { 60 | __u8 name[USB_RAW_EP_NAME_MAX]; 61 | __u32 addr; 62 | struct usb_raw_ep_caps caps; 63 | struct usb_raw_ep_limits limits; 64 | }; 65 | 66 | struct usb_raw_eps_info { 67 | struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX]; 68 | }; 69 | 70 | #define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init) 71 | #define USB_RAW_IOCTL_RUN _IO('U', 1) 72 | #define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event) 73 | #define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io) 74 | #define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io) 75 | #define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) 76 | #define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32) 77 | #define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io) 78 | #define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io) 79 | #define USB_RAW_IOCTL_CONFIGURE _IO('U', 9) 80 | #define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32) 81 | #define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info) 82 | #define USB_RAW_IOCTL_EP0_STALL _IO('U', 12) 83 | #define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32) 84 | #define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32) 85 | #define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32) 86 | 87 | /*----------------------------------------------------------------------*/ 88 | 89 | #define MAX_TRANSFER_SIZE 4096 90 | 91 | struct usb_raw_control_event { 92 | struct usb_raw_event inner; 93 | struct usb_ctrlrequest ctrl; 94 | }; 95 | 96 | struct usb_raw_transfer_io { 97 | struct usb_raw_ep_io inner; 98 | char data[MAX_TRANSFER_SIZE]; 99 | }; 100 | 101 | /*----------------------------------------------------------------------*/ 102 | 103 | struct thread_info { 104 | int fd; 105 | int ep_num; 106 | struct usb_endpoint_descriptor endpoint; 107 | std::string transfer_type; 108 | std::string dir; 109 | std::deque *data_queue; 110 | std::mutex *data_mutex; 111 | }; 112 | 113 | struct raw_gadget_endpoint { 114 | struct usb_endpoint_descriptor endpoint; 115 | pthread_t thread_read; 116 | pthread_t thread_write; 117 | struct thread_info thread_info; 118 | }; 119 | 120 | struct raw_gadget_altsetting { 121 | struct usb_interface_descriptor interface; 122 | struct raw_gadget_endpoint *endpoints; 123 | }; 124 | 125 | struct raw_gadget_interface { 126 | struct raw_gadget_altsetting *altsettings; 127 | int num_altsettings; 128 | int current_altsetting; 129 | }; 130 | 131 | struct raw_gadget_config { 132 | struct usb_config_descriptor config; 133 | struct raw_gadget_interface *interfaces; 134 | }; 135 | 136 | struct raw_gadget_device { 137 | struct usb_device_descriptor device; 138 | struct raw_gadget_config *configs; 139 | int current_config; 140 | }; 141 | 142 | extern struct raw_gadget_device host_device_desc; 143 | 144 | /*----------------------------------------------------------------------*/ 145 | 146 | enum usb_injection_flags { 147 | USB_INJECTION_FLAG_NONE, 148 | USB_INJECTION_FLAG_IGNORE, 149 | USB_INJECTION_FLAG_STALL, 150 | }; 151 | 152 | /*----------------------------------------------------------------------*/ 153 | 154 | int usb_raw_open(); 155 | void usb_raw_init(int fd, enum usb_device_speed speed, 156 | const char *driver, const char *device); 157 | void usb_raw_run(int fd); 158 | void usb_raw_event_fetch(int fd, struct usb_raw_event *event); 159 | int usb_raw_ep0_read(int fd, struct usb_raw_ep_io *io); 160 | int usb_raw_ep0_write(int fd, struct usb_raw_ep_io *io); 161 | int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor *desc); 162 | int usb_raw_ep_disable(int fd, uint32_t num); 163 | int usb_raw_ep_read(int fd, struct usb_raw_ep_io *io); 164 | int usb_raw_ep_write(int fd, struct usb_raw_ep_io *io); 165 | void usb_raw_configure(int fd); 166 | void usb_raw_vbus_draw(int fd, uint32_t power); 167 | int usb_raw_eps_info(int fd, struct usb_raw_eps_info *info); 168 | void usb_raw_ep0_stall(int fd); 169 | void usb_raw_ep_set_halt(int fd, int ep); 170 | 171 | void log_control_request(struct usb_ctrlrequest *ctrl); 172 | void log_event(struct usb_raw_event *event); 173 | void print_eps_info(int fd); 174 | -------------------------------------------------------------------------------- /injection.json: -------------------------------------------------------------------------------- 1 | { 2 | "control": { 3 | "modify": [ 4 | { 5 | "enable": false, 6 | "bRequestType": 0, 7 | "bRequest": 0, 8 | "wValue": 0, 9 | "wIndex": 0, 10 | "wLength": 0, 11 | "content_pattern": [], 12 | "replacement": "" 13 | } 14 | ], 15 | "ignore": [ 16 | { 17 | "enable": false, 18 | "bRequestType": 0, 19 | "bRequest": 0, 20 | "wValue": 0, 21 | "wIndex": 0, 22 | "wLength": 0, 23 | "content_pattern": [] 24 | } 25 | ], 26 | "stall": [ 27 | { 28 | "enable": false, 29 | "bRequestType": 0, 30 | "bRequest": 0, 31 | "wValue": 0, 32 | "wIndex": 0, 33 | "wLength": 0, 34 | "content_pattern": [] 35 | } 36 | ] 37 | }, 38 | "int": [ 39 | { 40 | "ep_address": 81, 41 | "enable": false, 42 | "content_pattern": [], 43 | "replacement": "" 44 | } 45 | ], 46 | "bulk": [ 47 | { 48 | "ep_address": 81, 49 | "enable": false, 50 | "content_pattern": [], 51 | "replacement": "" 52 | } 53 | ], 54 | "isoc": [] 55 | } 56 | -------------------------------------------------------------------------------- /misc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "misc.h" 4 | 5 | std::string hexToAscii(std::string input) { 6 | std::string output = input; 7 | size_t pos = output.find("\\x"); 8 | while (pos != std::string::npos) { 9 | std::string substr = output.substr(pos + 2, 2); 10 | 11 | std::istringstream iss(substr); 12 | iss.flags(std::ios::hex); 13 | int i; 14 | iss >> i; 15 | output = output.replace(pos, 4, 1, char(i)); 16 | pos = output.find("\\x"); 17 | } 18 | return output; 19 | } 20 | 21 | int hexToDecimal(int input) { 22 | int output = 0; 23 | int i = 0; 24 | while (input != 0) { 25 | output += (input % 10) * pow(16, i); 26 | input /= 10; 27 | i++; 28 | } 29 | return output; 30 | } 31 | -------------------------------------------------------------------------------- /misc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | extern int verbose_level; 17 | extern bool please_stop_ep0; 18 | extern volatile bool please_stop_eps; 19 | 20 | extern bool injection_enabled; 21 | extern std::string injection_file; 22 | extern Json::Value injection_config; 23 | 24 | extern bool customized_config_enabled; 25 | extern bool reset_device_before_proxy; 26 | extern bool bmaxpacketsize0_must_greater_than_64; 27 | 28 | std::string hexToAscii(std::string input); 29 | int hexToDecimal(int input); 30 | -------------------------------------------------------------------------------- /proxy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "host-raw-gadget.h" 4 | #include "device-libusb.h" 5 | #include "misc.h" 6 | 7 | void injection(struct usb_raw_transfer_io &io, Json::Value patterns, std::string replacement_hex, bool &data_modified) { 8 | std::string data(io.data, io.inner.length); 9 | std::string replacement = hexToAscii(replacement_hex); 10 | for (unsigned int j = 0; j < patterns.size(); j++) { 11 | std::string pattern_hex = patterns[j].asString(); 12 | std::string pattern = hexToAscii(pattern_hex); 13 | 14 | std::string::size_type pos = data.find(pattern); 15 | while (pos != std::string::npos) { 16 | if (data.length() - pattern.length() + replacement.length() > 1023) 17 | break; 18 | 19 | data = data.replace(pos, pattern.length(), replacement); 20 | printf("Modified from %s to %s at Index %ld\n", pattern_hex.c_str(), replacement_hex.c_str(), pos); 21 | data_modified = true; 22 | 23 | pos = data.find(pattern); 24 | } 25 | } 26 | 27 | if (data_modified) { 28 | io.inner.length = data.length(); 29 | for (size_t j = 0; j < data.length(); j++) { 30 | io.data[j] = data[j]; 31 | } 32 | } 33 | } 34 | 35 | void injection(struct usb_raw_control_event &event, struct usb_raw_transfer_io &io, int &injection_flags) { 36 | // This is just a simple injection function for control transfer. 37 | std::vector injection_type{"modify", "ignore", "stall"}; 38 | std::string transfer_type = "control"; 39 | 40 | for (unsigned int i = 0; i < injection_type.size(); i++) { 41 | for (unsigned int j = 0; j < injection_config[transfer_type][injection_type[i]].size(); j++) { 42 | Json::Value rule = injection_config[transfer_type][injection_type[i]][j]; 43 | if (rule["enable"].asBool() != true) 44 | continue; 45 | 46 | if (event.ctrl.bRequestType != hexToDecimal(rule["bRequestType"].asInt()) || 47 | event.ctrl.bRequest != hexToDecimal(rule["bRequest"].asInt()) || 48 | event.ctrl.wValue != hexToDecimal(rule["wValue"].asInt()) || 49 | event.ctrl.wIndex != hexToDecimal(rule["wIndex"].asInt()) || 50 | event.ctrl.wLength != hexToDecimal(rule["wLength"].asInt())) 51 | continue; 52 | 53 | printf("Matched injection rule: %s, index: %d\n", injection_type[i].c_str(), j); 54 | if (injection_type[i] == "modify") { 55 | Json::Value patterns = rule["content_pattern"]; 56 | std::string replacement_hex = rule["replacement"].asString(); 57 | bool data_modified = false; 58 | 59 | injection(io, patterns, replacement_hex, data_modified); 60 | if (!(event.ctrl.bRequestType & USB_DIR_IN)) 61 | event.ctrl.wLength = io.inner.length; 62 | } 63 | else if (injection_type[i] == "ignore") { 64 | printf("Ignore this control transfer\n"); 65 | injection_flags = USB_INJECTION_FLAG_IGNORE; 66 | } 67 | else if (injection_type[i] == "stall") { 68 | injection_flags = USB_INJECTION_FLAG_STALL; 69 | } 70 | } 71 | } 72 | } 73 | 74 | void injection(struct usb_raw_transfer_io &io, struct usb_endpoint_descriptor ep, std::string transfer_type) { 75 | // This is just a simple injection function for int and bulk transfer. 76 | for (unsigned int i = 0; i < injection_config[transfer_type].size(); i++) { 77 | Json::Value rule = injection_config[transfer_type][i]; 78 | if (rule["enable"].asBool() != true || 79 | hexToDecimal(rule["ep_address"].asInt()) != ep.bEndpointAddress) 80 | continue; 81 | 82 | Json::Value patterns = rule["content_pattern"]; 83 | std::string replacement_hex = rule["replacement"].asString(); 84 | bool data_modified = false; 85 | 86 | injection(io, patterns, replacement_hex, data_modified); 87 | 88 | if (data_modified) 89 | break; 90 | } 91 | } 92 | 93 | void printData(struct usb_raw_transfer_io io, __u8 bEndpointAddress, std::string transfer_type, std::string dir) { 94 | printf("Sending data to EP%x(%s_%s):", bEndpointAddress, 95 | transfer_type.c_str(), dir.c_str()); 96 | for (unsigned int i = 0; i < io.inner.length; i++) { 97 | printf(" %02hhx", (unsigned)io.data[i]); 98 | } 99 | printf("\n"); 100 | } 101 | 102 | void noop_signal_handler(int) { } 103 | 104 | void *ep_loop_write(void *arg) { 105 | struct thread_info thread_info = *((struct thread_info*) arg); 106 | int fd = thread_info.fd; 107 | int ep_num = thread_info.ep_num; 108 | struct usb_endpoint_descriptor ep = thread_info.endpoint; 109 | std::string transfer_type = thread_info.transfer_type; 110 | std::string dir = thread_info.dir; 111 | std::deque *data_queue = thread_info.data_queue; 112 | std::mutex *data_mutex = thread_info.data_mutex; 113 | 114 | printf("Start writing thread for EP%02x, thread id(%d)\n", 115 | ep.bEndpointAddress, gettid()); 116 | 117 | // Set a no-op handler for SIGUSR1. Sending this signal to the thread 118 | // will thus interrupt a blocking ioctl call without other side-effects. 119 | signal(SIGUSR1, noop_signal_handler); 120 | 121 | while (!please_stop_eps) { 122 | assert(ep_num != -1); 123 | if (data_queue->size() == 0) { 124 | usleep(100); 125 | continue; 126 | } 127 | 128 | data_mutex->lock(); 129 | struct usb_raw_transfer_io io = data_queue->front(); 130 | data_queue->pop_front(); 131 | data_mutex->unlock(); 132 | 133 | if (verbose_level >= 2) 134 | printData(io, ep.bEndpointAddress, transfer_type, dir); 135 | 136 | if (ep.bEndpointAddress & USB_DIR_IN) { 137 | int rv = usb_raw_ep_write(fd, (struct usb_raw_ep_io *)&io); 138 | if (rv < 0 && errno == ESHUTDOWN) { 139 | printf("EP%x(%s_%s): device likely reset, stopping thread\n", 140 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 141 | break; 142 | } 143 | if (rv < 0 && errno == EINTR) { 144 | printf("EP%x(%s_%s): interface likely changing, stopping thread\n", 145 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 146 | break; 147 | } 148 | if (rv < 0 && (errno == EXDEV || errno == ENODATA)) { 149 | printf("EP%x(%s_%s): missed isochronous timing, ignoring transfer\n", 150 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 151 | continue; 152 | } 153 | else if (rv < 0) { 154 | perror("usb_raw_ep_write()"); 155 | exit(EXIT_FAILURE); 156 | } 157 | else { 158 | printf("EP%x(%s_%s): wrote %d bytes to host\n", ep.bEndpointAddress, 159 | transfer_type.c_str(), dir.c_str(), rv); 160 | } 161 | } 162 | else { 163 | int length = io.inner.length; 164 | unsigned char *data = new unsigned char[length]; 165 | memcpy(data, io.data, length); 166 | int rv = send_data(ep.bEndpointAddress, ep.bmAttributes, data, length, USB_REQUEST_TIMEOUT); 167 | if (rv == LIBUSB_ERROR_NO_DEVICE) { 168 | printf("EP%x(%s_%s): device likely reset, stopping thread\n", 169 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 170 | break; 171 | } 172 | 173 | if (data) 174 | delete[] data; 175 | } 176 | } 177 | 178 | printf("End writing thread for EP%02x, thread id(%d)\n", 179 | ep.bEndpointAddress, gettid()); 180 | return NULL; 181 | } 182 | 183 | void *ep_loop_read(void *arg) { 184 | struct thread_info thread_info = *((struct thread_info*) arg); 185 | int fd = thread_info.fd; 186 | int ep_num = thread_info.ep_num; 187 | struct usb_endpoint_descriptor ep = thread_info.endpoint; 188 | std::string transfer_type = thread_info.transfer_type; 189 | std::string dir = thread_info.dir; 190 | std::deque *data_queue = thread_info.data_queue; 191 | std::mutex *data_mutex = thread_info.data_mutex; 192 | 193 | printf("Start reading thread for EP%02x, thread id(%d)\n", 194 | ep.bEndpointAddress, gettid()); 195 | 196 | // Set a no-op handler for SIGUSR1. Sending this signal to the thread 197 | // will thus interrupt a blocking ioctl call without other side-effects. 198 | signal(SIGUSR1, noop_signal_handler); 199 | 200 | while (!please_stop_eps) { 201 | assert(ep_num != -1); 202 | struct usb_raw_transfer_io io; 203 | 204 | if (ep.bEndpointAddress & USB_DIR_IN) { 205 | unsigned char *data = NULL; 206 | int nbytes = -1; 207 | 208 | if (data_queue->size() >= 32) { 209 | usleep(200); 210 | continue; 211 | } 212 | 213 | int rv = receive_data(ep.bEndpointAddress, ep.bmAttributes, usb_endpoint_maxp(&ep), 214 | &data, &nbytes, USB_REQUEST_TIMEOUT); 215 | if (rv == LIBUSB_ERROR_NO_DEVICE) { 216 | printf("EP%x(%s_%s): device likely reset, stopping thread\n", 217 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 218 | break; 219 | } 220 | 221 | if (nbytes >= 0) { 222 | memcpy(io.data, data, nbytes); 223 | io.inner.ep = ep_num; 224 | io.inner.flags = 0; 225 | io.inner.length = nbytes; 226 | 227 | if (injection_enabled) 228 | injection(io, ep, transfer_type); 229 | 230 | data_mutex->lock(); 231 | data_queue->push_back(io); 232 | data_mutex->unlock(); 233 | if (verbose_level) 234 | printf("EP%x(%s_%s): enqueued %d bytes to queue\n", ep.bEndpointAddress, 235 | transfer_type.c_str(), dir.c_str(), nbytes); 236 | } 237 | 238 | if (data) 239 | delete[] data; 240 | } 241 | else { 242 | io.inner.ep = ep_num; 243 | io.inner.flags = 0; 244 | io.inner.length = sizeof(io.data); 245 | 246 | int rv = usb_raw_ep_read(fd, (struct usb_raw_ep_io *)&io); 247 | if (rv < 0 && errno == ESHUTDOWN) { 248 | printf("EP%x(%s_%s): device likely reset, stopping thread\n", 249 | ep.bEndpointAddress, transfer_type.c_str(), dir.c_str()); 250 | break; 251 | } 252 | else if (rv < 0) { 253 | perror("usb_raw_ep_read()"); 254 | exit(EXIT_FAILURE); 255 | } 256 | else { 257 | printf("EP%x(%s_%s): read %d bytes from host\n", ep.bEndpointAddress, 258 | transfer_type.c_str(), dir.c_str(), rv); 259 | io.inner.length = rv; 260 | 261 | if (injection_enabled) 262 | injection(io, ep, transfer_type); 263 | 264 | data_mutex->lock(); 265 | data_queue->push_back(io); 266 | data_mutex->unlock(); 267 | if (verbose_level) 268 | printf("EP%x(%s_%s): enqueued %d bytes to queue\n", ep.bEndpointAddress, 269 | transfer_type.c_str(), dir.c_str(), rv); 270 | } 271 | } 272 | } 273 | 274 | printf("End reading thread for EP%02x, thread id(%d)\n", 275 | ep.bEndpointAddress, gettid()); 276 | return NULL; 277 | } 278 | 279 | void process_eps(int fd, int config, int interface, int altsetting) { 280 | struct raw_gadget_altsetting *alt = &host_device_desc.configs[config] 281 | .interfaces[interface].altsettings[altsetting]; 282 | 283 | printf("Activating %d endpoints on interface %d\n", (int)alt->interface.bNumEndpoints, interface); 284 | 285 | for (int i = 0; i < alt->interface.bNumEndpoints; i++) { 286 | struct raw_gadget_endpoint *ep = &alt->endpoints[i]; 287 | 288 | int addr = usb_endpoint_num(&ep->endpoint); 289 | assert(addr != 0); 290 | 291 | ep->thread_info.fd = fd; 292 | ep->thread_info.endpoint = ep->endpoint; 293 | ep->thread_info.data_queue = new std::deque; 294 | ep->thread_info.data_mutex = new std::mutex; 295 | 296 | switch (usb_endpoint_type(&ep->endpoint)) { 297 | case USB_ENDPOINT_XFER_ISOC: 298 | ep->thread_info.transfer_type = "isoc"; 299 | break; 300 | case USB_ENDPOINT_XFER_BULK: 301 | ep->thread_info.transfer_type = "bulk"; 302 | break; 303 | case USB_ENDPOINT_XFER_INT: 304 | ep->thread_info.transfer_type = "int"; 305 | break; 306 | default: 307 | printf("transfer_type %d is invalid\n", usb_endpoint_type(&ep->endpoint)); 308 | assert(false); 309 | } 310 | 311 | if (usb_endpoint_dir_in(&ep->endpoint)) 312 | ep->thread_info.dir = "in"; 313 | else 314 | ep->thread_info.dir = "out"; 315 | 316 | ep->thread_info.ep_num = usb_raw_ep_enable(fd, &ep->thread_info.endpoint); 317 | printf("%s_%s: addr = %u, ep = #%d\n", 318 | ep->thread_info.transfer_type.c_str(), 319 | ep->thread_info.dir.c_str(), 320 | addr, ep->thread_info.ep_num); 321 | 322 | if (verbose_level) 323 | printf("Creating thread for EP%02x\n", 324 | ep->thread_info.endpoint.bEndpointAddress); 325 | pthread_create(&ep->thread_read, 0, 326 | ep_loop_read, (void *)&ep->thread_info); 327 | pthread_create(&ep->thread_write, 0, 328 | ep_loop_write, (void *)&ep->thread_info); 329 | } 330 | 331 | printf("process_eps done\n"); 332 | } 333 | 334 | void terminate_eps(int fd, int config, int interface, int altsetting) { 335 | struct raw_gadget_altsetting *alt = &host_device_desc.configs[config] 336 | .interfaces[interface].altsettings[altsetting]; 337 | 338 | please_stop_eps = true; 339 | 340 | for (int i = 0; i < alt->interface.bNumEndpoints; i++) { 341 | struct raw_gadget_endpoint *ep = &alt->endpoints[i]; 342 | 343 | // Endpoint threads might be blocked either on a Raw Gadget 344 | // ioctl or on a libusb transfer handling. To interrupt the 345 | // former, we send the SIGUSR1 signal to the threads. The 346 | // threads have a no-op handler set for this signal, so the 347 | // ioctl gets interrupted with no other side-effects. 348 | // The libusb transfer handling does get interrupted directly 349 | // and instead times out. 350 | pthread_kill(ep->thread_read, SIGUSR1); 351 | pthread_kill(ep->thread_write, SIGUSR1); 352 | 353 | if (ep->thread_read && pthread_join(ep->thread_read, NULL)) { 354 | fprintf(stderr, "Error join thread_read\n"); 355 | } 356 | if (ep->thread_write && pthread_join(ep->thread_write, NULL)) { 357 | fprintf(stderr, "Error join thread_write\n"); 358 | } 359 | ep->thread_read = 0; 360 | ep->thread_write = 0; 361 | 362 | usb_raw_ep_disable(fd, ep->thread_info.ep_num); 363 | ep->thread_info.ep_num = -1; 364 | 365 | delete ep->thread_info.data_queue; 366 | delete ep->thread_info.data_mutex; 367 | } 368 | 369 | please_stop_eps = false; 370 | } 371 | 372 | void ep0_loop(int fd) { 373 | bool set_configuration_done_once = false; 374 | 375 | printf("Start for EP0, thread id(%d)\n", gettid()); 376 | 377 | if (verbose_level) 378 | print_eps_info(fd); 379 | 380 | while (!please_stop_ep0) { 381 | struct usb_raw_control_event event; 382 | event.inner.type = 0; 383 | event.inner.length = sizeof(event.ctrl); 384 | 385 | usb_raw_event_fetch(fd, (struct usb_raw_event *)&event); 386 | log_event((struct usb_raw_event *)&event); 387 | 388 | if (event.inner.length == 4294967295) { 389 | printf("End for EP0, thread id(%d)\n", gettid()); 390 | return; 391 | } 392 | 393 | // Normally, we would only need to check for USB_RAW_EVENT_RESET to handle a reset event. 394 | // However, dwc2 is buggy and it reports a disconnect event instead of a reset. 395 | if (event.inner.type == USB_RAW_EVENT_RESET || event.inner.type == USB_RAW_EVENT_DISCONNECT) { 396 | printf("Resetting device\n"); 397 | // Normally, we would need to stop endpoint threads first and only then 398 | // reset the device. However, libusb does not allow interrupting queued 399 | // requests submitted via sync I/O. Thus, we reset the proxied device to 400 | // force libusb to interrupt the requests and allow the endpoint threads 401 | // to exit on please_stop_eps checks. 402 | if (set_configuration_done_once) 403 | please_stop_eps = true; 404 | reset_device(); 405 | if (set_configuration_done_once) { 406 | struct raw_gadget_config *config = &host_device_desc.configs[host_device_desc.current_config]; 407 | printf("Stopping endpoint threads\n"); 408 | for (int i = 0; i < config->config.bNumInterfaces; i++) { 409 | struct raw_gadget_interface *iface = &config->interfaces[i]; 410 | int interface_num = iface->altsettings[iface->current_altsetting] 411 | .interface.bInterfaceNumber; 412 | terminate_eps(fd, host_device_desc.current_config, i, 413 | iface->current_altsetting); 414 | release_interface(interface_num); 415 | iface->current_altsetting = 0; 416 | } 417 | printf("Endpoint threads stopped\n"); 418 | host_device_desc.current_config = 0; 419 | set_configuration_done_once = false; 420 | } 421 | continue; 422 | } 423 | 424 | if (event.inner.type != USB_RAW_EVENT_CONTROL) 425 | continue; 426 | 427 | struct usb_raw_transfer_io io; 428 | io.inner.ep = 0; 429 | io.inner.flags = 0; 430 | io.inner.length = event.ctrl.wLength; 431 | 432 | int injection_flags = USB_INJECTION_FLAG_NONE; 433 | int nbytes = 0; 434 | int result = 0; 435 | unsigned char *control_data = new unsigned char[event.ctrl.wLength]; 436 | 437 | int rv = -1; 438 | if (event.ctrl.bRequestType & USB_DIR_IN) { 439 | result = control_request(&event.ctrl, &nbytes, &control_data, USB_REQUEST_TIMEOUT); 440 | if (result == 0) { 441 | memcpy(&io.data[0], control_data, nbytes); 442 | io.inner.length = nbytes; 443 | 444 | if (injection_enabled) { 445 | injection(event, io, injection_flags); 446 | switch(injection_flags) { 447 | case USB_INJECTION_FLAG_NONE: 448 | break; 449 | case USB_INJECTION_FLAG_IGNORE: 450 | delete[] control_data; 451 | continue; 452 | case USB_INJECTION_FLAG_STALL: 453 | delete[] control_data; 454 | usb_raw_ep0_stall(fd); 455 | continue; 456 | default: 457 | printf("[Warning] Unknown injection flags: %d\n", injection_flags); 458 | break; 459 | } 460 | } 461 | 462 | // Some UDCs require bMaxPacketSize0 to be at least 64. 463 | // Ideally, the information about UDC limitations needs to be 464 | // exposed by Raw Gadget, but this is not implemented at the moment; 465 | // see https://github.com/xairy/raw-gadget/issues/41. 466 | if (bmaxpacketsize0_must_greater_than_64 && 467 | (event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD && 468 | event.ctrl.bRequest == USB_REQ_GET_DESCRIPTOR && 469 | (event.ctrl.wValue >> 8) == USB_DT_DEVICE) { 470 | struct usb_device_descriptor *dev = (struct usb_device_descriptor *)&io.data; 471 | if (dev->bMaxPacketSize0 < 64) 472 | dev->bMaxPacketSize0 = 64; 473 | } 474 | 475 | if (verbose_level >= 2) 476 | printData(io, 0x00, "control", "in"); 477 | 478 | rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io *)&io); 479 | if (rv < 0) 480 | printf("ep0: ack failed: %d\n", rv); 481 | else 482 | printf("ep0: transferred %d bytes (in)\n", rv); 483 | } 484 | else { 485 | usb_raw_ep0_stall(fd); 486 | continue; 487 | } 488 | } 489 | else { 490 | if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD && 491 | event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { 492 | int desired_config = -1; 493 | for (int i = 0; i < host_device_desc.device.bNumConfigurations; i++) { 494 | if (host_device_desc.configs[i].config.bConfigurationValue == event.ctrl.wValue) { 495 | desired_config = i; 496 | break; 497 | } 498 | } 499 | if (desired_config < 0) { 500 | printf("[Warning] Skip changing configuration, wValue(%d) is invalid\n", event.ctrl.wValue); 501 | continue; 502 | } 503 | 504 | struct raw_gadget_config *config = &host_device_desc.configs[desired_config]; 505 | 506 | if (set_configuration_done_once) { // Need to stop all threads for eps and cleanup 507 | printf("Changing configuration\n"); 508 | for (int i = 0; i < config->config.bNumInterfaces; i++) { 509 | struct raw_gadget_interface *iface = &config->interfaces[i]; 510 | int interface_num = iface->altsettings[iface->current_altsetting] 511 | .interface.bInterfaceNumber; 512 | terminate_eps(fd, host_device_desc.current_config, i, 513 | iface->current_altsetting); 514 | release_interface(interface_num); 515 | } 516 | } 517 | 518 | usb_raw_configure(fd); 519 | set_configuration(config->config.bConfigurationValue); 520 | host_device_desc.current_config = desired_config; 521 | 522 | for (int i = 0; i < config->config.bNumInterfaces; i++) { 523 | struct raw_gadget_interface *iface = &config->interfaces[i]; 524 | iface->current_altsetting = 0; 525 | int interface_num = iface->altsettings[0].interface.bInterfaceNumber; 526 | claim_interface(interface_num); 527 | process_eps(fd, desired_config, i, 0); 528 | usleep(10000); // Give threads time to spawn. 529 | } 530 | 531 | set_configuration_done_once = true; 532 | 533 | // Ack request after spawning endpoint threads. 534 | rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io *)&io); 535 | if (rv < 0) 536 | printf("ep0: ack failed: %d\n", rv); 537 | else 538 | printf("ep0: request acked\n"); 539 | } 540 | else if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD && 541 | event.ctrl.bRequest == USB_REQ_SET_INTERFACE) { 542 | struct raw_gadget_config *config = 543 | &host_device_desc.configs[host_device_desc.current_config]; 544 | 545 | int desired_interface = -1; 546 | for (int i = 0; i < config->config.bNumInterfaces; i++) { 547 | if (config->interfaces[i].altsettings[0].interface.bInterfaceNumber == 548 | event.ctrl.wIndex) { 549 | desired_interface = i; 550 | break; 551 | } 552 | } 553 | if (desired_interface < 0) { 554 | printf("[Warning] Skip changing interface, wIndex(%d) is invalid\n", event.ctrl.wIndex); 555 | continue; 556 | } 557 | 558 | struct raw_gadget_interface *iface = &config->interfaces[desired_interface]; 559 | 560 | int desired_altsetting = -1; 561 | for (int i = 0; i < iface->num_altsettings; i++) { 562 | if (iface->altsettings[i].interface.bAlternateSetting == event.ctrl.wValue) { 563 | desired_altsetting = i; 564 | break; 565 | } 566 | } 567 | if (desired_altsetting < 0) { 568 | printf("[Warning] Skip changing alt_setting, wValue(%d) is invalid\n", event.ctrl.wValue); 569 | continue; 570 | } 571 | 572 | struct raw_gadget_altsetting *alt = &iface->altsettings[desired_altsetting]; 573 | 574 | if (desired_altsetting == iface->current_altsetting) { 575 | printf("Interface/altsetting already set\n"); 576 | // But lets propagate the request to the device. 577 | set_interface_alt_setting(alt->interface.bInterfaceNumber, 578 | alt->interface.bAlternateSetting); 579 | } 580 | else { 581 | printf("Changing interface/altsetting\n"); 582 | terminate_eps(fd, host_device_desc.current_config, 583 | desired_interface, iface->current_altsetting); 584 | set_interface_alt_setting(alt->interface.bInterfaceNumber, 585 | alt->interface.bAlternateSetting); 586 | process_eps(fd, host_device_desc.current_config, 587 | desired_interface, desired_altsetting); 588 | iface->current_altsetting = desired_altsetting; 589 | usleep(10000); // Give threads time to spawn. 590 | } 591 | 592 | // Ack request after spawning endpoint threads. 593 | rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io *)&io); 594 | if (rv < 0) 595 | printf("ep0: ack failed: %d\n", rv); 596 | else 597 | printf("ep0: request acked\n"); 598 | } 599 | else { 600 | if (injection_enabled) { 601 | injection(event, io, injection_flags); 602 | switch(injection_flags) { 603 | case USB_INJECTION_FLAG_NONE: 604 | break; 605 | case USB_INJECTION_FLAG_IGNORE: 606 | delete[] control_data; 607 | continue; 608 | case USB_INJECTION_FLAG_STALL: 609 | delete[] control_data; 610 | usb_raw_ep0_stall(fd); 611 | continue; 612 | default: 613 | printf("[Warning] Unknown injection flags: %d\n", injection_flags); 614 | break; 615 | } 616 | } 617 | 618 | if (event.ctrl.wLength == 0) { 619 | // For 0-length request, we can ack or stall the request via 620 | // Raw Gadget, depending on what the proxied device does. 621 | 622 | if (verbose_level >= 2) 623 | printData(io, 0x00, "control", "out"); 624 | 625 | result = control_request(&event.ctrl, &nbytes, &control_data, USB_REQUEST_TIMEOUT); 626 | if (result == 0) { 627 | // Ack the request. 628 | rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io *)&io); 629 | if (rv < 0) 630 | printf("ep0: ack failed: %d\n", rv); 631 | else 632 | printf("ep0: request acked\n"); 633 | } 634 | else { 635 | // Stall the request. 636 | usb_raw_ep0_stall(fd); 637 | continue; 638 | } 639 | } 640 | else { 641 | // For non-0-length requests, we cannot retrieve the request data 642 | // without acking the request due to the Gadget subsystem limitations. 643 | // Thus, we cannot stall such request for the host even if the proxied 644 | // device stalls. This is not ideal but seems to work fine in practice. 645 | 646 | // Retrieve data for sending request to proxied device 647 | // (and ack the request). 648 | rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io *)&io); 649 | if (rv < 0) { 650 | printf("ep0: ack failed: %d\n", rv); 651 | continue; 652 | } 653 | 654 | if (verbose_level >= 2) 655 | printData(io, 0x00, "control", "out"); 656 | 657 | memcpy(control_data, io.data, event.ctrl.wLength); 658 | 659 | result = control_request(&event.ctrl, &nbytes, &control_data, USB_REQUEST_TIMEOUT); 660 | if (result == 0) { 661 | printf("ep0: transferred %d bytes (out)\n", rv); 662 | } 663 | } 664 | } 665 | } 666 | 667 | delete[] control_data; 668 | } 669 | 670 | struct raw_gadget_config *config = &host_device_desc.configs[host_device_desc.current_config]; 671 | 672 | for (int i = 0; i < config->config.bNumInterfaces; i++) { 673 | struct raw_gadget_interface *iface = &config->interfaces[i]; 674 | int interface_num = iface->altsettings[iface->current_altsetting] 675 | .interface.bInterfaceNumber; 676 | terminate_eps(fd, host_device_desc.current_config, i, 677 | iface->current_altsetting); 678 | release_interface(interface_num); 679 | } 680 | 681 | printf("End for EP0, thread id(%d)\n", gettid()); 682 | } 683 | -------------------------------------------------------------------------------- /proxy.h: -------------------------------------------------------------------------------- 1 | void ep0_loop(int fd); 2 | -------------------------------------------------------------------------------- /usb-proxy.cpp: -------------------------------------------------------------------------------- 1 | #include "host-raw-gadget.h" 2 | #include "device-libusb.h" 3 | #include "proxy.h" 4 | #include "misc.h" 5 | 6 | int verbose_level = 0; 7 | bool please_stop_ep0 = false; 8 | volatile bool please_stop_eps = false; // Use volatile to mark as atomic. 9 | 10 | bool injection_enabled = false; 11 | std::string injection_file = "injection.json"; 12 | Json::Value injection_config; 13 | 14 | bool customized_config_enabled = false; 15 | std::string customized_config_file = "config.json"; 16 | bool reset_device_before_proxy = true; 17 | bool bmaxpacketsize0_must_greater_than_64 = true; 18 | 19 | void usage() { 20 | printf("Usage:\n"); 21 | printf("\t-h/--help: print this help message\n"); 22 | printf("\t-v/--verbose: increase verbosity\n"); 23 | printf("\t--device: use specific device\n"); 24 | printf("\t--driver: use specific driver\n"); 25 | printf("\t--vendor_id: use specific vendor_id of USB device\n"); 26 | printf("\t--product_id: use specific product_id of USB device\n"); 27 | printf("\t--enable_injection: enable the injection feature\n"); 28 | printf("\t--injection_file: specify the file that contains injection rules\n"); 29 | printf("\t--enable_customized_config: enable the customized config feature\n\n"); 30 | printf("* If `device` not specified, `usb-proxy` will use `dummy_udc.0` as default device.\n"); 31 | printf("* If `driver` not specified, `usb-proxy` will use `dummy_udc` as default driver.\n"); 32 | printf("* If both `vendor_id` and `product_id` not specified, `usb-proxy` will connect\n"); 33 | printf(" the first USB device it can find.\n"); 34 | printf("* If `injection_file` not specified, `usb-proxy` will use `injection.json` by default.\n\n"); 35 | exit(1); 36 | } 37 | 38 | void handle_signal(int signum) { 39 | switch (signum) { 40 | case SIGTERM: 41 | case SIGINT: 42 | static bool signal_received = false; 43 | if (signal_received) { 44 | printf("Signal received again, force exiting\n"); 45 | exit(1); 46 | } 47 | if (signum == SIGTERM) 48 | printf("Received SIGTERM, stopping...\n"); 49 | else 50 | printf("Received SIGINT, stopping...\n"); 51 | 52 | signal_received = true; 53 | please_stop_ep0 = true; 54 | please_stop_eps = true; 55 | break; 56 | } 57 | } 58 | 59 | int setup_host_usb_desc() { 60 | host_device_desc.device = { 61 | .bLength = device_device_desc.bLength, 62 | .bDescriptorType = device_device_desc.bDescriptorType, 63 | .bcdUSB = device_device_desc.bcdUSB, 64 | .bDeviceClass = device_device_desc.bDeviceClass, 65 | .bDeviceSubClass = device_device_desc.bDeviceSubClass, 66 | .bDeviceProtocol = device_device_desc.bDeviceProtocol, 67 | .bMaxPacketSize0 = device_device_desc.bMaxPacketSize0, 68 | .idVendor = device_device_desc.idVendor, 69 | .idProduct = device_device_desc.idProduct, 70 | .bcdDevice = device_device_desc.bcdDevice, 71 | .iManufacturer = device_device_desc.iManufacturer, 72 | .iProduct = device_device_desc.iProduct, 73 | .iSerialNumber = device_device_desc.iSerialNumber, 74 | .bNumConfigurations = device_device_desc.bNumConfigurations, 75 | }; 76 | 77 | int bNumConfigurations = device_device_desc.bNumConfigurations; 78 | host_device_desc.configs = new struct raw_gadget_config[bNumConfigurations]; 79 | for (int i = 0; i < bNumConfigurations; i++) { 80 | struct usb_config_descriptor temp_config = { 81 | .bLength = device_config_desc[i]->bLength, 82 | .bDescriptorType = device_config_desc[i]->bDescriptorType, 83 | .wTotalLength = device_config_desc[i]->wTotalLength, 84 | .bNumInterfaces = device_config_desc[i]->bNumInterfaces, 85 | .bConfigurationValue = device_config_desc[i]->bConfigurationValue, 86 | .iConfiguration = device_config_desc[i]->iConfiguration, 87 | .bmAttributes = device_config_desc[i]->bmAttributes, 88 | .bMaxPower = device_config_desc[i]->MaxPower, 89 | }; 90 | host_device_desc.configs[i].config = temp_config; 91 | 92 | int bNumInterfaces = device_config_desc[i]->bNumInterfaces; 93 | struct raw_gadget_interface *temp_interfaces = 94 | new struct raw_gadget_interface[bNumInterfaces]; 95 | for (int j = 0; j < bNumInterfaces; j++) { 96 | int num_altsetting = device_config_desc[i]->interface[j].num_altsetting; 97 | struct raw_gadget_altsetting *temp_altsettings = 98 | new struct raw_gadget_altsetting[num_altsetting]; 99 | for (int k = 0; k < num_altsetting; k++) { 100 | const struct libusb_interface_descriptor temp_device_altsetting = 101 | device_config_desc[i]->interface[j].altsetting[k]; 102 | struct usb_interface_descriptor temp_host_altsetting = { 103 | .bLength = temp_device_altsetting.bLength, 104 | .bDescriptorType = temp_device_altsetting.bDescriptorType, 105 | .bInterfaceNumber = temp_device_altsetting.bInterfaceNumber, 106 | .bAlternateSetting = temp_device_altsetting.bAlternateSetting, 107 | .bNumEndpoints = temp_device_altsetting.bNumEndpoints, 108 | .bInterfaceClass = temp_device_altsetting.bInterfaceClass, 109 | .bInterfaceSubClass = temp_device_altsetting.bInterfaceSubClass, 110 | .bInterfaceProtocol = temp_device_altsetting.bInterfaceProtocol, 111 | .iInterface = temp_device_altsetting.iInterface, 112 | }; 113 | temp_altsettings[k].interface = temp_host_altsetting; 114 | 115 | if (!temp_device_altsetting.bNumEndpoints) { 116 | printf("InterfaceNumber %x AlternateSetting %x has no endpoint, skip\n", 117 | temp_device_altsetting.bInterfaceNumber, 118 | temp_device_altsetting.bAlternateSetting); 119 | temp_altsettings[k].endpoints = NULL; 120 | continue; 121 | } 122 | 123 | int bNumEndpoints = temp_device_altsetting.bNumEndpoints; 124 | struct raw_gadget_endpoint *temp_endpoints = 125 | new struct raw_gadget_endpoint[bNumEndpoints]; 126 | for (int l = 0; l < bNumEndpoints; l++) { 127 | struct usb_endpoint_descriptor temp_endpoint = { 128 | .bLength = temp_device_altsetting.endpoint[l].bLength, 129 | .bDescriptorType = temp_device_altsetting.endpoint[l].bDescriptorType, 130 | .bEndpointAddress = temp_device_altsetting.endpoint[l].bEndpointAddress, 131 | .bmAttributes = temp_device_altsetting.endpoint[l].bmAttributes, 132 | .wMaxPacketSize = temp_device_altsetting.endpoint[l].wMaxPacketSize, 133 | .bInterval = temp_device_altsetting.endpoint[l].bInterval, 134 | .bRefresh = temp_device_altsetting.endpoint[l].bRefresh, 135 | .bSynchAddress = temp_device_altsetting.endpoint[l].bSynchAddress, 136 | }; 137 | temp_endpoints[l].endpoint = temp_endpoint; 138 | temp_endpoints[l].thread_read = 0; 139 | temp_endpoints[l].thread_write = 0; 140 | memset((void *)&temp_endpoints[l].thread_info, 0, 141 | sizeof(temp_endpoints[l].thread_info)); 142 | temp_endpoints[l].thread_info.ep_num = -1; 143 | } 144 | temp_altsettings[k].endpoints = temp_endpoints; 145 | } 146 | temp_interfaces[j].altsettings = temp_altsettings; 147 | temp_interfaces[j].num_altsettings = device_config_desc[i]->interface[j].num_altsetting; 148 | temp_interfaces[j].current_altsetting = 0; 149 | 150 | } 151 | host_device_desc.configs[i].interfaces = temp_interfaces; 152 | } 153 | 154 | host_device_desc.current_config = 0; 155 | 156 | return 0; 157 | } 158 | 159 | int main(int argc, char **argv) 160 | { 161 | const char *device = "dummy_udc.0"; 162 | const char *driver = "dummy_udc"; 163 | int vendor_id = -1; 164 | int product_id = -1; 165 | 166 | struct sigaction action; 167 | memset(&action, 0, sizeof(struct sigaction)); 168 | action.sa_handler = handle_signal; 169 | sigaction(SIGTERM, &action, NULL); 170 | sigaction(SIGINT, &action, NULL); 171 | 172 | int opt, lopt, loidx; 173 | const char *optstring = "hv"; 174 | const struct option long_options[] = { 175 | {"help", no_argument, &lopt, 1}, 176 | {"verbose", no_argument, &lopt, 2}, 177 | {"device", required_argument, &lopt, 3}, 178 | {"driver", required_argument, &lopt, 4}, 179 | {"vendor_id", required_argument, &lopt, 5}, 180 | {"product_id", required_argument, &lopt, 6}, 181 | {"enable_injection", no_argument, &lopt, 7}, 182 | {"injection_file", required_argument, &lopt, 8}, 183 | {"enable_customized_config", no_argument, &lopt, 9}, 184 | {0, 0, 0, 0} 185 | }; 186 | while ((opt = getopt_long(argc, argv, optstring, long_options, &loidx)) != -1) { 187 | if(opt == 0) 188 | opt = lopt; 189 | switch (opt) { 190 | case 'h': 191 | usage(); 192 | break; 193 | case 'v': 194 | verbose_level++; 195 | break; 196 | case 1: 197 | usage(); 198 | break; 199 | case 2: 200 | verbose_level++; 201 | break; 202 | case 3: 203 | device = optarg; 204 | break; 205 | case 4: 206 | driver = optarg; 207 | break; 208 | case 5: 209 | vendor_id = std::stoul(optarg, nullptr, 16); 210 | break; 211 | case 6: 212 | product_id = std::stoul(optarg, nullptr, 16); 213 | break; 214 | case 7: 215 | injection_enabled = true; 216 | break; 217 | case 8: 218 | injection_file = optarg; 219 | break; 220 | case 9: 221 | customized_config_enabled = true; 222 | break; 223 | 224 | default: 225 | usage(); 226 | return 1; 227 | } 228 | } 229 | printf("Device is: %s\n", device); 230 | printf("Driver is: %s\n", driver); 231 | printf("vendor_id is: %d\n", vendor_id); 232 | printf("product_id is: %d\n", product_id); 233 | 234 | if (injection_enabled) { 235 | printf("Injection enabled\n"); 236 | if (injection_file.empty()) { 237 | printf("Injection file not specified\n"); 238 | return 1; 239 | } 240 | struct stat buffer; 241 | if (stat(injection_file.c_str(), &buffer) != 0) { 242 | printf("Injection file %s not found\n", injection_file.c_str()); 243 | return 1; 244 | } 245 | 246 | Json::Reader jsonReader; 247 | std::ifstream ifs(injection_file.c_str()); 248 | if (jsonReader.parse(ifs, injection_config)) 249 | printf("Parsed injection file: %s\n", injection_file.c_str()); 250 | else { 251 | printf("Error parsing injection file: %s\n", injection_file.c_str()); 252 | return 1; 253 | } 254 | ifs.close(); 255 | } 256 | 257 | if (customized_config_enabled) { 258 | struct stat buffer; 259 | if (stat(customized_config_file.c_str(), &buffer) != 0) { 260 | printf("Customized config file %s not found\n", customized_config_file.c_str()); 261 | return 1; 262 | } 263 | 264 | Json::Reader jsonReader; 265 | std::ifstream ifs(customized_config_file.c_str()); 266 | Json::Value customized_config; 267 | if (jsonReader.parse(ifs, customized_config)) 268 | printf("Parsed customized config file: %s\n", customized_config_file.c_str()); 269 | else { 270 | printf("Error parsing customized config file: %s\n", customized_config_file.c_str()); 271 | return 1; 272 | } 273 | ifs.close(); 274 | 275 | if (customized_config["reset_device_before_proxy"] == false) { 276 | printf("reset_device_before_proxy set to false\n"); 277 | reset_device_before_proxy = false; 278 | } 279 | if (customized_config["bmaxpacketsize0_must_greater_than_64"] == false) { 280 | printf("bmaxpacketsize0_must_greater_than_64 set to false\n"); 281 | bmaxpacketsize0_must_greater_than_64 = false; 282 | } 283 | } 284 | 285 | while (connect_device(vendor_id, product_id)) { 286 | sleep(1); 287 | } 288 | printf("Device opened successfully\n"); 289 | 290 | setup_host_usb_desc(); 291 | printf("Setup USB config successfully\n"); 292 | 293 | int fd = usb_raw_open(); 294 | usb_raw_init(fd, USB_SPEED_HIGH, driver, device); 295 | usb_raw_run(fd); 296 | 297 | ep0_loop(fd); 298 | 299 | close(fd); 300 | 301 | int bNumConfigurations = device_device_desc.bNumConfigurations; 302 | for (int i = 0; i < bNumConfigurations; i++) { 303 | int bNumInterfaces = device_config_desc[i]->bNumInterfaces; 304 | for (int j = 0; j < bNumInterfaces; j++) { 305 | int num_altsetting = device_config_desc[i]->interface[j].num_altsetting; 306 | for (int k = 0; k < num_altsetting; k++) { 307 | if (host_device_desc.configs[i].interfaces[j].altsettings[k].endpoints) { 308 | delete[] host_device_desc.configs[i].interfaces[j].altsettings[k].endpoints; 309 | } 310 | } 311 | delete[] host_device_desc.configs[i].interfaces[j].altsettings; 312 | } 313 | delete[] host_device_desc.configs[i].interfaces; 314 | } 315 | delete[] host_device_desc.configs; 316 | delete[] device_config_desc; 317 | 318 | if (context && callback_handle != -1) { 319 | libusb_hotplug_deregister_callback(context, callback_handle); 320 | } 321 | if (hotplug_monitor_thread && 322 | pthread_join(hotplug_monitor_thread, NULL)) { 323 | fprintf(stderr, "Error join hotplug_monitor_thread\n"); 324 | } 325 | 326 | return 0; 327 | } 328 | --------------------------------------------------------------------------------