├── .gitignore ├── Makefile ├── README.md ├── TODO ├── usbdevice.h ├── usbfunctionfs.cpp ├── usbfunctionfs.h ├── usbredir2phys.config ├── usbredir2phys.cpp ├── usbredir2phys.creator ├── usbredir2phys.files └── usbredir2phys.includes /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.user 3 | usbredir2phys 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEBUG := TRUE 2 | CC = gcc 3 | CXX = g++ 4 | 5 | GCCFLAGS = -Wall -W -Wextra $(shell pkg-config --cflags libusbgx) -std=c++17 -Wno-unused-parameter -Wno-unused-variable 6 | LDFLAGS = -pthread -lusbredirparser $(shell pkg-config --libs libusbgx) 7 | 8 | ifeq ($(DEBUG),FALSE) 9 | GCCFLAGS += -Os -D NDEBUG 10 | else 11 | GCCFLAGS += -O0 -g 12 | endif 13 | 14 | OBJS = $(patsubst %.c, %.o, $(shell find . -name \*.c)) 15 | OBJS += $(patsubst %.cpp, %.o, $(shell find . -name \*.cpp)) 16 | EXE = usbredir2phys 17 | 18 | all: $(EXE) 19 | 20 | %.o: %.c 21 | $(CC) $(GCCFLAGS) -c $< -o $@ 22 | 23 | %.o: %.cpp 24 | $(CXX) $(GCCFLAGS) -c $< -o $@ 25 | 26 | %.o: %.S 27 | $(AS) -c $< -o $@ 28 | 29 | $(EXE): $(OBJS) 30 | $(CXX) $^ -o $@ $(LDFLAGS) 31 | 32 | clean: 33 | rm -f $(OBJS) $(EXE) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | usbredir2phys 2 | ------------- 3 | 4 | This tool can be used to turn virtual USB connections (usbredir) into physical ones by utilizing the linux USB gadget stack. 5 | 6 | Usage 7 | ----- 8 | 9 | Just run 10 | 11 | ``` 12 | usbredir2phys 13 | ``` 14 | 15 | to connect to a usbredir server at ```:```. 16 | usbredir2phys will start to query all descriptors, setup a composite device using libusbgx, add a new USB FFS function for each configuration and start to listen for events on each function. 17 | 18 | Known limitations 19 | ----------------- 20 | 21 | Multiple configurations are not fully implemented yet. 22 | They will be configured and set up internally, but switching from the host will not have any effect on the device (no setConfiguration packet sent) -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Refactor USBFunctionFS out of main .cpp file 2 | - Handle different configurations 3 | -------------------------------------------------------------------------------- /usbdevice.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICE_H 2 | #define USBDEVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | /* Although the max len supported is 65536, 14 | * libusb rejects anything over 4096 as "Windows does not support it". 15 | * *facedesk* 16 | */ 17 | #define USB_MAX_CTRL_SIZE 4096 18 | 19 | constexpr size_t epAddrToIndex(uint8_t a) 20 | { 21 | return (a & 0x80 >> 3) | (a & 0xF); 22 | } 23 | 24 | constexpr uint8_t stringIndex(uint32_t str) 25 | { 26 | return str & 0xFF; 27 | } 28 | 29 | constexpr uint16_t stringLangID(uint32_t str) 30 | { 31 | return str >> 16; 32 | } 33 | 34 | constexpr uint32_t makeString(uint16_t langid, uint8_t index) 35 | { 36 | return langid << 16 | index; 37 | } 38 | 39 | struct USBEndpoint { 40 | usb_endpoint_descriptor desc; 41 | }; 42 | 43 | struct USBInterface { 44 | usb_interface_descriptor desc; 45 | /* Addresses of endpoints. 46 | * Use epAddrToIndex to get the index into USBDevice::endpoints. */ 47 | std::vector endpoints; 48 | }; 49 | 50 | struct USBConfiguration { 51 | usb_config_descriptor desc; 52 | std::vector interfaces; 53 | /* The whole blob receviced, containing IFs, EPs 54 | * and unrecognized descriptors */ 55 | std::vector full_desc; 56 | }; 57 | 58 | struct USBStrings { 59 | std::set langs; 60 | // Use makeString as index 61 | std::map strings; 62 | 63 | std::string getUTF8(uint16_t lang, uint8_t index) const 64 | { 65 | auto key = makeString(lang, index); 66 | if(index == 0 || strings.count(key) == 0) 67 | return {}; 68 | 69 | std::wstring_convert, char16_t> conv; 70 | return conv.to_bytes(strings.at(key)); 71 | } 72 | }; 73 | 74 | struct USBDevice { 75 | usb_device_descriptor desc; 76 | uint8_t active_config; 77 | std::vector configs; 78 | USBEndpoint endpoints[32]; 79 | USBStrings strings; 80 | }; 81 | 82 | #endif // USBDEVICE_H 83 | -------------------------------------------------------------------------------- /usbfunctionfs.cpp: -------------------------------------------------------------------------------- 1 | #include "usbfunctionfs.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "usbdevice.h" 12 | 13 | template 14 | void debugPrintf(Args... args) 15 | { 16 | #ifndef NDEBUG 17 | printf(args...); 18 | #endif 19 | } 20 | 21 | USBFunctionFS::~USBFunctionFS() 22 | { 23 | if(path.empty()) 24 | return; 25 | 26 | stop_threads = true; 27 | 28 | if(dirfd >= 0) 29 | close(dirfd); 30 | 31 | // Close all endpoints 32 | endpoints.clear(); 33 | 34 | std::string command = std::string("umount " + path); 35 | system(command.c_str()); 36 | 37 | rmdir(path.c_str()); 38 | } 39 | 40 | bool USBFunctionFS::create(std::string name) 41 | { 42 | // Check for invalid name 43 | if(name.find('\'') != std::string::npos) 44 | return false; 45 | 46 | char tmpname[] = "/tmp/ffsXXXXXX"; 47 | if(mkdtemp(tmpname) == nullptr) 48 | return false; 49 | 50 | path = tmpname; 51 | 52 | std::string command = std::string("mount -t functionfs '" + name + "' " + path); 53 | if(system(command.c_str()) == 0) 54 | { 55 | dirfd = open(path.c_str(), O_RDONLY | O_DIRECTORY); 56 | return dirfd >= 0; 57 | } 58 | 59 | rmdir(path.c_str()); 60 | path = ""; 61 | return false; 62 | } 63 | 64 | int USBFunctionFS::openEP(uint8_t id, bool nonblock) 65 | { 66 | char name[5]; 67 | snprintf(name, sizeof(name), "ep%x", id); 68 | return openat(dirfd, name, O_RDWR | (nonblock ? O_NONBLOCK : 0)); 69 | } 70 | 71 | bool USBFunctionFS::writePacketEP(uint8_t ep, const uint8_t *data, size_t size) 72 | { 73 | auto &&ep_data = endpoints.find(ep); 74 | if(ep_data == endpoints.end()) 75 | return false; 76 | 77 | return write(ep_data->second->fd, data, size) == ssize_t(size); 78 | } 79 | 80 | bool USBFunctionFS::readPacketEP(uint8_t ep, uint8_t *data, size_t size) 81 | { 82 | auto &&ep_data = endpoints.find(ep); 83 | if(ep_data == endpoints.end()) 84 | return false; 85 | 86 | return read(ep_data->second->fd, data, size) == ssize_t(size); 87 | } 88 | 89 | bool USBFunctionFS::handleEPXData(uint8_t ep, EPData &ep_data) 90 | { 91 | while(ep_data.waiting_for_answer) 92 | { 93 | using namespace std::chrono_literals; 94 | std::this_thread::sleep_for(5ms); 95 | 96 | if(stop_threads) 97 | return true; 98 | } 99 | 100 | uint8_t buf[USB_MAX_CTRL_SIZE]; 101 | auto r = read(ep_data.fd, buf, sizeof(buf)); 102 | if(r == 0 || (r < 0 && errno == EAGAIN)) 103 | return true; // EOF 104 | else if(r < 0) 105 | { 106 | perror("EPX READ"); 107 | return false; 108 | } 109 | 110 | debugPrintf("Host: Received EPX Packet(%ld bytes)\n", r); 111 | 112 | epx_cb(ep, buf, r); 113 | 114 | return true; 115 | } 116 | 117 | template 118 | void appendToVector(std::vector &v, const T &data) 119 | { 120 | v.resize(v.size() + sizeof(T)); 121 | memcpy(v.data() + v.size() - sizeof(T), &data, sizeof(T)); 122 | } 123 | 124 | bool USBFunctionFS::initForConfig(const USBDevice &dev, const USBConfiguration &config) 125 | { 126 | int ep0 = openEP(0, true); 127 | 128 | if(ep0 == -1) 129 | { 130 | fprintf(stderr, "Could not open ep0!"); 131 | return false; 132 | } 133 | 134 | endpoints[0x80] = std::make_shared(); 135 | endpoints[0x80]->fd = ep0; 136 | 137 | std::vector request; 138 | 139 | /* TODO: Same descriptors used for FS and HS (*2) */ 140 | uint32_t length = sizeof(usb_functionfs_descs_head_v2) 141 | + sizeof(uint32_t) * 2 142 | + config.full_desc.size() * 2; 143 | 144 | request.reserve(length); 145 | 146 | usb_functionfs_descs_head_v2 head { 147 | .magic = FUNCTIONFS_DESCRIPTORS_MAGIC_V2, 148 | .length = length, 149 | .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | 64 /* FUNCTIONFS_ALL_CTRL_RECIP */ 150 | }; 151 | 152 | appendToVector(request, head); 153 | 154 | /* Count descriptors */ 155 | uint32_t fs_count = 0; 156 | { 157 | uint32_t size = config.full_desc.size(); 158 | const uint8_t *data = config.full_desc.data(); 159 | 160 | while(size) { 161 | auto *desc = reinterpret_cast(data); 162 | if(desc->bLength < sizeof(usb_descriptor_header) || desc->bLength > size) 163 | break; /* Invalid bLength */ 164 | 165 | fs_count += 1; 166 | data += desc->bLength; 167 | size -= desc->bLength; 168 | } 169 | } 170 | 171 | if(fs_count == 0) 172 | { 173 | fprintf(stderr, "Config %d has no valid descriptors!", config.desc.bConfigurationValue); 174 | return false; 175 | } 176 | 177 | appendToVector(request, uint32_t(fs_count)); 178 | appendToVector(request, uint32_t(fs_count)); 179 | 180 | request.insert(request.end(), config.full_desc.begin(), config.full_desc.end()); 181 | request.insert(request.end(), config.full_desc.begin(), config.full_desc.end()); 182 | 183 | //auto e = write(open("/tmp/ep0descs", O_CREAT | O_WRONLY, 0777), request.data(), request.size()); 184 | auto e = write(ep0, request.data(), request.size()); 185 | if(e != ssize_t(request.size())) 186 | { 187 | perror("FFS EP0 descs write (Kernel too old?)"); 188 | return false; 189 | } 190 | 191 | /* Write string descriptors */ 192 | request.clear(); 193 | 194 | /* TODO: Handle all strings and languages here */ 195 | auto s = dev.strings.getUTF8(0x409, config.desc.iConfiguration); 196 | 197 | length = sizeof(usb_functionfs_strings_head) 198 | + sizeof(uint16_t) 199 | + s.size(); 200 | 201 | usb_functionfs_strings_head shead { 202 | .magic = FUNCTIONFS_STRINGS_MAGIC, 203 | .length = length, 204 | .str_count = 1, 205 | .lang_count = 1 206 | }; 207 | 208 | appendToVector(request, shead); 209 | appendToVector(request, uint16_t(0x409)); 210 | request.insert(request.end(), s.c_str(), s.c_str() + s.size()); 211 | 212 | //e = write(open("/tmp/ep0strs", O_CREAT | O_WRONLY, 0777), request.data(), request.size()); 213 | e = write(ep0, request.data(), request.size()); 214 | if(e != ssize_t(request.size())) 215 | { 216 | perror("FFS EP0 strs write"); 217 | return false; 218 | } 219 | 220 | /* FFS does not use the EP address, it uses consecutive numbers 221 | * based on the descriptors we supplied... 222 | * FFS, FFS! */ 223 | uint8_t index = 0; 224 | for(auto&& intf : config.interfaces) 225 | for(auto&& epAddr : intf.endpoints) 226 | { 227 | if(endpoints.find(epAddr) != endpoints.end()) 228 | { 229 | perror("Duplicate EP address?"); 230 | return false; 231 | } 232 | 233 | auto epfd = openEP(index += 1, false); 234 | if(epfd == -1) 235 | { 236 | perror("FFS EP open"); 237 | return false; 238 | } 239 | 240 | this->endpoints[epAddr] = std::make_shared(); 241 | this->endpoints[epAddr]->fd = epfd; 242 | } 243 | 244 | for(auto &&ep : endpoints) 245 | { 246 | auto &epAddr = ep.first; 247 | auto &ep_data = *ep.second; 248 | 249 | // ep0 is handled separately 250 | if(epAddr == 0x80) 251 | continue; 252 | 253 | if((epAddr & USB_DIR_IN)) 254 | continue; 255 | 256 | /* Create read threads for all OUT endpoints. 257 | * Normally this could be handled in the main thread, 258 | * but despite O_NONBLOCK is set it blocks sometimes... */ 259 | 260 | /* Although 'this' is captured by value it is still a pointer. 261 | * However, the threads cannot outlive this object, so it is safe. */ 262 | auto ep_thread = [this, epAddr, &ep_data] () 263 | { 264 | while(!this->stop_threads.load()) 265 | handleEPXData(epAddr, ep_data); 266 | }; 267 | 268 | ep_data.thread = std::thread{ep_thread}; 269 | } 270 | 271 | return true; 272 | } 273 | 274 | int USBFunctionFS::getEP0FD() 275 | { 276 | return endpoints.at(0x80)->fd; 277 | } 278 | 279 | bool USBFunctionFS::handleEP0Data() 280 | { 281 | auto &ep_data = *endpoints[0x80]; 282 | 283 | /* Read events until EOF or reponse needed */ 284 | while(!ep_data.waiting_for_answer) 285 | { 286 | usb_functionfs_event event; 287 | auto r = read(ep_data.fd, &event, sizeof(event)); 288 | if(r == 0 || (r < 0 && errno == EAGAIN)) 289 | return true; // EOF 290 | else if(r < 0) 291 | { 292 | perror("EP0 READ"); 293 | return false; 294 | } 295 | else if(size_t(r) < sizeof(event)) 296 | { 297 | perror("EP0 partial read"); 298 | return false; 299 | } 300 | 301 | /* Handle event */ 302 | switch(event.type) 303 | { 304 | case FUNCTIONFS_BIND: 305 | case FUNCTIONFS_UNBIND: 306 | break; 307 | 308 | case FUNCTIONFS_ENABLE: 309 | debugPrintf("ENABLE\n"); break; 310 | case FUNCTIONFS_DISABLE: 311 | debugPrintf("DISABLE\n"); break; 312 | 313 | case FUNCTIONFS_SETUP: 314 | { 315 | debugPrintf("Host: Received control packet\n"); 316 | 317 | if((event.u.setup.bRequestType & USB_DIR_IN) == 0) 318 | { 319 | if(event.u.setup.wLength > USB_MAX_CTRL_SIZE) 320 | { 321 | fprintf(stderr, "Packet too big\n"); 322 | return false; 323 | } 324 | 325 | uint8_t data[USB_MAX_CTRL_SIZE]; 326 | if(!readPacketEP(0x80, data, event.u.setup.wLength)) 327 | perror("Warn: packet read"); 328 | 329 | ep0_cb(event.u.setup, data, event.u.setup.wLength); 330 | } 331 | else 332 | ep0_cb(event.u.setup, nullptr, 0); 333 | 334 | return true; 335 | } 336 | 337 | case FUNCTIONFS_SUSPEND: 338 | case FUNCTIONFS_RESUME: 339 | debugPrintf("Suspend/Resume: TODO\n"); 340 | break; 341 | 342 | default: 343 | fprintf(stderr, "Unknown EP0 event %d\n", event.type); 344 | // no return false here 345 | break; 346 | } 347 | } 348 | 349 | return true; 350 | } 351 | 352 | void USBFunctionFS::pauseProcessing(uint8_t ep) 353 | try{ 354 | endpoints.at(ep)->waiting_for_answer = true; 355 | }catch(...) { __builtin_trap(); } 356 | 357 | bool USBFunctionFS::processingPaused(uint8_t ep) 358 | try{ 359 | return endpoints.at(ep)->waiting_for_answer; 360 | }catch(...) { __builtin_trap(); } 361 | 362 | void USBFunctionFS::resumeProcessing(uint8_t ep) 363 | try { 364 | endpoints.at(ep)->waiting_for_answer = false; 365 | }catch(...) { __builtin_trap(); } 366 | 367 | USBFunctionFS::EPData::~EPData() 368 | { 369 | if(thread.joinable()) 370 | thread.join(); 371 | 372 | close(fd); 373 | } 374 | -------------------------------------------------------------------------------- /usbfunctionfs.h: -------------------------------------------------------------------------------- 1 | #ifndef USBFunctionFS_H 2 | #define USBFunctionFS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct usb_ctrlrequest; 12 | struct USBConfiguration; 13 | struct USBDevice; 14 | 15 | class USBFunctionFS 16 | { 17 | struct EPData 18 | { 19 | EPData() = default; 20 | EPData(EPData &&) = delete; 21 | EPData(const EPData &) = delete; 22 | ~EPData(); 23 | 24 | std::thread thread; 25 | std::atomic_bool waiting_for_answer{false}; 26 | int fd; 27 | }; 28 | 29 | // header data size 30 | typedef std::function EP0Callback; 31 | // ep data size 32 | typedef std::function EPXCallback; 33 | 34 | public: 35 | USBFunctionFS() = default; 36 | USBFunctionFS(const USBFunctionFS &other) = delete; 37 | USBFunctionFS(USBFunctionFS&& other) = delete; 38 | ~USBFunctionFS(); 39 | 40 | bool create(std::string name); 41 | 42 | bool initForConfig(const USBDevice &dev, const USBConfiguration &config); 43 | 44 | /* For use with select. */ 45 | int getEP0FD(); 46 | bool handleEP0Data(); 47 | 48 | /* Use these functions to inhibit reading data from the specified ep. */ 49 | void pauseProcessing(uint8_t ep); 50 | bool processingPaused(uint8_t ep); 51 | void resumeProcessing(uint8_t ep); 52 | 53 | /* Use this to access the various endpoints. 54 | * Only read from IN eps and only write to OUT eps. */ 55 | bool writePacketEP(uint8_t ep, const uint8_t *data, size_t size); 56 | bool readPacketEP(uint8_t ep, uint8_t *data, size_t size); 57 | 58 | // Only used in handleEP0Data 59 | EP0Callback ep0_cb; 60 | // Used in various threads 61 | // std::atomic epx_cb; 62 | // However, that ^^^ does not work. As it's only written once 63 | // before the fun/thread starts, it doesn't matter 64 | EPXCallback epx_cb; 65 | 66 | private: 67 | int openEP(uint8_t id, bool nonblock); 68 | 69 | bool handleEPXData(uint8_t ep, EPData &ep_data); 70 | void pollEPX(uint8_t ep, uint32_t max_size, EPData &ep_data); 71 | 72 | std::string path; 73 | int dirfd = -1; 74 | std::atomic_bool stop_threads{false}; 75 | std::map> endpoints; 76 | }; 77 | 78 | #endif // USBFunctionFS_H 79 | -------------------------------------------------------------------------------- /usbredir2phys.config: -------------------------------------------------------------------------------- 1 | // Add predefined macros for your project here. For example: 2 | // #define THE_ANSWER 42 3 | -------------------------------------------------------------------------------- /usbredir2phys.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | extern "C" { 24 | #include 25 | } 26 | 27 | #include "usbdevice.h" 28 | #include "usbfunctionfs.h" 29 | 30 | /* Various constants */ 31 | const constexpr uint32_t EP_FORWARD = 0x71FEBEEF, // Always forward 32 | EP_PROCESS = 0xDEADBEEF; // Targeted at ur2p itself 33 | 34 | /* Simple RAII deleter */ 35 | template 36 | using scope_ptr = std::unique_ptr; 37 | 38 | template 39 | void debugPrintf(Args... args) 40 | { 41 | #ifndef NDEBUG 42 | printf(args...); 43 | #endif 44 | } 45 | 46 | /* Helper for RAII */ 47 | class TCPConnection { 48 | public: 49 | TCPConnection() = default; 50 | TCPConnection(const TCPConnection &other) = delete; 51 | ~TCPConnection() 52 | { 53 | disconnect(); 54 | } 55 | 56 | bool connect(const char *hostname, uint16_t port) 57 | { 58 | if(connected()) 59 | { 60 | fprintf(stderr, "Already connected!\n"); 61 | return false; 62 | } 63 | 64 | addrinfo hints{}; 65 | addrinfo *res = nullptr; 66 | 67 | hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 68 | hints.ai_family = AF_UNSPEC; 69 | hints.ai_socktype = SOCK_STREAM; 70 | hints.ai_protocol = IPPROTO_TCP; 71 | 72 | if(getaddrinfo(hostname, std::to_string(port).c_str(), &hints, &res) != 0) 73 | return false; 74 | 75 | scope_ptr d(res, freeaddrinfo); (void) d; 76 | 77 | for(struct addrinfo *i = res; i != nullptr; i = i->ai_next) 78 | { 79 | if((fd = socket(i->ai_family, i->ai_socktype, i->ai_protocol)) == -1) 80 | continue; 81 | 82 | if(::connect(fd, i->ai_addr, i->ai_addrlen) == 0) 83 | break; 84 | 85 | close(fd); 86 | fd = -1; 87 | } 88 | 89 | return connected(); 90 | } 91 | 92 | ssize_t write(void *data, size_t count) 93 | { 94 | if(!connected()) 95 | { 96 | fprintf(stderr, "Not connected!\n"); 97 | return -1; 98 | } 99 | 100 | auto r = ::write(fd, data, count); 101 | if(r == -1 && errno == EPIPE) 102 | disconnect(); 103 | 104 | return r; 105 | } 106 | 107 | ssize_t read(void *data, size_t count) 108 | { 109 | if(!connected()) 110 | { 111 | fprintf(stderr, "Not connected!\n"); 112 | return -1; 113 | } 114 | 115 | auto r = ::read(fd, data, count); 116 | if(r == -1 && errno != EAGAIN && errno != EWOULDBLOCK) 117 | disconnect(); 118 | 119 | return r; 120 | } 121 | 122 | bool connected() 123 | { 124 | return fd != -1; 125 | } 126 | 127 | void disconnect() 128 | { 129 | if(connected()) 130 | close(fd); 131 | } 132 | 133 | int fd = -1; 134 | }; 135 | 136 | template 137 | void usbg_perror(usbg_error e, Args... args) 138 | { 139 | fprintf(stderr, args...); 140 | fprintf(stderr, ": %s\n", usbg_strerror(e)); 141 | } 142 | 143 | struct PrivUSBG { 144 | ~PrivUSBG() { 145 | usbg_error e; 146 | if(g != nullptr) 147 | { 148 | usbg_disable_gadget(g); 149 | if((e = usbg_error(usbg_rm_gadget(g, USBG_RM_RECURSE))) != USBG_SUCCESS) 150 | usbg_perror(e, "usbg_rm_gadget"); 151 | } 152 | 153 | if(s != nullptr) 154 | usbg_cleanup(s); 155 | } 156 | 157 | usbg_state *s; 158 | usbg_gadget *g; 159 | usbg_function *f; 160 | usbg_config *c; 161 | }; 162 | 163 | struct UR2PPriv { 164 | ~UR2PPriv() 165 | { 166 | if(poll_thread.joinable()) 167 | poll_thread.join(); 168 | } 169 | 170 | PrivUSBG usbg; 171 | TCPConnection con; 172 | scope_ptr parser{nullptr, usbredirparser_destroy}; 173 | USBDevice device; 174 | 175 | /* To be requested */ 176 | std::set missing_strings; 177 | 178 | enum { 179 | NO_IDEA, 180 | DEV_DESC_QUERIED, 181 | CONF_DESC_QUERIED, 182 | STR0_DESC_QUERIED, 183 | STR_DESC_QUERIED, 184 | FORWARDING 185 | } state; 186 | 187 | /* Some EPs (e.g. BULK IN) need to be polled for data. 188 | * This is done in this thread. */ 189 | std::thread poll_thread; 190 | 191 | /* One per configuration. 192 | * This is the last member as it needs to be cleaned up first. */ 193 | std::vector> ffs; 194 | }; 195 | 196 | /* Set to false in the signal handler. */ 197 | static std::atomic_bool keep_running{true}; 198 | 199 | void setup_signals() 200 | { 201 | struct sigaction sa; 202 | memset(&sa, 0, sizeof(sa)); 203 | sa.sa_flags |= SA_SIGINFO; 204 | sa.sa_sigaction = [](int, siginfo_t *, void *) { keep_running = false; }; 205 | 206 | for(auto i : {SIGINT, SIGHUP, SIGTERM, SIGQUIT}) 207 | sigaction(i, &sa, NULL); 208 | } 209 | 210 | static void forwardEP0ToRedir(UR2PPriv &priv, USBFunctionFS &ffs, const usb_ctrlrequest &ctrl, uint8_t *data, size_t size) 211 | { 212 | usb_redir_control_packet_header header = { 213 | .endpoint = uint8_t(ctrl.bRequestType & USB_DIR_IN), 214 | .request = ctrl.bRequest, 215 | .requesttype = ctrl.bRequestType, 216 | .status = usb_redir_success, 217 | .value = ctrl.wValue, 218 | .index = ctrl.wIndex, 219 | .length = ctrl.wLength, 220 | }; 221 | 222 | ffs.pauseProcessing(0x80); 223 | 224 | usbredirparser_send_control_packet(priv.parser.get(), EP_FORWARD, &header, data, size); 225 | 226 | // Main thread could hang inside select 227 | usbredirparser_do_write(priv.parser.get()); 228 | } 229 | 230 | static void forwardBulkToRedir(UR2PPriv &priv, USBFunctionFS &ffs, uint8_t ep, uint8_t *data, size_t size) 231 | { 232 | usb_redir_bulk_packet_header header = { 233 | .endpoint = ep, 234 | .status = usb_redir_success, 235 | .length = uint16_t(size), 236 | .stream_id = 0, 237 | .length_high = uint16_t(size >> 16), 238 | }; 239 | 240 | usbredirparser_send_bulk_packet(priv.parser.get(), EP_FORWARD, &header, data, size); 241 | 242 | ffs.pauseProcessing(ep); 243 | 244 | // Main thread could hang inside select 245 | usbredirparser_do_write(priv.parser.get()); 246 | } 247 | 248 | bool gadget_init(UR2PPriv &priv) 249 | { 250 | auto &dev = priv.device; 251 | 252 | usbg_gadget_attrs attrs = { 253 | .bcdUSB = dev.desc.bcdUSB, 254 | .bDeviceClass = dev.desc.bDeviceClass, 255 | .bDeviceSubClass = dev.desc.bDeviceSubClass, 256 | .bDeviceProtocol = dev.desc.bDeviceProtocol, 257 | .bMaxPacketSize0 = dev.desc.bMaxPacketSize0, 258 | .idVendor = dev.desc.idVendor, 259 | .idProduct = dev.desc.idProduct, 260 | .bcdDevice = dev.desc.bcdDevice, 261 | }; 262 | 263 | auto serial = priv.device.strings.getUTF8(0x409, dev.desc.iSerialNumber), 264 | product = priv.device.strings.getUTF8(0x409, dev.desc.iProduct), 265 | manuf = priv.device.strings.getUTF8(0x409, dev.desc.iManufacturer); 266 | 267 | usbg_gadget_strs strs; 268 | 269 | strs.serial = const_cast(serial.c_str()); 270 | strs.product = const_cast(product.c_str()); 271 | strs.manufacturer = const_cast(manuf.c_str()); 272 | 273 | auto e = usbg_error(usbg_create_gadget(priv.usbg.s, "redir0", &attrs, &strs, &priv.usbg.g)); 274 | if(e != USBG_SUCCESS) 275 | { 276 | usbg_perror(e, "usbg_create_gadget"); 277 | return false; 278 | } 279 | 280 | /* Add configurations */ 281 | for(uint8_t index = 0; index < dev.configs.size(); ++index) 282 | { 283 | auto &conf = dev.configs[index]; 284 | auto sConfig = priv.device.strings.getUTF8(0x409, conf.desc.iConfiguration); 285 | 286 | usbg_config_strs c_strs; 287 | c_strs.configuration = const_cast(sConfig.c_str()); 288 | 289 | char name[8]; 290 | snprintf(name, sizeof(name), "conf%x", index); 291 | 292 | usbg_config *g_c; 293 | usbg_create_config(priv.usbg.g, 1, name, nullptr, &c_strs, &g_c); 294 | 295 | /* Each configuration has exactly one FFS function */ 296 | usbg_function *g_f; 297 | snprintf(name, sizeof(name), "func%x", index); 298 | e = usbg_error(usbg_create_function(priv.usbg.g, USBG_F_FFS, name, nullptr, &g_f)); 299 | if(e != USBG_SUCCESS) 300 | { 301 | usbg_perror(e, "usbg_create_function"); 302 | return false; 303 | } 304 | 305 | usbg_add_config_function(g_c, name, g_f); 306 | 307 | /* Create the USBFunctionFS instance */ 308 | priv.ffs.emplace_back(std::make_unique()); 309 | USBFunctionFS &ffs = *priv.ffs.back(); 310 | 311 | if(!ffs.create(std::string{name})) 312 | { 313 | fprintf(stderr, "Could not create ffs!\n"); 314 | return false; 315 | } 316 | 317 | if(!ffs.initForConfig(dev, conf)) 318 | { 319 | fprintf(stderr, "Could not fill ffs!\n"); 320 | return false; 321 | } 322 | 323 | ffs.ep0_cb = [&priv, &ffs] (usb_ctrlrequest &req, uint8_t *data, size_t size) 324 | { 325 | forwardEP0ToRedir(priv, ffs, req, data, size); 326 | }; 327 | 328 | ffs.epx_cb = [&priv, &ffs] (uint8_t ep, uint8_t *data, size_t size) 329 | { 330 | forwardBulkToRedir(priv, ffs, ep, data, size); 331 | }; 332 | 333 | /* TODO: This won't work with multiple configurations */ 334 | 335 | std::vector poll_eps; 336 | for(auto &intf : conf.interfaces) 337 | { 338 | for(auto &ep_addr : intf.endpoints) 339 | { 340 | if((ep_addr & USB_DIR_IN) == 0) 341 | continue; 342 | 343 | auto &ep = dev.endpoints[epAddrToIndex(ep_addr)]; 344 | 345 | /* TODO: Handle others. */ 346 | switch(ep.desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) 347 | { 348 | case USB_ENDPOINT_XFER_BULK: 349 | poll_eps.push_back(&ep); 350 | break; 351 | case USB_ENDPOINT_XFER_INT: 352 | { 353 | usb_redir_start_interrupt_receiving_header header = { 354 | .endpoint = ep.desc.bEndpointAddress, 355 | }; 356 | 357 | usbredirparser_send_start_interrupt_receiving(priv.parser.get(), EP_PROCESS, &header); 358 | 359 | break; 360 | } 361 | default: 362 | perror("Not implemented"); 363 | } 364 | } 365 | } 366 | 367 | priv.poll_thread = std::thread{[&priv, &ffs, eps = std::move(poll_eps)] () 368 | { 369 | while(keep_running) 370 | { 371 | for(auto &ep : eps) 372 | { 373 | if(!keep_running) 374 | return; 375 | 376 | if(ffs.processingPaused(ep->desc.bEndpointAddress)) 377 | { 378 | using namespace std::chrono_literals; 379 | std::this_thread::sleep_for(5ms); 380 | 381 | continue; 382 | } 383 | 384 | usb_redir_bulk_packet_header header = { 385 | .endpoint = ep->desc.bEndpointAddress, 386 | .status = usb_redir_success, 387 | .length = uint16_t(ep->desc.wMaxPacketSize), 388 | .stream_id = EP_FORWARD, 389 | .length_high = uint16_t(0), 390 | }; 391 | 392 | usbredirparser_send_bulk_packet(priv.parser.get(), EP_FORWARD, &header, nullptr, 0); 393 | 394 | ffs.pauseProcessing(ep->desc.bEndpointAddress); 395 | 396 | // Main thread could hang inside select 397 | usbredirparser_do_write(priv.parser.get()); 398 | } 399 | } 400 | }}; 401 | } 402 | 403 | e = usbg_error(usbg_enable_gadget(priv.usbg.g, nullptr)); 404 | if(e != USBG_SUCCESS) 405 | fprintf(stderr, "usbg_enable_gadget: %s\n", usbg_strerror(e)); 406 | 407 | priv.state = UR2PPriv::FORWARDING; 408 | return true; 409 | } 410 | 411 | /* Parser callbacks */ 412 | #define DECL_PRIV UR2PPriv &priv = *reinterpret_cast(ppriv) 413 | 414 | void ur2p_log(void *, int, const char *msg) 415 | { 416 | debugPrintf("%s\n", msg); 417 | } 418 | 419 | int ur2p_read(void *ppriv, uint8_t *data, int count) 420 | { 421 | DECL_PRIV; 422 | 423 | ssize_t r = priv.con.read(data, size_t(count)); 424 | 425 | if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) 426 | return 0; 427 | 428 | return int(r); 429 | } 430 | 431 | int ur2p_write(void *ppriv, uint8_t *data, int count) 432 | { 433 | DECL_PRIV; 434 | 435 | ssize_t r = priv.con.write(data, size_t(count)); 436 | 437 | if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) 438 | return 0; 439 | 440 | return int(r); 441 | } 442 | 443 | void ur2p_device_connect(void *ppriv, struct usb_redir_device_connect_header *) 444 | { 445 | DECL_PRIV; 446 | 447 | if(priv.state != UR2PPriv::NO_IDEA) 448 | { 449 | fprintf(stderr, "Invalid state!\n"); 450 | return; 451 | } 452 | 453 | usb_redir_control_packet_header header = { 454 | .endpoint = USB_DIR_IN, 455 | .request = USB_REQ_GET_DESCRIPTOR, 456 | .requesttype = USB_DIR_IN, 457 | .status = usb_redir_success, 458 | .value = (USB_DT_DEVICE << 8) | USB_RECIP_DEVICE, 459 | .index = 0, 460 | .length = sizeof(usb_device_descriptor) 461 | }; 462 | 463 | usbredirparser_send_control_packet(priv.parser.get(), EP_PROCESS, &header, nullptr, 0); 464 | 465 | priv.state = UR2PPriv::DEV_DESC_QUERIED; 466 | } 467 | 468 | void ur2p_device_disconnect(void *) 469 | { 470 | keep_running = false; 471 | } 472 | 473 | /* Unused. Can't be nullptr in the callback pointer struct as usbredirparser does not check... */ 474 | void ur2p_interface_info(void *, struct usb_redir_interface_info_header *) {} 475 | void ur2p_ep_info(void *, struct usb_redir_ep_info_header *) {} 476 | void ur2p_configuration_status(void *ppriv, uint64_t id, struct usb_redir_configuration_status_header *config_status) {} 477 | void ur2p_alt_setting_status(void *ppriv, uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status) {} 478 | void ur2p_iso_stream_status(void *ppriv, uint64_t id, struct usb_redir_iso_stream_status_header *iso_stream_status) {} 479 | void ur2p_interrupt_receiving_status(void *ppriv, uint64_t id, struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status) {} 480 | void ur2p_bulk_receiving_status(void *ppriv, uint64_t id, struct usb_redir_bulk_receiving_status_header *bulk_streams_status) {} 481 | 482 | void ur2p_device_descriptor(UR2PPriv &priv, usb_device_descriptor *desc) 483 | { 484 | priv.device.desc = *desc; 485 | 486 | if(priv.device.desc.bDescriptorType != USB_DT_DEVICE) 487 | fprintf(stderr, "Not a device descriptor?\n"); 488 | else 489 | debugPrintf("Got device descriptor (%.4x:%.4x)\n", priv.device.desc.idVendor, priv.device.desc.idProduct); 490 | } 491 | 492 | void ur2p_config_descriptor(UR2PPriv &priv, uint8_t *data, int data_len) 493 | { 494 | usb_config_descriptor desc; 495 | memcpy(&desc, data, sizeof(desc)); 496 | data_len -= sizeof(usb_config_descriptor); 497 | data += sizeof(usb_config_descriptor); 498 | 499 | auto index = priv.device.configs.size(); 500 | priv.device.configs.push_back({}); 501 | 502 | USBConfiguration ¤t_conf = priv.device.configs[index]; 503 | if(current_conf.desc.bDescriptorType != 0) 504 | fprintf(stderr, "Config already seen!\n"); 505 | 506 | current_conf.desc = desc; 507 | current_conf.full_desc.insert(current_conf.full_desc.end(), data, data + data_len); 508 | 509 | if(desc.bDescriptorType != USB_DT_CONFIG) 510 | fprintf(stderr, "Not a config descriptor?\n"); 511 | else 512 | printf("Configuration %lu\n", index); 513 | 514 | /* Step through data to collect additional descriptors */ 515 | while(data_len > 0) 516 | { 517 | auto *d_type = reinterpret_cast(data); 518 | switch(d_type->bDescriptorType) 519 | { 520 | case USB_DT_INTERFACE: 521 | { 522 | if(unsigned(data_len) < sizeof(usb_interface_descriptor)) 523 | { 524 | fprintf(stderr, "Invalid descriptor size\n"); 525 | return; 526 | } 527 | 528 | usb_interface_descriptor idesc; 529 | memcpy(&idesc, data, sizeof(idesc)); 530 | 531 | USBInterface intf; 532 | intf.desc = idesc; 533 | current_conf.interfaces.emplace_back(std::move(intf)); 534 | 535 | printf("`Interface %d\n", idesc.bInterfaceNumber); 536 | break; 537 | } 538 | case USB_DT_ENDPOINT: 539 | { 540 | if(unsigned(data_len) < USB_DT_ENDPOINT_SIZE) 541 | { 542 | fprintf(stderr, "Invalid descriptor size\n"); 543 | return; 544 | } 545 | 546 | if(current_conf.interfaces.size() == 0) 547 | { 548 | fprintf(stderr, "Endpoint without interface?\n"); 549 | return; 550 | } 551 | 552 | usb_endpoint_descriptor edesc; 553 | memcpy(&edesc, data, sizeof(edesc)); 554 | 555 | /* Add endpoint */ 556 | priv.device.endpoints[epAddrToIndex(edesc.bEndpointAddress)].desc = edesc; 557 | 558 | /* Add endpoint to interface */ 559 | current_conf.interfaces.back().endpoints.push_back(edesc.bEndpointAddress); 560 | 561 | printf("\t`Endpoint 0x%.2x\n", edesc.bEndpointAddress); 562 | break; 563 | } 564 | case 0x21: 565 | printf("\t`HID descriptor\n"); 566 | break; 567 | default: 568 | fprintf(stderr, "Unhandled case 0x%.2x\n", d_type->bDescriptorType); 569 | break; 570 | } 571 | 572 | if(data_len < d_type->bLength) 573 | { 574 | fprintf(stderr, "Invalid bLength!\n"); 575 | return; 576 | } 577 | 578 | data_len -= d_type->bLength; 579 | data += d_type->bLength; 580 | } 581 | } 582 | 583 | void ur2p_string_descriptor(UR2PPriv &priv, usb_string_descriptor *desc, size_t size, uint64_t id) 584 | { 585 | if(desc->bDescriptorType != USB_DT_STRING) 586 | fprintf(stderr, "Not a string descriptor!\n"); 587 | 588 | size_t bytes = size - reinterpret_cast(&(reinterpret_cast(0)->wData)); 589 | std::u16string string{reinterpret_cast(desc->wData), bytes / 2}; 590 | priv.device.strings.strings[uint32_t(id)] = string; 591 | } 592 | 593 | void ur2p_str0_descriptor(UR2PPriv &priv, usb_string_descriptor *desc, size_t size) 594 | { 595 | if(desc->bDescriptorType != USB_DT_STRING) 596 | fprintf(stderr, "Not a string descriptor!\n"); 597 | 598 | /* Save available lang ids */ 599 | size -= reinterpret_cast(&(reinterpret_cast(0)->wData)); 600 | for(unsigned int i = 0; i < size / 2; ++i) 601 | priv.device.strings.langs.insert(desc->wData[i]); 602 | } 603 | 604 | void ur2p_control_packet(void *ppriv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len) 605 | { 606 | DECL_PRIV; 607 | 608 | /* I don't understand the use of signed sizes... */ 609 | assert(data_len >= 0); 610 | 611 | /* If the ID matches or we queried a string descriptor the packet is ours */ 612 | if(id == EP_PROCESS || priv.state == UR2PPriv::STR_DESC_QUERIED) 613 | { 614 | /* Process input */ 615 | if(priv.state == UR2PPriv::DEV_DESC_QUERIED) 616 | { 617 | if(size_t(data_len) != sizeof(usb_device_descriptor)) 618 | { 619 | fprintf(stderr, "Invalid control packet len.\n"); 620 | return; 621 | } 622 | 623 | ur2p_device_descriptor(priv, reinterpret_cast(data)); 624 | } 625 | else if(priv.state == UR2PPriv::CONF_DESC_QUERIED) 626 | { 627 | if(size_t(data_len) < sizeof(usb_config_descriptor)) 628 | { 629 | fprintf(stderr, "Invalid control packet len.\n"); 630 | return; 631 | } 632 | 633 | ur2p_config_descriptor(priv, data, data_len); 634 | } 635 | else if(priv.state == UR2PPriv::STR0_DESC_QUERIED) 636 | { 637 | if(size_t(data_len) < sizeof(usb_string_descriptor)) 638 | { 639 | fprintf(stderr, "Invalid control packet len.\n"); 640 | return; 641 | } 642 | 643 | ur2p_str0_descriptor(priv, reinterpret_cast(data), size_t(data_len)); 644 | } 645 | else if(priv.state == UR2PPriv::STR_DESC_QUERIED) 646 | { 647 | if(size_t(data_len) < sizeof(usb_string_descriptor)) 648 | { 649 | fprintf(stderr, "Invalid control packet len.\n"); 650 | return; 651 | } 652 | 653 | ur2p_string_descriptor(priv, reinterpret_cast(data), size_t(data_len), id); 654 | } 655 | 656 | /* Generate output */ 657 | 658 | // Need to fetch config descriptors? 659 | if(priv.device.configs.size() != priv.device.desc.bNumConfigurations) 660 | { 661 | usb_redir_control_packet_header header = { 662 | .endpoint = USB_DIR_IN, 663 | .request = USB_REQ_GET_DESCRIPTOR, 664 | .requesttype = USB_DIR_IN, 665 | .status = usb_redir_success, 666 | .value = (USB_DT_CONFIG << 8) | USB_RECIP_DEVICE, 667 | .index = uint16_t(priv.device.configs.size()), 668 | .length = USB_MAX_CTRL_SIZE 669 | }; 670 | 671 | usbredirparser_send_control_packet(priv.parser.get(), EP_PROCESS, &header, nullptr, 0); 672 | 673 | priv.state = UR2PPriv::CONF_DESC_QUERIED; 674 | return; 675 | } 676 | else if(priv.state == UR2PPriv::CONF_DESC_QUERIED) 677 | { 678 | /* Last config descriptor arrived, query STR0 descriptor for LANGID list */ 679 | 680 | usb_redir_control_packet_header header = { 681 | .endpoint = USB_DIR_IN, 682 | .request = USB_REQ_GET_DESCRIPTOR, 683 | .requesttype = USB_DIR_IN, 684 | .status = usb_redir_success, 685 | .value = (USB_DT_STRING << 8) | 0, 686 | .index = 0, 687 | .length = USB_MAX_CTRL_SIZE 688 | }; 689 | 690 | usbredirparser_send_control_packet(priv.parser.get(), EP_PROCESS, &header, nullptr, 0); 691 | 692 | priv.state = UR2PPriv::STR0_DESC_QUERIED; 693 | return; 694 | } 695 | 696 | if(priv.state == UR2PPriv::STR0_DESC_QUERIED) 697 | { 698 | /* Got STR0 descriptor, make a list of all referenced strings */ 699 | 700 | std::set strings; 701 | /* Referenced by device descriptor */ 702 | strings.insert({priv.device.desc.iManufacturer, priv.device.desc.iProduct, priv.device.desc.iSerialNumber}); 703 | /* Referenced by config descriptors */ 704 | for(auto &c : priv.device.configs) 705 | { 706 | strings.insert(c.desc.iConfiguration); 707 | /* Referenced by interface descriptors */ 708 | for(auto &i : c.interfaces) 709 | strings.insert(i.desc.iInterface); 710 | } 711 | 712 | for(auto i : strings) 713 | { 714 | if(i == 0) 715 | continue; 716 | for(auto langid : priv.device.strings.langs) 717 | priv.missing_strings.insert(makeString(langid, i)); 718 | } 719 | } 720 | 721 | // Need to fetch string descriptors? 722 | if(!priv.missing_strings.empty()) 723 | { 724 | auto end = priv.missing_strings.end(); 725 | --end; 726 | uint32_t string = *end; 727 | priv.missing_strings.erase(end); 728 | 729 | usb_redir_control_packet_header header = { 730 | .endpoint = USB_DIR_IN, 731 | .request = USB_REQ_GET_DESCRIPTOR, 732 | .requesttype = USB_DIR_IN, 733 | .status = usb_redir_success, 734 | .value = uint16_t((USB_DT_STRING << 8) | stringIndex(string)), 735 | .index = stringLangID(string), 736 | .length = USB_MAX_CTRL_SIZE 737 | }; 738 | 739 | usbredirparser_send_control_packet(priv.parser.get(), string, &header, nullptr, 0); 740 | 741 | priv.state = UR2PPriv::STR_DESC_QUERIED; 742 | return; 743 | } 744 | 745 | /* We got everything! Let's configure and boot up the gadget */ 746 | if(!gadget_init(priv)) 747 | keep_running = false; 748 | } 749 | else 750 | fprintf(stderr, "Got packet with unexpected id 0x%.8lx\n", id); 751 | } 752 | 753 | template 754 | void ur2p_packet_handler(void *ppriv, uint64_t id, T *packet, uint8_t *data, int data_len) 755 | { 756 | DECL_PRIV; 757 | 758 | debugPrintf("Device: Received %s (%d bytes)\n", typeid(T).name(), data_len); 759 | 760 | if(packet->status != usb_redir_success) 761 | { 762 | fprintf(stderr, "Packet error\n"); 763 | return; 764 | } 765 | 766 | if(priv.state == UR2PPriv::FORWARDING) 767 | { 768 | /* Forward packets */ 769 | if(packet->endpoint & USB_DIR_IN) 770 | { 771 | if(!priv.ffs[0]->writePacketEP(packet->endpoint, data, data_len)) 772 | fprintf(stderr, "Packet send failed\n"); 773 | } 774 | 775 | priv.ffs[0]->resumeProcessing(packet->endpoint); 776 | } 777 | else 778 | fprintf(stderr, "Got unexpected packet with id 0x%.8lx\n", id); 779 | 780 | if(data) 781 | usbredirparser_free_packet_data(priv.parser.get(), data); 782 | } 783 | 784 | template <> 785 | void ur2p_packet_handler(void *ppriv, uint64_t id, usb_redir_control_packet_header *packet, uint8_t *data, int data_len) 786 | { 787 | DECL_PRIV; 788 | 789 | debugPrintf("Device: Received %s (%d bytes)\n", typeid(usb_redir_control_packet_header).name(), data_len); 790 | 791 | if(packet->status != usb_redir_success) 792 | { 793 | fprintf(stderr, "Packet error\n"); 794 | return; 795 | } 796 | 797 | if(id == EP_FORWARD && priv.state == UR2PPriv::FORWARDING) 798 | { 799 | /* Forward packets */ 800 | if(packet->endpoint & USB_DIR_IN) 801 | { 802 | if(!priv.ffs[0]->writePacketEP(packet->endpoint, data, data_len)) 803 | fprintf(stderr, "Packet send failed\n"); 804 | } 805 | 806 | /* Control packets can go in both directions */ 807 | priv.ffs[0]->resumeProcessing(packet->endpoint | USB_DIR_IN); 808 | } 809 | else 810 | ur2p_control_packet(ppriv, id, packet, data, data_len); 811 | 812 | if(data) 813 | usbredirparser_free_packet_data(priv.parser.get(), data); 814 | } 815 | 816 | void *ur2p_alloc_lock() 817 | { 818 | return new std::mutex; 819 | } 820 | 821 | void ur2p_lock(void *lock) 822 | { 823 | reinterpret_cast(lock)->lock(); 824 | } 825 | 826 | void ur2p_unlock(void *lock) 827 | { 828 | reinterpret_cast(lock)->unlock(); 829 | } 830 | 831 | void ur2p_free_lock(void *lock) 832 | { 833 | delete reinterpret_cast(lock); 834 | } 835 | 836 | void ur2p_hello(void *, struct usb_redir_hello_header *header) 837 | { 838 | printf("Connected to %.64s.\n", header->version); 839 | } 840 | 841 | int main(int argc, char **argv) 842 | try { 843 | UR2PPriv priv; 844 | 845 | priv.state = UR2PPriv::NO_IDEA; 846 | priv.usbg.s = nullptr; 847 | priv.usbg.g = nullptr; 848 | 849 | /* Parse options */ 850 | char *endptr = nullptr; 851 | unsigned long port = 0; 852 | const char *hostname; 853 | 854 | if(argc < 3) 855 | goto usage; 856 | 857 | hostname = argv[1]; 858 | port = strtoul(argv[2], &endptr, 10); 859 | 860 | if(port == 0 || port > 65535 || *endptr != 0) 861 | { 862 | usage: 863 | fprintf(stderr, "Usage: %s \n", argv[0]); 864 | return 1; 865 | } 866 | 867 | setup_signals(); 868 | 869 | if(!priv.con.connect(hostname, uint16_t(port))) 870 | { 871 | fprintf(stderr, "Could not connect to %s:%lu\n", hostname, port); 872 | return 1; 873 | } 874 | 875 | int flags = fcntl(priv.con.fd, F_GETFL); 876 | if(flags == -1 || fcntl(priv.con.fd, F_SETFL, flags | O_NONBLOCK) == -1) 877 | { 878 | perror("Unable to set O_NONBLOCK"); 879 | return 1; 880 | } 881 | 882 | auto ret = usbg_error(usbg_init("/sys/kernel/config", &priv.usbg.s)); 883 | if(ret != USBG_SUCCESS) 884 | { 885 | fprintf(stderr, "Could not initialize usbg: %s\n", usbg_strerror(ret)); 886 | fprintf(stderr, "Make sure configfs is mounted at /sys/kernel/config and the libcomposite kernel module is loaded.\n"); 887 | return 1; 888 | } 889 | 890 | priv.parser = scope_ptr(usbredirparser_create(), usbredirparser_destroy); 891 | 892 | if(!priv.parser) 893 | { 894 | fprintf(stderr, "Could not create parser.\n"); 895 | return 1; 896 | } 897 | 898 | usbredirparser *parser = priv.parser.get(); 899 | 900 | parser->priv = &priv; 901 | parser->log_func = ur2p_log; 902 | parser->read_func = ur2p_read; 903 | parser->write_func = ur2p_write; 904 | parser->device_connect_func = ur2p_device_connect; 905 | parser->device_disconnect_func = ur2p_device_disconnect; 906 | parser->interface_info_func = ur2p_interface_info; 907 | parser->ep_info_func = ur2p_ep_info; 908 | parser->configuration_status_func = ur2p_configuration_status; 909 | parser->alt_setting_status_func = ur2p_alt_setting_status; 910 | parser->iso_stream_status_func = ur2p_iso_stream_status; 911 | parser->interrupt_receiving_status_func = ur2p_interrupt_receiving_status; 912 | parser->bulk_receiving_status_func= ur2p_bulk_receiving_status; 913 | parser->control_packet_func = ur2p_packet_handler; 914 | parser->bulk_packet_func = ur2p_packet_handler; 915 | parser->iso_packet_func = ur2p_packet_handler; 916 | parser->interrupt_packet_func = ur2p_packet_handler; 917 | parser->alloc_lock_func = ur2p_alloc_lock; 918 | parser->lock_func = ur2p_lock; 919 | parser->unlock_func = ur2p_unlock; 920 | parser->free_lock_func = ur2p_free_lock; 921 | parser->hello_func = ur2p_hello; 922 | 923 | uint32_t caps[USB_REDIR_CAPS_SIZE] = {0}; 924 | 925 | // fdo#99015 926 | //usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); 927 | usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); 928 | usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); 929 | 930 | usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); 931 | 932 | usbredirparser_init(parser, "UR2P 0.-1", caps, USB_REDIR_CAPS_SIZE, 0); 933 | 934 | puts("Initialized."); 935 | 936 | /* We need the connect packet first. */ 937 | usbredirparser_send_reset(parser); 938 | usbredirparser_send_get_configuration(parser, 0); 939 | 940 | while(keep_running && priv.con.fd != -1) 941 | { 942 | fd_set rfds, wfds; 943 | FD_ZERO(&rfds); 944 | FD_ZERO(&wfds); 945 | 946 | int highest = priv.con.fd; 947 | 948 | FD_SET(priv.con.fd, &rfds); 949 | if(usbredirparser_has_data_to_write(parser)) 950 | FD_SET(priv.con.fd, &wfds); 951 | 952 | for(auto &ffs : priv.ffs) 953 | { 954 | int ep0_fd = ffs->getEP0FD(); 955 | FD_SET(ep0_fd, &rfds); 956 | highest = std::max(ep0_fd, highest); 957 | } 958 | 959 | if(select(highest + 1, &rfds, &wfds, nullptr, nullptr) == -1) 960 | { 961 | if(errno == EINTR) 962 | continue; 963 | 964 | perror("select"); 965 | break; 966 | } 967 | 968 | for(auto &ffs : priv.ffs) 969 | { 970 | if(!ffs->handleEP0Data()) 971 | keep_running = false; 972 | } 973 | 974 | if(FD_ISSET(priv.con.fd, &rfds) && usbredirparser_do_read(parser) != 0) 975 | { 976 | fprintf(stderr, "Read error.\n"); 977 | break; 978 | } 979 | 980 | if(FD_ISSET(priv.con.fd, &wfds) && usbredirparser_do_write(parser) != 0) 981 | { 982 | fprintf(stderr, "Write error.\n"); 983 | break; 984 | } 985 | } 986 | 987 | printf("Shutting down....\n"); 988 | return 0; 989 | } 990 | catch(...) { return 1; } 991 | -------------------------------------------------------------------------------- /usbredir2phys.creator: -------------------------------------------------------------------------------- 1 | [General] 2 | -------------------------------------------------------------------------------- /usbredir2phys.files: -------------------------------------------------------------------------------- 1 | Makefile 2 | usbfunctionfs.cpp 3 | usbfunctionfs.h 4 | usbredir2phys.ctempl 5 | usbredir2phys.cpp 6 | usb.h 7 | usbdevice.h 8 | -------------------------------------------------------------------------------- /usbredir2phys.includes: -------------------------------------------------------------------------------- 1 | . 2 | /usr/include/libusbgx 3 | /usr/include 4 | --------------------------------------------------------------------------------