Last active
December 30, 2022 20:46
-
-
Save jgottula/db6a50485fa43a21dff8733ed2343ab4 to your computer and use it in GitHub Desktop.
libusb_wrapper.cpp: “wow, this could use some serious boilerplate reduction”
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HOW TO BUILD: | |
// g++ -std=c++17 -fPIC -shared -Wl,--no-undefined -fuse-ld=gold -ldl -lbsd -lusb-1.0 -Wall -O0 -g3 -o libusb_wrapper.so libusb_wrapper.cpp | |
// HOW TO USE: | |
// LD_PRELOAD=libusb_wrapper.so <program command line> | |
#ifndef _GNU_SOURCE | |
#define _GNU_SOURCE | |
#endif | |
#include <dlfcn.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <inttypes.h> | |
#include <stdarg.h> | |
#include <unistd.h> | |
#include <sys/syscall.h> | |
#include <bsd/string.h> | |
#include <libusb-1.0/libusb.h> | |
#include <mutex> | |
#include <map> | |
#ifdef __cplusplus | |
#define libusb_strerror(errcode) libusb_strerror(static_cast<enum libusb_error>(errcode)) | |
#endif | |
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | |
enum | |
{ | |
C_BRIGHT_RED = 91, | |
C_BRIGHT_GREEN = 92, | |
C_BRIGHT_YELLOW = 93, | |
C_BRIGHT_BLUE = 94, | |
C_BRIGHT_MAGENTA = 95, | |
C_BRIGHT_CYAN = 96, | |
}; | |
static void msg(int color, const char *fmt, ...) __attribute__((format(printf, 2, 3))); | |
static void msg(int color, const char *fmt, ...) | |
{ | |
char fmt_color[8192]; | |
snprintf(fmt_color, sizeof(fmt_color), "\e[%dm" "%s" "\e[0m", color, fmt); | |
va_list va; | |
va_start(va, fmt); | |
vfprintf(stderr, fmt_color, va); | |
va_end(va); | |
} | |
#define SETUP(fname) \ | |
do { \ | |
if (REAL__##fname == NULL) { \ | |
REAL__##fname = reinterpret_cast<decltype(REAL__##fname)>(dlsym(RTLD_NEXT, #fname)); \ | |
if (REAL__##fname == NULL) { \ | |
msg(C_BRIGHT_RED, "libusb_wrapper: dlsym(RTLD_NEXT, " #fname ") returned NULL! dlerror() says: \"%s\"\n", dlerror()); \ | |
exit(1); \ | |
} \ | |
} \ | |
} while (false) | |
static int (*REAL__libusb_clear_halt)(libusb_device_handle *dev_handle, unsigned char endpoint) = NULL; | |
extern "C" int libusb_clear_halt(libusb_device_handle *dev_handle, unsigned char endpoint) | |
{ | |
SETUP(libusb_clear_halt); | |
msg(C_BRIGHT_CYAN, "[libusb_clear_halt:PRE] dev_handle=%p endpoint=0x%02X\n", | |
dev_handle, endpoint); | |
int retval = REAL__libusb_clear_halt(dev_handle, endpoint); | |
msg(C_BRIGHT_CYAN, "[libusb_clear_halt:POST] retval={ %d %s \"%s\" }\n", | |
retval, libusb_error_name(retval), libusb_strerror(retval)); | |
return retval; | |
} | |
static int (*REAL__libusb_bulk_transfer)(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout) = NULL; | |
extern "C" int libusb_bulk_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout) | |
{ | |
SETUP(libusb_bulk_transfer); | |
msg(C_BRIGHT_MAGENTA, "[libusb_bulk_transfer:PRE] endpoint=0x%02X data=%p length=0x%X timeout=%d\n", | |
endpoint, data, length, timeout); | |
int retval = REAL__libusb_bulk_transfer(dev_handle, endpoint, data, length, actual_length, timeout); | |
msg(C_BRIGHT_MAGENTA, "[libusb_bulk_transfer:POST] retval={ %d %s \"%s\" } *actual_length=0x%X\n", | |
retval, libusb_error_name(retval), libusb_strerror(retval), *actual_length); | |
return retval; | |
} | |
static void transfer_flags_str(struct libusb_transfer *transfer, char *buf, size_t size) | |
{ | |
if (transfer->flags == 0) { | |
strlcpy(buf, "<none>", size); | |
return; | |
} | |
buf[0] = '\x00'; | |
int num = 0; | |
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) != 0) { | |
if (num++ != 0) strlcat(buf, "|", size); | |
strlcat(buf, "SHORT_NOT_OK", size); | |
} | |
if ((transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) != 0) { | |
if (num++ != 0) strlcat(buf, "|", size); | |
strlcat(buf, "FREE_BUFFER", size); | |
} | |
if ((transfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) != 0) { | |
if (num++ != 0) strlcat(buf, "|", size); | |
strlcat(buf, "FREE_TRANSFER", size); | |
} | |
if ((transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) != 0) { | |
if (num++ != 0) strlcat(buf, "|", size); | |
strlcat(buf, "ADD_ZERO_PACKET", size); | |
} | |
} | |
#define TRANSFER_TYPE_STR_HELPER(type) \ | |
case LIBUSB_TRANSFER_TYPE_##type: strlcpy(buf, #type, size); break | |
static void transfer_type_str(struct libusb_transfer *transfer, char *buf, size_t size) | |
{ | |
switch (transfer->type) { | |
TRANSFER_TYPE_STR_HELPER(CONTROL); | |
TRANSFER_TYPE_STR_HELPER(ISOCHRONOUS); | |
TRANSFER_TYPE_STR_HELPER(BULK); | |
TRANSFER_TYPE_STR_HELPER(INTERRUPT); | |
TRANSFER_TYPE_STR_HELPER(BULK_STREAM); | |
default: strlcat(buf, "???", size); break; | |
} | |
} | |
#define TRANSFER_STATUS_STR_HELPER(status) \ | |
case LIBUSB_TRANSFER_##status: strlcpy(buf, #status, size); break | |
static void transfer_status_str(struct libusb_transfer *transfer, char *buf, size_t size) | |
{ | |
switch (transfer->status) { | |
TRANSFER_STATUS_STR_HELPER(COMPLETED); | |
TRANSFER_STATUS_STR_HELPER(ERROR); | |
TRANSFER_STATUS_STR_HELPER(TIMED_OUT); | |
TRANSFER_STATUS_STR_HELPER(CANCELLED); | |
TRANSFER_STATUS_STR_HELPER(STALL); | |
TRANSFER_STATUS_STR_HELPER(NO_DEVICE); | |
TRANSFER_STATUS_STR_HELPER(OVERFLOW); | |
default: strlcat(buf, "???", size); break; | |
} | |
} | |
// I'm generally worried about these various asynchronous things being called | |
// from different threads, so I'll use this BIG FAT MUTEX to allay those fears | |
static std::recursive_mutex big_fat_mutex; | |
static std::map<struct libusb_transfer *, libusb_transfer_cb_fn> callbacks; | |
static void xfer_cbfunc_shim(struct libusb_transfer *transfer) | |
{ | |
// uhhhhh I really hope that the callback isn't called internally via | |
// libusb_submit_transfer or libusb_cancel_transfer, because that will make | |
// this deadlock... | |
// AHA! this is what they invented std::recursive_mutex for! | |
std::lock_guard<std::recursive_mutex> protector(big_fat_mutex); | |
// if this lookup fails, it will throw std::out_of_range | |
libusb_transfer_cb_fn real_cbfunc = callbacks.at(transfer); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_YELLOW, "[libusb_transfer->callback:PRE] TID=%ld transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p }\n", | |
syscall(SYS_gettid), transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer); | |
} | |
(*real_cbfunc)(transfer); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_YELLOW, "[libusb_transfer->callback:POST] TID=%ld transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p }\n", | |
syscall(SYS_gettid), transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer); | |
} | |
} | |
static int (*REAL__libusb_submit_transfer)(struct libusb_transfer *transfer) = NULL; | |
extern "C" int libusb_submit_transfer(struct libusb_transfer *transfer) | |
{ | |
SETUP(libusb_submit_transfer); | |
std::lock_guard<std::recursive_mutex> protector(big_fat_mutex); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_GREEN, "[libusb_submit_transfer:PRE] transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p }\n", | |
transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer); | |
} | |
callbacks.emplace(transfer, transfer->callback); | |
transfer->callback = &xfer_cbfunc_shim; | |
int retval = REAL__libusb_submit_transfer(transfer); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_GREEN, "[libusb_submit_transfer:POST] transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p } retval={ %d %s \"%s\" }\n", | |
transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer, retval, libusb_error_name(retval), libusb_strerror(retval)); | |
} | |
return retval; | |
} | |
static int (*REAL__libusb_cancel_transfer)(struct libusb_transfer *transfer) = NULL; | |
extern "C" int libusb_cancel_transfer(struct libusb_transfer *transfer) | |
{ | |
SETUP(libusb_cancel_transfer); | |
std::lock_guard<std::recursive_mutex> protector(big_fat_mutex); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_RED, "[libusb_cancel_transfer:PRE] transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p }\n", | |
transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer); | |
} | |
int retval = REAL__libusb_cancel_transfer(transfer); | |
{ | |
char buf_flags [1024]; transfer_flags_str (transfer, buf_flags, sizeof(buf_flags)); | |
char buf_type [1024]; transfer_type_str (transfer, buf_type, sizeof(buf_type)); | |
char buf_status[1024]; transfer_status_str(transfer, buf_status, sizeof(buf_status)); | |
msg(C_BRIGHT_RED, "[libusb_cancel_transfer:POST] transfer=%p { flags={ 0x%02X %s } endpoint=0x%02X type={ 0x%X %s } timeout=%d status={ 0x%X %s } length=%d actual_length=%d buffer=%p } retval={ %d %s \"%s\" }\n", | |
transfer, transfer->flags, buf_flags, transfer->endpoint, transfer->type, buf_type, transfer->timeout, transfer->status, buf_status, transfer->length, transfer->actual_length, transfer->buffer, retval, libusb_error_name(retval), libusb_strerror(retval)); | |
} | |
return retval; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment