diff --git a/README.md b/README.md index 382c841..551f2ae 100644 --- a/README.md +++ b/README.md @@ -65,5 +65,10 @@ cp -R TL866_Updater.app /Applications ``` ### InfoicDump utility -This utility can dump the `infoic.dll` and `infoic2plus.dll` to an XML format database. +This utility can dump the `infoic.dll` and `infoic2plus.dll` to an XML format database. More information on this here: [InfoicDump](https://github.com/radiomanV/TL866/tree/master/InfoIcDump#infoicdump) + + +### WineLib wrapper +This library provide an easy way to make Minipro/Xgpro software work in Linux. +More information on this here: [Wine](https://github.com/radiomanV/TL866/tree/master/wine#) diff --git a/udev/60-minipro.rules b/udev/60-minipro.rules index 6a7361e..1e83cee 100644 --- a/udev/60-minipro.rules +++ b/udev/60-minipro.rules @@ -23,7 +23,10 @@ SUBSYSTEM!="usb", GOTO="minipro_rules_end" # TL866A/CS ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="e11c", ENV{ID_MINIPRO}="1" -# TL866II+ +# TL866II+, T48, T56 ATTRS{idVendor}=="a466", ATTRS{idProduct}=="0a53", ENV{ID_MINIPRO}="1" +# T76 +ATTRS{idVendor}=="a466", ATTRS{idProduct}=="1a86", ENV{ID_MINIPRO}="1" + LABEL="minipro_rules_end" diff --git a/wine/Makefile b/wine/Makefile index c93b68e..1300186 100644 --- a/wine/Makefile +++ b/wine/Makefile @@ -2,9 +2,15 @@ SRCDIR = . DLL = setupapi.dll ### Common settings -CFLAGS = +CFLAGS = CEXTRA = -m32 -mincoming-stack-boundary=2 -LIBRARIES = -lusb-1.0 -ludev +LIBRARIES = -lusb-1.0 + +ifeq ($(hotplug),udev) +CFLAGS += -DUDEV +LIBRARIES += -ludev +endif + ### setupapi.dll sources and settings diff --git a/wine/readme b/wine/readme deleted file mode 100644 index 82c0672..0000000 --- a/wine/readme +++ /dev/null @@ -1,21 +0,0 @@ -Simple low level winelib usb wrapper for the TL866A/CS, TL866II+ and Xgecu T56 programmers -This version will autodetect the software used. - -Add the following rule to the udev subsystem: -sudo cp ./udev/* /etc/udev/rules.d/ && sudo udevadm trigger - - -How to install: -1. Install wine, libusb -2. Copy the provided setupapi.dll file in the Minipro or Xgpro folder -3. Run the Minipro.exe or Xgpro.exe - - -How to compile: -1. Install wine, wine-devel, libusb-devel, libudev-devel packages -2. Run make -3. Rename the setup.dll.so file as setupapi.dll and copy this file in the Minipro/Xgpro folder - -You should install the 32 bit version of the libusb library (Debian users libusb-1.0-0:i386, Arch users lib32-libusb) -To compile the debug version pass the DBG define to make like this: make CFLAGS=-DDBG. This will print to console all the usb communication. - diff --git a/wine/readme.md b/wine/readme.md new file mode 100644 index 0000000..13f9a98 --- /dev/null +++ b/wine/readme.md @@ -0,0 +1,48 @@ +## Simple low level winelib usb wrapper for the Minipro TL866A/CS, TL866II+, Xgecu T48, T56 and T76 programmers. + + +#### Installing the udev rules: +Add the following rules to the udev subsystem: +```nohighlight +sudo cp ./udev/* /etc/udev/rules.d/ && sudo udevadm control --reload-rules && sudo udevadm trigger +``` +This will grant you permission to run the software as regular user, otherwise you must be a superuser. + +#### How to install: +1. Install `wine`, `libusb` and `libudev` packages +You should install the 32 bit version of `LibUsb` and `libudev` and create a 32 bit wine prefix: + +```nohighlight +sudo apt install libusb-1.0-0:i386 libudev1:i386 +WINEPREFIX="$HOME/wine32" WINEARCH=win32 wine wineboot +``` + +2. Copy the provided setupapi.dll file in the Minipro or Xgpro folder + +3. Run the `Minipro.exe`, `Xgpro.exe` or `Xgpro_T76.exe` using `wine`: +```nohighlight +WINEPREFIX="$HOME/wine32" wine ./Xgpro.exe 2>/dev/null +``` +If you already have a default 32 bit `wine` prefix located in `$HOME/.wine` you can use it without creating a new one: +```nohighlight +wine ./Xgpro.exe 2>/dev/null +``` + +#### How to compile: +1. Install `wine`, `wine-devel`, `libusb-1.0-0-dev:i386`, `libudev-dev:i386` packages + +2. Run `make hotplug=udev` to compile the `setupapi.dll` using `udev` library for hotplug notifications subsystem. +Running only `make` will compile the `setupapi.dll` using `libusb` library for handling hotplug events which can +be useful if `udev` subsystem is not available in your OS. + +4. Rename the compiled `setupapi.dll.so` file as `setupapi.dll` and copy this file in the Minipro/Xgpro folder + + +#### Debugging: +Running this in a terminal session: +```nohighligh +TL_DEBUG=1 wine ./Xgpro.exe or TL_DEBUG=1 wine ./Xgpro.exe 2>/dev/null +``` +will dump all usb communication to the console which can be very useful when something goes wrong. +The `2>/dev/null` will redirect the `stderr` to `/dev/null` thus cancelling the wine debugging messages which can be +very annoying sometime. diff --git a/wine/setupapi.c b/wine/setupapi.c index 77640b7..732c1d1 100644 --- a/wine/setupapi.c +++ b/wine/setupapi.c @@ -1,32 +1,49 @@ +/* + * setupapi.c + * Winelib wrapper for Minipro TL866A/CS, TL866II+, XgPro T48, T56 and T76 + * programmers. + * This library will redirect all USB related functions from Minipro or Xgpro + * software to the Linux USB subsystem using the standard LibUsb library. + * Created: May 5, 2014 + * Author: radiomanV + */ + #define __WINESRC__ #define __CYGWIN__ #define _GNU_SOURCE #include +#ifdef UDEV #include +#endif #include +#include #include #include #include #include #include -#include - #include #include #include #include +// Defines #define TL866A_VID 0x04d8 #define TL866A_PID 0xe11c -#define TL866II_VID 0xA466 -#define TL866II_PID 0x0A53 -#define T76_PID 0x1A86 -#define T76_VID 0xA466 +#define TL866II_VID 0xa466 +#define TL866II_PID 0x0a53 +#define T76_VID 0xa466 +#define T76_PID 0x1a86 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define X86_PUSH 0x68 +#define X86_RET 0xc3 +#define X86_JMP 0xeb +// Typedefs typedef struct { - HANDLE InterfaceHandle; + libusb_device_handle *handle; UCHAR PipeID; PUCHAR Buffer; ULONG BufferLength; @@ -34,34 +51,49 @@ typedef struct { LPOVERLAPPED Overlapped; } Args; +typedef struct { + struct libusb_transfer *transfer; + int timeout; +} Endpoint; + +typedef BOOL(WINAPI *pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); +typedef HWND(WINAPI *pGetForegroundWindow)(); +typedef LRESULT(WINAPI *pSendMessageA)(HWND, UINT, WPARAM, LPARAM); +typedef BOOL(WINAPI *pRedrawWindow)(HWND, const RECT *, HRGN, UINT); + +// Notification interfaces +const GUID MINIPRO_GUID = + {0x85980D83, 0x32B9, 0x4BA1, {0x8F, 0xDF, 0x12, 0xA7, 0x11, 0xB9, 0x9C, 0xA2}}; +const GUID XGPRO_GUID1 = + {0xE7E8BA13, 0x2A81, 0x446E, {0xA1, 0x1E, 0x72, 0x39, 0x8F, 0xBD, 0xA8, 0x2F}}; +const GUID XGPRO_GUID2 = + {0x015DE341, 0x91CC, 0x8286, {0x39, 0x64, 0x1A, 0x00, 0x6B, 0xC1, 0xF0, 0x0F}}; + // Global variables -libusb_device_handle *device_handle[4]; -libusb_device **devs; int debug = 0; - -HANDLE h_thread; - -HWND hWnd; BOOL cancel; + +libusb_device **devs = NULL; +libusb_device_handle *device_handle[4]; +Endpoint endpoints[2][7]; +unsigned short device_vid; +unsigned short device_pid; +GUID m_guid; + HANDLE *usb_handle; HANDLE *winusb_handle; int *devices_count; -GUID m_guid; -unsigned short device_vid; -unsigned short device_pid; - -typedef BOOL(__stdcall *pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); -typedef HWND(__stdcall *pGetForegroundWindow)(); -typedef LRESULT(__stdcall *pSendMessageA)(HWND, UINT, WPARAM, LPARAM); -typedef BOOL(__stdcall *pRedrawWindow)(HWND, const RECT *, HRGN, UINT); +HANDLE hotplug_thread; +HWND hWnd; pMessageBoxA message_box; pGetForegroundWindow get_foreground_window; pSendMessageA send_message; pRedrawWindow redraw_window; -pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER; +void device_changed(unsigned int); // These are functions signature extracted from Xgpro.exe and should be // compatible from V7.0 and above. @@ -70,45 +102,43 @@ const unsigned char xgpro_open_devices_pattern1[] = { 0x6A, 0x00, 0x6A, 0x03, 0x68, 0x00, 0x00, 0x00, 0xC0, 0x68}; const unsigned char xgpro_open_devices_pattern2[] = { - 0x6A, 0x00, 0x68, 0x80, 0x00, 0x00, 0x40, 0x6A, 0x03, 0x6A, - 0x00, 0x6A, 0x03, 0x68, 0x00, 0x00, 0x00, 0xC0, 0x51}; + 0x6A, 0x00, 0x68, 0x80, 0x00, 0x00, 0x40, 0x6A, 0x03, 0x6A, 0x00, + 0x6A, 0x03, 0x68, 0x00, 0x00, 0x00, 0xC0, 0x51}; // These are functions signature extracted from MiniPro.exe and should be // compatible from V6.0 and above. const unsigned char minipro_open_devices_pattern[] = { - 0x6A, 0x00, 0x68, 0x80, 0x00, 0x00, 0x00, - 0x6A, 0x03, 0x6A, 0x00, 0x6A, 0x03}; -const unsigned char usb_write_pattern[] = {0x8B, 0x94, 0x24, 0x0C, 0x10, 0x00, - 0x00, 0x8D, 0x44, 0x24, 0x00, 0x6A, - 0x00, 0x50, 0x8B, 0x84}; -const unsigned char usb_write2_pattern[] = {0x8B, 0x94, 0x24, 0x10, 0x10, 0x00, - 0x00, 0x8D, 0x44, 0x24, 0x00, 0x6A, - 0x00, 0x50, 0x8B, 0x84}; -const unsigned char usb_read_pattern[] = {0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, - 0x8B, 0x4C, 0x24, 0x08, 0x8B, 0x54, - 0x24, 0x04, 0x6A, 0xFF}; -const unsigned char usb_read2_pattern[] = {0x8B, 0x4C, 0x24, 0x0C, 0x8B, 0x54, - 0x24, 0x08, 0x8D, 0x44, 0x24, 0x0C, - 0x6A, 0x00, 0x50, 0x51}; -const unsigned char brickbug_pattern[] = {0x83, 0xC4, 0x18, 0x3D, 0x13, - 0xF0, 0xC2, 0xC8, 0x75}; + 0x6A, 0x00, 0x68, 0x80, 0x00, 0x00, 0x00, 0x6A, 0x03, 0x6A, 0x00, + 0x6A, 0x03}; +const unsigned char usb_write_pattern[] = { + 0x8B, 0x94, 0x24, 0x0C, 0x10, 0x00, 0x00, 0x8D, 0x44, 0x24, 0x00, + 0x6A, 0x00, 0x50, 0x8B, 0x84}; +const unsigned char usb_write2_pattern[] = { + 0x8B, 0x94, 0x24, 0x10, 0x10, 0x00, 0x00, 0x8D, 0x44, 0x24, 0x00, + 0x6A, 0x00, 0x50, 0x8B, 0x84}; +const unsigned char usb_read_pattern[] = { + 0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x4C, 0x24, 0x08, 0x8B, + 0x54, 0x24, 0x04, 0x6A, 0xFF}; +const unsigned char usb_read2_pattern[] = { + 0x8B, 0x4C, 0x24, 0x0C, 0x8B, 0x54, 0x24, 0x08, 0x8D, 0x44, 0x24, + 0x0C, 0x6A, 0x00, 0x50, 0x51}; +const unsigned char brickbug_pattern[] = { + 0x83, 0xC4, 0x18, 0x3D, 0x13, 0xF0, 0xC2, 0xC8, 0x75}; // Print given array in hex -void print_hex(unsigned char *buffer, unsigned int size) { - int i, k, r = 0; +void print_hex(const unsigned char *buffer, unsigned int size) { + unsigned int i; for (i = 0; i < size; i++) { printf("%02X ", buffer[i]); - r++; - if ((r == 16) || (i + 1 == size && r < 16)) { - if (i + 1 == size && r < 16) - printf("%*c", r * 3 - 48, ' '); - printf(" "); - - for (k = i - r + 1; k <= i; k++) { - printf("%c", (buffer[k] < 32 || buffer[k] > 127) ? '.' : buffer[k]); + if ((i + 1) % 16 == 0 || i + 1 == size) { + unsigned int start = i / 16 * 16; + if ((i + 1) % 16 != 0) { + printf("%*s", (16 - (i + 1) % 16) * 3, ""); + } + printf(" "); + for (unsigned int j = start; j <= i; j++) { + printf("%c", (buffer[j] < 32 || buffer[j] > 126) ? '.' : buffer[j]); } - - r = 0; printf("\n"); } } @@ -117,29 +147,24 @@ void print_hex(unsigned char *buffer, unsigned int size) { // USB open/close function replacement void close_devices() { - printf("Close devices.\n"); if (devs != NULL) { - for (int i = 0; i < 4; i++) { + printf("Close devices.\n"); + // Xgpro T76 doesn't support multiple devices yet. + for (int i = 0; i < (device_pid == T76_PID ? 1 : 4); i++) { if (device_handle[i] != NULL) { libusb_release_interface(device_handle[i], 0); libusb_close(device_handle[i]); device_handle[i] = NULL; } } + // close session libusb_free_device_list(devs, 1); - libusb_exit(NULL); // close session + libusb_exit(NULL); devs = NULL; } } int open_devices() { - char *debug_var = NULL; - debug_var = getenv("TL_DEBUG"); - if (debug_var && 0 == strncmp(debug_var, "1", 1)) - debug = 1; - else - debug = 0; - printf("Open devices.\n"); close_devices(); device_handle[0] = NULL; device_handle[1] = NULL; @@ -147,6 +172,14 @@ int open_devices() { device_handle[3] = NULL; devs = NULL; + // Initialize all transfers pointers and timeouts + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 7; j++) { + endpoints[i][j].transfer = NULL; + endpoints[i][j].timeout = 5000; + } + } + // initialize a new session libusb_init(NULL); // set verbosity level @@ -157,18 +190,25 @@ int open_devices() { #endif usb_handle[0] = INVALID_HANDLE_VALUE; - usb_handle[1] = INVALID_HANDLE_VALUE; - usb_handle[2] = INVALID_HANDLE_VALUE; - usb_handle[3] = INVALID_HANDLE_VALUE; + + // Xgpro T76 doesn't support multiple devices yet. + if (device_pid != T76_PID) { + usb_handle[1] = INVALID_HANDLE_VALUE; + usb_handle[2] = INVALID_HANDLE_VALUE; + usb_handle[3] = INVALID_HANDLE_VALUE; + } if (device_vid == TL866II_VID) { *devices_count = 0; winusb_handle[0] = INVALID_HANDLE_VALUE; - winusb_handle[1] = INVALID_HANDLE_VALUE; - winusb_handle[2] = INVALID_HANDLE_VALUE; - winusb_handle[3] = INVALID_HANDLE_VALUE; + if (device_pid != T76_PID) { + winusb_handle[1] = INVALID_HANDLE_VALUE; + winusb_handle[2] = INVALID_HANDLE_VALUE; + winusb_handle[3] = INVALID_HANDLE_VALUE; + } } + printf("Open devices.\n"); int devices_found = 0, ret; struct libusb_device_descriptor desc; int count = libusb_get_device_list(NULL, &devs); @@ -177,6 +217,7 @@ int open_devices() { return 0; } + char name[128]; for (int i = 0; i < count; i++) { ret = libusb_get_device_descriptor(devs[i], &desc); if (ret != LIBUSB_SUCCESS) { @@ -184,7 +225,6 @@ int open_devices() { } if (device_pid == desc.idProduct && device_vid == desc.idVendor) { - if (libusb_open(devs[i], &device_handle[devices_found]) == LIBUSB_SUCCESS && libusb_claim_interface(device_handle[devices_found], 0) == @@ -194,152 +234,329 @@ int open_devices() { winusb_handle[devices_found] = (HANDLE)devices_found; *devices_count = devices_found + 1; } + libusb_get_string_descriptor_ascii(device_handle[devices_found], 2, + (unsigned char *)name, sizeof(name)); + + // Get device name string and remove trailing spaces + if (strstr(name, "Xingong")) { + strcpy(name, "XGecu TL866II+"); + } else if (strstr(name, "MiniPro")) { + strcpy(name, "Minipro TL866A/CS"); + } + char *end = name + strlen(name) - 1; + while (end > name && isspace((unsigned char)*end)) end--; + end[1] = '\0'; + + // Get device speed string + char *speed = ""; + switch (libusb_get_device_speed(devs[i])) { + case LIBUSB_SPEED_LOW: + speed = "Low speed (1.5MBit/s)"; + break; + case LIBUSB_SPEED_FULL: + speed = "Full speed (12MBit/s)"; + break; + case LIBUSB_SPEED_HIGH: + speed = "High speed (480MBit/s)"; + break; + case LIBUSB_SPEED_SUPER: + speed = "Super speed (5000MBit/s)"; + default: + break; + } devices_found++; - if (devices_found == 4) - return 0; + printf("Found USB device %u: VID_%04X, PID_%04X; %s; %s\n", + devices_found, desc.idVendor, desc.idProduct, name, speed); + + // Xgpro T76 doesn't support multiple devices yet. + if (devices_found == ((device_pid == T76_PID) ? 1 : 4)) return 0; } } } return 0; } -/// Xgpro replacement functions. -BOOL __stdcall WinUsb_SetPipePolicy(HANDLE InterfaceHandle, UCHAR PipeID, - ULONG PolicyType, ULONG ValueLength, - PVOID Value) { - return TRUE; +// Helper function to retrieve a transfer structure from a PipeID +struct libusb_transfer *get_transfer(UCHAR PipeID) { + return endpoints[(PipeID > 80 ? 1 : 0)][(PipeID & 0x7f) - 1].transfer; } -BOOL __stdcall WinUsb_AbortPipe(HANDLE InterfaceHandle, UCHAR PipeID) { - return TRUE; +// Helper functions to set/get timeout from PipeID +int get_timeout(UCHAR PipeID) { + return endpoints[(PipeID > 80 ? 1 : 0)][(PipeID & 0x7f) - 1].timeout; } -BOOL __stdcall WinUsb_FlushPipe(HANDLE InterfaceHandle, UCHAR PipeID) { - return TRUE; +void set_timeout(UCHAR PipeID, int ep_timeout) { + endpoints[(PipeID > 80 ? 1 : 0)][(PipeID & 0x7f) - 1].timeout = ep_timeout; } -// Asynchronous transfer for WinUsb_ReadPipe/WinUsb_WritePipe. -void async_transfer(Args *args) { - libusb_bulk_transfer(device_handle[(int)args->InterfaceHandle], args->PipeID, - args->Buffer, args->BufferLength, - args->LengthTransferred, 20000); - if (debug) { - pthread_mutex_lock(&mylock); - printf("%s %lu bytes on endpoint %u\n", - (args->PipeID & 0x80) ? "Read async" : "Write async", - args->BufferLength, args->PipeID & 0x7F); - print_hex(args->Buffer, *args->LengthTransferred); - pthread_mutex_unlock(&mylock); +// libusb transfer callback +void transfer_cb(struct libusb_transfer *transfer) { + // We only set the completion flag here. + *(int *)transfer->user_data = 1; +} + +/*** Xgpro replacement functions. ***/ + +// USB transfer for WinUsb_ReadPipe/WinUsb_WritePipe. +// This function will run in a separate thread if overlapped +// transfer is specified. +void usb_transfer(Args *args) { + + int ret, completed = 0; + + // Allocate transfer + struct libusb_transfer *tr = get_transfer(args->PipeID); + tr = libusb_alloc_transfer(0); + if (!tr) { + printf("Out of memory!\n"); + free(args); + return; } - SetEvent(args->Overlapped - ->hEvent); // signal the event to release the waiting object. - free(args); // Free the malloced args. -} -// WinUsb_ReadPipe/winUsb_WritePipe LibUsb implementation. -BOOL __stdcall WinUsb_Transfer(HANDLE InterfaceHandle, UCHAR PipeID, - PUCHAR Buffer, ULONG BufferLength, - PUINT LengthTransferred, - LPOVERLAPPED Overlapped) { - if (InterfaceHandle == INVALID_HANDLE_VALUE) - return FALSE; - if (device_handle[(int)InterfaceHandle] == NULL) - return FALSE; - int ret; - if ((PipeID & 0x80) && (PipeID & 0x7F) > 1 && BufferLength < 64) - BufferLength = 64; - if (Overlapped != NULL) // If an asynchronous transfer is needed then pack - // all the arguments to an Arg structure and pass - // them to a new thread and return immediately. - { - ResetEvent(Overlapped->hEvent); - Args *args = malloc(sizeof(*args)); - args->InterfaceHandle = InterfaceHandle; - args->PipeID = PipeID; - args->Buffer = Buffer; - args->BufferLength = BufferLength; - args->LengthTransferred = LengthTransferred; - args->Overlapped = Overlapped; - CreateThread(NULL, 0, (void *)async_transfer, args, 0, NULL); - return TRUE; - } else // Just an synchronous transfer is needed; just call the - // libusb_bulk_transfer. - { - ret = libusb_bulk_transfer(device_handle[(int)InterfaceHandle], PipeID, - Buffer, BufferLength, LengthTransferred, 20000); - if (debug) { - pthread_mutex_lock(&mylock); - printf("%s %lu bytes on endpoint %u\n", - (PipeID & 0x80) ? "Read normal" : "Write normal", BufferLength, - PipeID & 0x7F); - print_hex(Buffer, *LengthTransferred); - pthread_mutex_unlock(&mylock); + // Initialize transfer structure for bulk transfer + libusb_fill_bulk_transfer(tr, args->handle, args->PipeID, args->Buffer, + args->BufferLength, + (libusb_transfer_cb_fn)transfer_cb, &completed, + get_timeout(args->PipeID)); + + // Submit the transfer + ret = libusb_submit_transfer(tr); + if (ret < 0) { + printf("\nIO error: %s\n", libusb_error_name(ret)); + free(args); + libusb_free_transfer(tr); + return; + } + + // Wait for transfer to complete + while (!completed) { + ret = libusb_handle_events_completed(NULL, &completed); + if (ret < 0) { + if (ret == LIBUSB_ERROR_INTERRUPTED) + continue; + libusb_cancel_transfer(tr); + continue; } } - return (ret == LIBUSB_SUCCESS); -} - -/// Minipro replacement functions -unsigned int uread(HANDLE hDevice, unsigned char *data, size_t size) { - if (hDevice == INVALID_HANDLE_VALUE) - return 0; - if (device_handle[(int)hDevice] == NULL) - return 0; - size_t bytes_read; - int ret = - libusb_bulk_transfer(device_handle[(int)hDevice], LIBUSB_ENDPOINT_IN | 1, - data, size, &bytes_read, 20000); - if (debug) { - printf("Read %d bytes\n", bytes_read); - print_hex(data, bytes_read); + // Check if transfer was okay + if (tr->status != 0) { + printf("\nIO Error: %s\n", libusb_error_name(tr->status)); + libusb_free_transfer(tr); + tr = NULL; + free(args); + return; } - return (ret == LIBUSB_SUCCESS ? bytes_read : 0xFFFFFFFF); + + // Get the actual transfer length + *args->LengthTransferred = tr->actual_length; + + // Free the allocated transfer structure + libusb_free_transfer(tr); + tr = NULL; + + // If debug mode is active print some debug info + if (debug) { + pthread_mutex_lock(&print_lock); + printf("%s %s %u bytes on endpoint 0x%02X\n", + (args->PipeID & 0x80) ? "Read" : "Write", + args->Overlapped ? "Async" : "Normal", *args->LengthTransferred, + args->PipeID); + if (debug == 1) { + print_hex(args->Buffer, *args->LengthTransferred); + } + pthread_mutex_unlock(&print_lock); + } + + // If Overlapped (async) transfer was completed + // signal the event to release the waiting object. + if (args->Overlapped) { + SetEvent(args->Overlapped->hEvent); + } + + // Free the malloced args. + free(args); } +/********************** ENDPOINTS USAGE ******************************** + *********************************************************************** + * TL866A/CS; wMaxPacketSize=64 bytes, 2 endpoints; USB 2.0, 12MBit/s * + * EP1_OUT=0x01, EP1_IN=0x81; All used * + *********************************************************************** + * TL866II+; wMaxPacketSize=64 bytes, 6 endpoints; USB 2.0, 12MBit/s * + * EP1_OUT=0x01, EP1_IN=0x81, EP2_OUT=0x02, EP2_IN=0x82, * + * EP3_OUT=0x03, EP1_IN=0x83; All used * + *********************************************************************** + * T48; wMaxPacketSize=512 bytes, 4 endpoints; USB 2.0, 480MBit/s * + * EP1_OUT=0x01, EP1_IN=0x81, EP2_OUT=0x02, EP2_IN=0x82; All used * + *********************************************************************** + * T56 wMaxPacketSize = 512 bytes, 2 endpoints; USB 2.0, 480MBit/s * + * EP1_OUT=0x01, EP1_IN=0x81; All used * + *********************************************************************** + * T76 wMaxPacketSize = 1024 bytes, 14 endpoints; USB 3.0, 5000MBit/s * + * EP1_OUT=0x01, EP1_IN=0x81, EP2_OUT=0x02, EP2_IN=0x82, * + * EP3_OUT=0x03, EP1_IN=0x83, EP3_OUT=0x04, EP1_IN=0x84, * + * EP3_OUT=0x05, EP1_IN=0x85, EP3_OUT=0x06, EP1_IN=0x86 * + * EP3_OUT=0x07, EP1_IN=0x87; * + * Only EP1_OUT, EP1_IN, EP2_IN, EP5_OUT are used in current firmware * + ***********************************************************************/ + +// WinUsb_ReadPipe/winUsb_WritePipe LibUsb implementation. +BOOL WINAPI WinUsb_Transfer(HANDLE InterfaceHandle, UCHAR PipeID, PUCHAR Buffer, + ULONG BufferLength, PUINT LengthTransferred, + LPOVERLAPPED Overlapped) { + // Check for usb handles + if (InterfaceHandle == INVALID_HANDLE_VALUE) return FALSE; + libusb_device_handle *handle = device_handle[(int)InterfaceHandle]; + if (handle == NULL) return FALSE; + + // Workaround for T76 endpoint 0x83 not used issue. + // The Xgecu T76 software will issue a Winusb_ReadPipe on + // endpoint 0x83 and later on will call WinUSB_AbortPipe. + // Because the T76 firmware doesn't use endpoint 0x83 this will + // get us a libusb timeout error and the Xgpro software locked + // waiting for 'Overlapped->hEvent' to be signaled. + // This will throw an error in Xgpro T76 and the programmer power + // must be cycled. + // We handle this bug here by releasing the waiting object first + // and then aborting the transfer on this endpoint. + if (device_pid == T76_PID && PipeID == 0x83) { + if (Overlapped != NULL) { + SetEvent(Overlapped->hEvent); + } + return TRUE; + } + + // Workaround for Xgpro read BufferLength issue. + // Depending on what chip is used we can get more bytes than + // declared by the Xgpro software in 'BufferLength' argument; + // so if the BufferLength < LengthTransferred we end with a libusb + // overflow error (there is more unread data). + // Perhaps the Windows driver handle this somewhat (multiple reads + // or bigger buffers). We handle this by rounding the buffer size + // in multiple of wMaxPacketSize bytes (64, 512 or 1024). + if ((PipeID > 0x80)) { + libusb_device *device = libusb_get_device(handle); + if (device == NULL) { + return FALSE; + } + // Round BufferLength to the next multiple of endpoint wMaxPacketSize + int wMaxPacketSize = libusb_get_max_packet_size(device, PipeID) - 1; + BufferLength = (BufferLength + wMaxPacketSize) & ~wMaxPacketSize; + } + + // Prepare args + Args *args = malloc(sizeof(Args)); + if (!args) { + printf("Out of memory!\n"); + return FALSE; + } + + *args = (Args){.handle = handle, + .PipeID = PipeID, + .Buffer = Buffer, + .BufferLength = BufferLength, + .LengthTransferred = LengthTransferred, + .Overlapped = Overlapped}; + + // If an overlapped (async) transfer is needed then create a + // new thread and return immediately. + if (Overlapped != NULL) { + ResetEvent(Overlapped->hEvent); + CreateThread(NULL, 0, (void *)usb_transfer, args, 0, NULL); + return TRUE; + } else { + // Just a synchronous transfer is needed; + usb_transfer(args); + } + return TRUE; +} + +// WinUsb_SetPipePolicy LibUsb implementation. +// Only setting pipe timeout is supported +BOOL WINAPI WinUsb_SetPipePolicy(HANDLE InterfaceHandle, UCHAR PipeID, + ULONG PolicyType, ULONG ValueLength, + PVOID Value) { + if (PolicyType == 0x03) { + set_timeout(PipeID, *(int *)Value); + } + return TRUE; +} + +// WinUsb_AbortPipe LibUsb implementation +BOOL WINAPI WinUsb_AbortPipe(HANDLE InterfaceHandle, UCHAR PipeID) { + struct libusb_transfer *tr = get_transfer(PipeID); + if (tr) { + libusb_cancel_transfer(tr); + } + return TRUE; +} + +// WinUsb unused but stubbbed functions. +BOOL WINAPI WinUsb_FlushPipe(HANDLE InterfaceHandle, UCHAR PipeID) { + return TRUE; +} + +BOOL WINAPI WinUsb_Initialize(HANDLE DeviceHandle, PVOID *InterfaceHandle) { + return TRUE; +} + +BOOL WINAPI WinUsb_Free(HANDLE InterfaceHandle) { return TRUE; } + +/*** Minipro replacement functions ***/ + +// USB read implementation. Use the WinUsb_Transfer above +int uread(HANDLE hDevice, unsigned char *data, unsigned int size) { + unsigned int transferred = 0; + set_timeout(LIBUSB_ENDPOINT_IN | 1, 20000); + BOOL ret = WinUsb_Transfer(hDevice, LIBUSB_ENDPOINT_IN | 1, data, size, + &transferred, NULL); + return (ret ? transferred : -1); +} + +// USB write implementation. Use the WinUsb_Transfer above BOOL uwrite(HANDLE hDevice, unsigned char *data, size_t size) { - if (hDevice == INVALID_HANDLE_VALUE) - return 0; - if (device_handle[(int)hDevice] == NULL) - return 0; - size_t bytes_writen; - int ret = - libusb_bulk_transfer(device_handle[(int)hDevice], LIBUSB_ENDPOINT_OUT | 1, - data, size, &bytes_writen, 20000); - if (debug) { - printf("Write %d bytes\n", bytes_writen); - print_hex(data, bytes_writen); - } - return (ret == LIBUSB_SUCCESS); + unsigned int transferred = 0; + set_timeout(LIBUSB_ENDPOINT_OUT | 1, 20000); + return WinUsb_Transfer(hDevice, LIBUSB_ENDPOINT_OUT | 1, data, size, + &transferred, NULL); } +// USB write to device zero BOOL usb_write(unsigned char *lpInBuffer, unsigned int nInBufferSize) { - BOOL ret = uwrite(0, lpInBuffer, nInBufferSize); - return ret; + return uwrite(0, lpInBuffer, nInBufferSize); } -unsigned int usb_read(unsigned char *lpOutBuffer, unsigned int nBytesToRead, - unsigned int nOutBufferSize) { - unsigned int ret = uread(0, lpOutBuffer, nBytesToRead); - if (ret == 0xFFFFFFFF) +// USB read from device zero +int usb_read(unsigned char *lpOutBuffer, unsigned int nBytesToRead, + unsigned int nOutBufferSize) { + int ret = uread(0, lpOutBuffer, nBytesToRead); + if (ret == -1) message_box(get_foreground_window(), "Read error!", "TL866", MB_ICONWARNING); return ret; } +// USB write to specified device BOOL usb_write2(HANDLE hDevice, unsigned char *lpInBuffer, unsigned int nInBufferSize) { - BOOL ret = uwrite(hDevice, lpInBuffer, nInBufferSize); - return ret; + return uwrite(hDevice, lpInBuffer, nInBufferSize); } -unsigned int usb_read2(HANDLE hDevice, unsigned char *lpOutBuffer, - unsigned int nBytesToRead, unsigned int nOutBufferSize) { - unsigned int ret = uread(hDevice, lpOutBuffer, nBytesToRead); - return ret; +// USB read from specified device +int usb_read2(HANDLE hDevice, unsigned char *lpOutBuffer, + unsigned int nBytesToRead, unsigned int nOutBufferSize) { + return uread(hDevice, lpOutBuffer, nBytesToRead); } -// Return the device count +// If make hotplug=udev is invoked then libudev is used for monitoring, +// otherwise libusb hotplug events monitoring is used. +#ifdef UDEV + +/*** Udev functions ***/ + +// Return the device count using Udev library API int get_device_count() { struct udev *udev = udev_new(); if (!udev) { @@ -373,37 +590,38 @@ int get_device_count() { return count; } +// Udev hotplug USB monitoring thread void notifier_function() { struct udev *udev; struct udev_monitor *mon; struct udev_device *dev; - DEV_BROADCAST_DEVICEINTERFACE_W DevBi; - DevBi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W); - DevBi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - DevBi.dbcc_classguid = m_guid; - udev = udev_new(); if (!udev) { printf("Can't create udev\n"); return; } + // Get a new udev monitor from the netlink mon = udev_monitor_new_from_netlink(udev, "udev"); if (!mon) { printf("NetLink not available!\n"); return; } + + // Get the device count int count = get_device_count(); if (count == -1) { printf("udev error.\n"); return; } + printf("Using Udev hotplug events.\n\n"); udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL); udev_monitor_enable_receiving(mon); - int fd = udev_monitor_get_fd(mon); + int udev_mon_fd = udev_monitor_get_fd(mon); + // Enter the monitoring loop cancel = FALSE; while (!cancel) { fd_set fds; @@ -411,12 +629,12 @@ void notifier_function() { int ret; FD_ZERO(&fds); - FD_SET(fd, &fds); + FD_SET(udev_mon_fd, &fds); tv.tv_sec = 0; tv.tv_usec = 0; - ret = select(fd + 1, &fds, NULL, NULL, &tv); - if (ret > 0 && FD_ISSET(fd, &fds)) { + ret = select(udev_mon_fd + 1, &fds, NULL, NULL, &tv); + if (ret > 0 && FD_ISSET(udev_mon_fd, &fds)) { dev = udev_monitor_receive_device(mon); if (dev && !strcasecmp(udev_device_get_devtype(dev), "usb_device")) { int count_new; @@ -425,50 +643,113 @@ void notifier_function() { if (count != count_new) { count = count_new; // printf("device added.\n"); - close_devices(); - usleep(100000); - send_message(hWnd, WM_DEVICECHANGE, DBT_DEVICEARRIVAL, - (LPARAM)&DevBi); - usleep(100000); - redraw_window(hWnd, NULL, NULL, RDW_INVALIDATE); + device_changed(DBT_DEVICEARRIVAL); } - } else if (!strcasecmp(udev_device_get_action(dev), "remove")) { count_new = get_device_count(); if (count != count_new) { count = count_new; // printf("device removed.\n"); - close_devices(); - usleep(100000); - send_message(hWnd, WM_DEVICECHANGE, DBT_DEVICEARRIVAL, - (LPARAM)&DevBi); - usleep(100000); - redraw_window(hWnd, NULL, NULL, RDW_INVALIDATE); + device_changed(DBT_DEVICEREMOVECOMPLETE); } } udev_device_unref(dev); } } - usleep(10000); + usleep(50000); } udev_monitor_unref(mon); } -// RegisterDeviceNotifications WIN API replacement -HANDLE __stdcall RegisterDeviceNotifications(HANDLE hRecipient, - LPVOID NotificationFilter, - DWORD Flags) { - printf("RegisterDeviceNotifications hWnd=%X4\n", (unsigned int)hRecipient); - hWnd = hRecipient; - h_thread = CreateThread(NULL, 0, (void *)notifier_function, NULL, 0, NULL); - if (!h_thread) - printf("Thread notifier failed.\n"); +// Use libusb hotplug events. +#else +// LibUsb hotplug callback function +int hotplug_cb(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event, void *user_data) { + + // Notify the event loop thread to rescan the devices. + // If we call the device_changed function from here the code will crash. + // See https://libusb.sourceforge.io/api-1.0/libusb_hotplug.html + *(int *)user_data = event; return 0; } -/// Patcher functions -BOOL patch_function(char *library, char *func, void *funcaddress) { +// LibUsb hotplug USB monitoring thread +void notifier_function() { + printf("Using LibUsb hotplug events.\n\n"); + int changed = 0; + libusb_hotplug_callback_handle callback_handle; + + // Register the hotplug callback for the desired USB_VID/PID + libusb_init(NULL); + int rc = libusb_hotplug_register_callback( + NULL, + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, + device_vid, device_pid, LIBUSB_HOTPLUG_MATCH_ANY, + (libusb_hotplug_callback_fn)hotplug_cb, &changed, &callback_handle); + + // Check if the registration was okay + if (LIBUSB_SUCCESS != rc) { + printf("LibUsb hotplug callback error.\n"); + return; + } + + // Enter the monitoring thread and handle hotplug events. + while (!cancel) { + libusb_handle_events_completed(NULL, NULL); + usleep(50000); + if (changed) { + changed = 0; + device_changed(changed == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED + ? DBT_DEVICEARRIVAL + : DBT_DEVICEREMOVECOMPLETE); + } + } + + // Deregister hotplug callback and exit + libusb_hotplug_deregister_callback(NULL, callback_handle); + libusb_exit(NULL); +} +#endif + +// Notifier function. This will force the software to rescan devices. +void device_changed(unsigned int event) { + + // Initialize the device broadcast interface + DEV_BROADCAST_DEVICEINTERFACE_W DevBi; + DevBi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W); + DevBi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + DevBi.dbcc_classguid = m_guid; + + // Close all devices + close_devices(); + usleep(100000); + + // Broadcast a device change message + send_message(hWnd, WM_DEVICECHANGE, event, (LPARAM)&DevBi); + usleep(100000); + + // Force software to refresh the GUI + redraw_window(hWnd, NULL, NULL, RDW_INVALIDATE); +} + +// RegisterDeviceNotifications WINAPI replacement +HANDLE WINAPI RegisterDeviceNotifications(HANDLE hRecipient, + LPVOID NotificationFilter, + DWORD Flags) { + printf("RegisterDeviceNotifications hWnd = %p\n", hRecipient); + hWnd = hRecipient; + hotplug_thread = + CreateThread(NULL, 0, (void *)notifier_function, NULL, 0, NULL); + if (!hotplug_thread) printf("Failed to create the USB monitoring thread!\n"); + return 0; +} + +/*** Patcher functions ***/ + +// Dll redirect patch function +BOOL patch_function(char *library, char *func_name, void *custom_func) { DWORD dwOldProtection; DWORD func_addr = 0; @@ -483,7 +764,7 @@ BOOL patch_function(char *library, char *func, void *funcaddress) { [IMAGE_DIRECTORY_ENTRY_IMPORT] .VirtualAddress); - // Search for library in the IAT + // Search for library in the import directory while (ImpDesc->Characteristics && ImpDesc->Name) { if (strcasecmp(BaseAddress + ImpDesc->Name, library) == 0) { break; // Found it! @@ -491,54 +772,59 @@ BOOL patch_function(char *library, char *func, void *funcaddress) { ImpDesc++; } - // check if the library was found in the IAT + // check if the library was found in the import directory if (!ImpDesc->Characteristics) { - printf("%s was not found in the IAT.\n", library); - return FALSE; // nope, exit with error. + printf("Library '%s' was not found in the import directory.\n", library); + return FALSE; } - // Get the address of function in library + // If the desired library was found we can get the function address DWORD_PTR ProcAddress = - (DWORD_PTR)GetProcAddress(GetModuleHandleA(library), func); + (DWORD_PTR)GetProcAddress(GetModuleHandleA(library), func_name); - // Find the address in the thunk table + // Check if the desired function address was found + if (!ProcAddress) { + printf("Function '%s' was not found in '%s' library.\n", func_name, + library); + return FALSE; + } + + // We have the address, let's search it in the thunk table PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(BaseAddress + ImpDesc->FirstThunk); while (thunk->u1.Function) { if ((DWORD_PTR)thunk->u1.Function == ProcAddress) { - // if found, patch it to point to our custom function + // if an entry is found, patch it to point to our custom function MEMORY_BASIC_INFORMATION info; VirtualQuery(&thunk->u1.Function, &info, sizeof(MEMORY_BASIC_INFORMATION)); VirtualProtect(info.BaseAddress, info.RegionSize, PAGE_READWRITE, &dwOldProtection); func_addr = thunk->u1.Function; - thunk->u1.Function = (DWORD_PTR)funcaddress; + thunk->u1.Function = (DWORD_PTR)custom_func; VirtualProtect(info.BaseAddress, info.RegionSize, info.Protect, &dwOldProtection); } - thunk++; } // check if the patch was ok. if (!func_addr) { - printf("%s was not found in %s.\n", func, library); - return FALSE; // nope, exit with error. + printf("Function '%s' was not found in the IAT thunk table.\n", func_name); + return FALSE; } - return TRUE; } -// Inline helper patch function +// Inline helper patch function. Warning, this is x86 ASM code static inline void patch(void *src, void *dest) { // push xxxx, ret; an absolute Jump replacement. - *(BYTE *)src = 0x68; + *(BYTE *)src = X86_PUSH; *((DWORD *)((BYTE *)src + 1)) = (DWORD)dest; - *((BYTE *)src + 5) = 0xc3; + *((BYTE *)src + 5) = X86_RET; } -// Xgpro patcher function. Called from DllMain. Return TRUE if patch was ok and +// Xgpro patcher function. Called from DllMain. Returns TRUE if patch was ok and // continue with program loading or FALSE to exit with error. BOOL patch_xgpro() { // Get the BaseAddress, NT Header and Image Import Descriptor @@ -547,26 +833,29 @@ BOOL patch_xgpro() { (PIMAGE_NT_HEADERS)((PBYTE)BaseAddress + ((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew); - // Search for version - BOOL t76 = FALSE; - unsigned char *version = - memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, "Xgpro v", 7); - if (!version) { - version = memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, - "Xgpro T76 v", 11); - if (!version) - return FALSE; - t76 = TRUE; - } + // Search for version and set the Xgpro GUID and VID/PID + unsigned char *version; + if ((version = memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, + "Xgpro v", 7))) { + // TL866II+, T48, T56 VID/PID and interface GUID + device_vid = TL866II_VID; + device_pid = TL866II_PID; + memcpy(&m_guid, &XGPRO_GUID1, sizeof(GUID)); + } else if ((version = + memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, + "Xgpro T76 v", 11))) { + + // T76 VID/PID and interface GUID + device_vid = T76_VID; + device_pid = T76_PID; + memcpy(&m_guid, &XGPRO_GUID2, sizeof(GUID)); + } else { + return FALSE; + } printf("Found %s\n", version); - // Set some function pointers - HMODULE hmodule = LoadLibraryA("user32.dll"); - send_message = (pSendMessageA)GetProcAddress(hmodule, "SendMessageA"); - redraw_window = (pRedrawWindow)GetProcAddress(hmodule, "RedrawWindow"); - - // Patch the Linux incompatible functions functions + // Patch the Linux incompatible functions if (!patch_function("user32.dll", "RegisterDeviceNotificationA", &RegisterDeviceNotifications)) return FALSE; @@ -581,6 +870,11 @@ BOOL patch_xgpro() { if (!patch_function("winusb.dll", "WinUsb_ReadPipe", &WinUsb_Transfer)) return FALSE; + if (!patch_function("winusb.dll", "WinUsb_Initialize", &WinUsb_Initialize)) + return FALSE; + + if (!patch_function("winusb.dll", "WinUsb_Free", &WinUsb_Free)) return FALSE; + // Searching for functions signature in code section. void *p_opendevices = NULL; void *p_closedevices = NULL; @@ -588,18 +882,40 @@ BOOL patch_xgpro() { void *p_usbhandle = NULL; void *p_devicescount = NULL; - // Search for open device function pattern 1 (xgpro < V12.7x) + // Search for open_device function pattern 1 (xgpro < V12.7x) void *p_od1 = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, &xgpro_open_devices_pattern1, sizeof(xgpro_open_devices_pattern1)); - // Search for open device function pattern 2 (xgpro > V12.7x) + // Search for open_device function pattern 2 (xgpro > V12.7x) void *p_od2 = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, &xgpro_open_devices_pattern2, sizeof(xgpro_open_devices_pattern2)); + // If we obtained the most important function address (open_devices) then, + // we can also calculate the other necessary addresses. + // Basically we need two function pointers (open_devices and close_devices) + // which are invoked by Minipro/Xgpro when the program is started/closed + // or a device is attached or dettached. + // + // We also need three data pointers: + // 1. usb_handle which is used in both Minipro and Xgpro as a handle to a + // device obtained by calling the CreateFile API. This is actually an array of + // four pointers which in windows holds a handle to the newly opened device or + // an invalid handle value. As we redirect the open/close usb functions we + // initialize each item with an index (0 to 3) or the INVALID_HANDLE_VALUE + // (0xffffffff) if the coresponding device is not found. + // + // 2. winusb_handle used only in Xgpro because Xgpro uses the WinUsb library + // for USB communications. Like the usb_handle this is actually an array of + // four pointers which are initialized with an index (0 to 3) or the invalid + // handle value in our custom open_devices function. + // + // 3. devices_count which is used only by Xgpro to know how many devices are + // available. The Minipro and Xgpro can handle up to four devices while the + // Xgpro_T76 software can handle only one programmer at this time. if (p_od1) { p_opendevices = p_od1 - 0x1D; p_closedevices = (void *)(*(int *)((unsigned char *)p_opendevices + 5)) + @@ -621,65 +937,57 @@ BOOL patch_xgpro() { return FALSE; } else { printf("Function signatures not found! Unsupported Xgpro version.\n"); - return FALSE; // nope, exit with error. + return FALSE; } // Print debug info. - printf("Base Address = 0x%08lX\n", (DWORD)BaseAddress); - printf("Code section = 0x%08lX,0x%08lX\n", - (DWORD)BaseAddress + NtHeader->OptionalHeader.BaseOfCode, + printf("Base Address = %p\n", BaseAddress); + printf("Code section = %p, 0x%lx\n", + BaseAddress + NtHeader->OptionalHeader.BaseOfCode, (DWORD)NtHeader->OptionalHeader.SizeOfCode); - printf("Open Devices found at 0x%08lX\n", (DWORD)p_opendevices); - printf("Close Devices found at 0x%08lX\n", (DWORD)p_closedevices); - printf("Usb Handle found at 0x%08lX\n", (DWORD)p_usbhandle); - printf("WinUsb Handle found at 0x%08lX\n", (DWORD)p_winusbhandle); - printf("Devices count found at 0x%08lX\n", (DWORD)p_devicescount); + printf("Open Devices found at %p\n", p_opendevices); + printf("Close Devices found at %p\n", p_closedevices); + printf("Usb Handle found at %p\n", p_usbhandle); + printf("WinUsb Handle found at %p\n", p_winusbhandle); + printf("Devices count found at %p\n", p_devicescount); // Patch all low level functions in Xgpro.exe to point to our custom // functions. DWORD dwOldProtection; - // Initialize the usb handle address. + // Initialize the usb_handle, winusb_handle and devices_count pointers + // These variables are used by Xgpro to handle all opened devices usb_handle = p_usbhandle; winusb_handle = p_winusbhandle; devices_count = p_devicescount; + + // Now this is the actual code patch. So we need to patch the code + // to redirect the open_devices/close_devices functions to our custom + // functions. The patch is done by inserting an absolute jump at the + // desired adress. To do this we need first to change the READ_ONLY + // attribute of the code section, patch the desired address and then + // restore the old READ_ONLY attribute. + // So, we have a self modifying code here. + + // Unprotect the code memory section (make it writable) VirtualProtect(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, PAGE_READWRITE, - &dwOldProtection); // unprotect the code memory section + &dwOldProtection); - // patch Open_Devices function + // patch open_devices function to point to our implementation patch(p_opendevices, &open_devices); - // patch close_devices function + // patch close_devices function to point to our implementation patch(p_closedevices, &close_devices); + // restore the old READ_ONLY protection VirtualProtect(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, dwOldProtection, - &dwOldProtection); // restore the old protection - - // Set the Xgpro GUID and VID/PID - GUID guid; - if (t76) { - device_vid = T76_VID; - device_pid = T76_PID; - guid = (GUID){0x015DE341, - 0x91CC, - 0x8286, - {0x39, 0x64, 0x1A, 0x00, 0x6B, 0xC1, 0xF0, 0x0F}}; - } else { - device_vid = TL866II_VID; - device_pid = TL866II_PID; - guid = (GUID){0xE7E8BA13, - 0x2A81, - 0x446E, - {0xA1, 0x1E, 0x72, 0x39, 0x8F, 0xBD, 0xA8, 0x2F}}; - } - - memcpy(&m_guid, &guid, sizeof(GUID)); + &dwOldProtection); return TRUE; } -// Minipro patcher function. Called from DllMain. Return TRUE if patch was ok +// Minipro patcher function. Called from DllMain. Returns TRUE if patch was ok // and continue with program loading or FALSE to exit with error. BOOL patch_minipro() { // Get the BaseAddress, NT Header and Image Import Descriptor @@ -690,18 +998,9 @@ BOOL patch_minipro() { unsigned char *version = memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, "MiniPro v", 9); - if (!version) - return FALSE; + if (!version) return FALSE; printf("Found %s\n", version); - // Set some function pointers - HMODULE hmodule = LoadLibraryA("user32.dll"); - message_box = (pMessageBoxA)GetProcAddress(hmodule, "MessageBoxA"); - get_foreground_window = - (pGetForegroundWindow)GetProcAddress(hmodule, "GetForegroundWindow"); - send_message = (pSendMessageA)GetProcAddress(hmodule, "SendMessageA"); - redraw_window = (pRedrawWindow)GetProcAddress(hmodule, "RedrawWindow"); - // Patch the Linux incompatible functions functions if (!patch_function("user32.dll", "RegisterDeviceNotificationA", &RegisterDeviceNotifications)) @@ -736,152 +1035,194 @@ BOOL patch_minipro() { if (!p_opendevices || !p_usbwrite || !p_usbwrite2 || !p_usbread || !p_usbread2) { printf("Function signatures not found! Unsupported MiniPro version.\n"); - return FALSE; // nope, exit with error. + return FALSE; } - // search for brick bug + // Search for brick bug. This is not an actually bug but a special code + // used to brick pirated TL866A/CS devices. The problem is that they + // used a wrong detection which can also brick genuine TL866A/CS devices + // See this for more info: https://pastebin.com/i5iLGPs1 unsigned char *p_brickbug = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, &brickbug_pattern, sizeof(brickbug_pattern)); - // Print debug info. - printf("Base Address = 0x%08lX\n", (DWORD)BaseAddress); - printf("Code section = 0x%08lX,0x%08lX\n", - (DWORD)BaseAddress + NtHeader->OptionalHeader.BaseOfCode, + // Print some debug info. + printf("Base Address = %p\n", BaseAddress); + printf("Code section = %p, 0x%lx\n", + BaseAddress + NtHeader->OptionalHeader.BaseOfCode, (DWORD)NtHeader->OptionalHeader.SizeOfCode); - printf("Open Devices found at 0x%08lX\n", (DWORD)p_opendevices); - printf("Close Devices found at 0x%08lX\n", (DWORD)p_closedevices); - printf("Usb Write found at 0x%08lX\n", (DWORD)p_usbwrite); - printf("Usb Read found at 0x%08lX\n", (DWORD)p_usbread); - printf("Usb Write2 found at 0x%08lX\n", (DWORD)p_usbwrite2); - printf("Usb Read2 found at 0x%08lX\n", (DWORD)p_usbread2); - printf("Usb Handle found at 0x%08lX\n", (DWORD)p_usbhandle); - if (p_brickbug) - printf("Patched brick bug at 0x%08lX\n", (DWORD)p_brickbug + 0x08); + printf("Open Devices found at %p\n", p_opendevices); + printf("Close Devices found at %p\n", p_closedevices); + printf("Usb Write found at %p\n", p_usbwrite); + printf("Usb Read found at %p\n", p_usbread); + printf("Usb Write2 found at %p\n", p_usbwrite2); + printf("Usb Read2 found at %p\n", p_usbread2); + printf("Usb Handle found at %p\n", p_usbhandle); + if (p_brickbug) printf("Patched brick bug at %p\n", p_brickbug + 0x08); // Patch all low level functions in MiniPro.exe to point to our custom // functions. - DWORD dwOldProtection; - // Initialize the usb handle address. + // Initialize the usb_handle pointer. + // Compared to Xgpro software we have only a data pointer here. + // We initialize each element with a simple index (0 to 3) + // or the INVALID_HANDLE_VALUE. usb_handle = p_usbhandle; + + // Now this is the actual code patch. So we need to patch the code + // to redirect all the usb realated functions as well as open/close devices + // functions to point to our custom implementation. + // functions. The patch is done by inserting an absolute jump at the + // desired adress. To do this we need first to change the READ_ONLY + // attribute of the code section, patch the desired address and then + // restore the old READ_ONLY attribute. + // So, we have a self modifying code here. + + // Unprotect the code memory section (make it writable) + DWORD dwOldProtection; VirtualProtect(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, PAGE_READWRITE, - &dwOldProtection); // unprotect the code memory section + &dwOldProtection); - // patch Open_Devices function + // patch open_devices function patch(p_opendevices, &open_devices); // patch close_devices function patch(p_closedevices, &close_devices); - // patch USB_Write function + // patch usb_write function patch(p_usbwrite, &usb_write); - // patch USB_Read function + // patch usb_read function patch(p_usbread, &usb_read); - // patch USB_Write2 function + // patch usb_write2 function patch(p_usbwrite2, &usb_write2); - // patch USB_Read2 function + // patch usb_read2 function patch(p_usbread2, &usb_read2); // patch the brick bug - if (p_brickbug) - *(p_brickbug + 0x08) = 0xEB; + if (p_brickbug) *(p_brickbug + 0x08) = X86_JMP; + // Restore the old READ_ONLY protection VirtualProtect(BaseAddress + NtHeader->OptionalHeader.BaseOfCode, NtHeader->OptionalHeader.SizeOfCode, dwOldProtection, - &dwOldProtection); // restore the old protection + &dwOldProtection); // Set the Minipro GUID - const GUID guid = {0x85980D83, - 0x32B9, - 0x4BA1, - {0x8F, 0xDF, 0x12, 0xA7, 0x11, 0xB9, 0x9C, 0xA2}}; - memcpy(&m_guid, &guid, sizeof(GUID)); + memcpy(&m_guid, &MINIPRO_GUID, sizeof(GUID)); - // set vid/pid + // Set the vid/pid device_vid = TL866A_VID; device_pid = TL866A_PID; return TRUE; } -/// DLLMAIN +/*** DllMain ***/ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - switch (fdwReason) { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); - printf("Dll Loaded.\n"); - if (patch_xgpro() || patch_minipro()) - return TRUE; - printf("Dll Unloaded.\n"); - return FALSE; - break; - case DLL_PROCESS_DETACH: - cancel = TRUE; - WaitForSingleObject(h_thread, 5000); - printf("Dll Unloaded.\n"); - break; + // Get the module name (should be setupapi.dll) + WCHAR buffer[MAX_PATH]; + GetModuleFileNameW(hinstDLL, buffer, sizeof(buffer)); + char *module_path = wine_get_unix_file_name(buffer); + if(!module_path){ + module_path = "setupapi.dll"; } + switch (fdwReason) { + // Dll loaded and atached to process + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + printf("%s loaded.\n", module_path); + + // Set the debug mode if TL_DEBUG environment variable is set + char *debug_var = NULL; + debug_var = getenv("TL_DEBUG"); + if (debug_var && !strncmp(debug_var, "1", 1)) + debug = 1; + else if (debug_var && !strncmp(debug_var, "2", 1)) + debug = 2; + else + debug = 0; + + // Set some used function pointers from the user32.dll library + HMODULE hmodule = LoadLibraryA("user32.dll"); + message_box = (pMessageBoxA)GetProcAddress(hmodule, "MessageBoxA"); + get_foreground_window = + (pGetForegroundWindow)GetProcAddress(hmodule, "GetForegroundWindow"); + send_message = (pSendMessageA)GetProcAddress(hmodule, "SendMessageA"); + redraw_window = (pRedrawWindow)GetProcAddress(hmodule, "RedrawWindow"); + + // Try to patch the software + if (patch_xgpro() || patch_minipro()) return TRUE; + printf("%s unloaded.\n", wine_get_unix_file_name(buffer)); + return FALSE; + break; + + // We are detached from a process, terminate the USB hotplug monitoring + // thread, close all opened devices and exit. + case DLL_PROCESS_DETACH: + cancel = TRUE; + WaitForSingleObject(hotplug_thread, 5000); + close_devices(); + printf("%s unloaded.\n", module_path); + break; + } return TRUE; } -/// SetupApi redirected functions needed for the new wine 4.11+ winex11.drv -/// calls -typedef BOOL(__stdcall *pSetupDiGetDeviceInterfaceDetailW)(HANDLE, HANDLE, - HANDLE, DWORD, - PDWORD, LPVOID); +/*************************************************************************/ +/********************** Exported functions section ***********************/ +/*************************************************************************/ -typedef BOOL(__stdcall *pSetupDiGetDeviceRegistryPropertyW)(HANDLE, LPVOID, - DWORD, PDWORD, - PBYTE, DWORD, - PDWORD); -typedef BOOL(__stdcall *pSetupDiCallClassInstaller)(LPVOID, HANDLE, LPVOID); -typedef HANDLE(__stdcall *pSetupDiGetClassDevsA)(const GUID *, PCSTR, HWND, +// SetupApi redirected functions needed for the new wine >4.11 winex11.drv +// calls. These functions must be specified in setupapi.spec file. +typedef BOOL(WINAPI *pSetupDiGetDeviceInterfaceDetailW)(HANDLE, HANDLE, HANDLE, + DWORD, PDWORD, LPVOID); + +typedef BOOL(WINAPI *pSetupDiGetDeviceRegistryPropertyW)(HANDLE, LPVOID, DWORD, + PDWORD, PBYTE, DWORD, + PDWORD); +typedef BOOL(WINAPI *pSetupDiCallClassInstaller)(LPVOID, HANDLE, LPVOID); +typedef HANDLE(WINAPI *pSetupDiGetClassDevsA)(const GUID *, PCSTR, HWND, DWORD); +typedef HANDLE(WINAPI *pSetupDiGetClassDevsW)(const GUID *, PCWSTR, HWND, + DWORD); +typedef BOOL(WINAPI *pSetupDiEnumDeviceInfo)(HANDLE, DWORD, LPVOID); +typedef BOOL(WINAPI *pSetupDiEnumDeviceInterfaces)(HANDLE, LPVOID, const GUID *, + DWORD, HANDLE); +typedef BOOL(WINAPI *pSetupDiGetDevicePropertyW)(HANDLE, LPVOID, const LPVOID *, + LPVOID *, PBYTE, DWORD, PDWORD, DWORD); -typedef HANDLE(__stdcall *pSetupDiGetClassDevsW)(const GUID *, PCWSTR, HWND, - DWORD); -typedef BOOL(__stdcall *pSetupDiEnumDeviceInfo)(HANDLE, DWORD, LPVOID); -typedef BOOL(__stdcall *pSetupDiEnumDeviceInterfaces)(HANDLE, LPVOID, - const GUID *, DWORD, - HANDLE); -typedef BOOL(__stdcall *pSetupDiGetDevicePropertyW)(HANDLE, LPVOID, - const LPVOID *, LPVOID *, - PBYTE, DWORD, PDWORD, - DWORD); typedef BOOL (*pSetupDiDestroyDeviceInfoList)(HANDLE DeviceInfoSet); -typedef HANDLE(__stdcall *pSetupDiCreateDeviceInfoList)(const GUID *ClassGuid, - HWND hwndParent); -typedef BOOL(__stdcall *pSetupDiSetDevicePropertyW)(HANDLE, LPVOID, - const LPVOID *, LPVOID, - const PBYTE, DWORD, DWORD); -typedef BOOL(__stdcall *pSetupDiCreateDeviceInfoW)(HANDLE, PCWSTR, const GUID *, - PCWSTR, HWND, DWORD, LPVOID); -typedef BOOL(__stdcall *pSetupDiOpenDeviceInfoW)(HANDLE, PCWSTR, HWND, DWORD, - LPVOID); -typedef BOOL(__stdcall *pSetupDiRegisterDeviceInfo)(HANDLE, LPVOID, DWORD, - LPVOID, PVOID, LPVOID); -typedef BOOL(__stdcall *pSetupDiSetDeviceRegistryPropertyW)(HANDLE, LPVOID, - DWORD, const BYTE *, - DWORD); -typedef HKEY(__stdcall *pSetupDiCreateDevRegKeyW)(HANDLE, LPVOID, DWORD, DWORD, - DWORD, HANDLE, PCWSTR); -typedef BOOL(__stdcall *pSetupDiRemoveDevice)(HANDLE, LPVOID); +typedef HANDLE(WINAPI *pSetupDiCreateDeviceInfoList)(const GUID *ClassGuid, + HWND hwndParent); +typedef BOOL(WINAPI *pSetupDiSetDevicePropertyW)(HANDLE, LPVOID, const LPVOID *, + LPVOID, const PBYTE, DWORD, + DWORD); +typedef BOOL(WINAPI *pSetupDiCreateDeviceInfoW)(HANDLE, PCWSTR, const GUID *, + PCWSTR, HWND, DWORD, LPVOID); +typedef BOOL(WINAPI *pSetupDiOpenDeviceInfoW)(HANDLE, PCWSTR, HWND, DWORD, + LPVOID); +typedef BOOL(WINAPI *pSetupDiRegisterDeviceInfo)(HANDLE, LPVOID, DWORD, LPVOID, + PVOID, LPVOID); +typedef BOOL(WINAPI *pSetupDiSetDeviceRegistryPropertyW)(HANDLE, LPVOID, DWORD, + const BYTE *, DWORD); +typedef HKEY(WINAPI *pSetupDiCreateDevRegKeyW)(HANDLE, LPVOID, DWORD, DWORD, + DWORD, HANDLE, PCWSTR); +typedef BOOL(WINAPI *pSetupDiRemoveDevice)(HANDLE, LPVOID); +// Helper function to obtain the function address to original setupapi.dll FARPROC get_proc_address(LPCSTR lpProcName) { char sysdir[MAX_PATH]; GetSystemDirectoryA(sysdir, MAX_PATH); strcat(sysdir, "\\setupapi.dll"); HMODULE hmodule = LoadLibraryA(sysdir); FARPROC address = GetProcAddress(hmodule, lpProcName); - // printf("%s : 0x%08lX\n", lpProcName, (uint32_t)address); + //printf("%s : %p\n", lpProcName, address); return address; } -__stdcall BOOL SetupDiGetDeviceInterfaceDetailW( +WINAPI BOOL SetupDiGetDeviceInterfaceDetailW( HANDLE DeviceInfoSet, HANDLE DeviceInterfaceData, HANDLE DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, LPVOID DeviceInfoData) { @@ -892,7 +1233,7 @@ __stdcall BOOL SetupDiGetDeviceInterfaceDetailW( DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); } -_stdcall BOOL SetupDiGetDeviceRegistryPropertyW( +WINAPI BOOL SetupDiGetDeviceRegistryPropertyW( HANDLE DeviceInfoSet, LPVOID DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize) { @@ -903,40 +1244,40 @@ _stdcall BOOL SetupDiGetDeviceRegistryPropertyW( PropertyBuffer, PropertyBufferSize, RequiredSize); } -__stdcall BOOL SetupDiCallClassInstaller(LPVOID InstallFunction, - HANDLE DeviceInfoSet, - LPVOID DeviceInfoDat) { +WINAPI BOOL SetupDiCallClassInstaller(LPVOID InstallFunction, + HANDLE DeviceInfoSet, + LPVOID DeviceInfoDat) { pSetupDiCallClassInstaller pfunc = (pSetupDiCallClassInstaller)get_proc_address("SetupDiCallClassInstaller"); return pfunc(InstallFunction, DeviceInfoSet, DeviceInfoDat); } -__stdcall HANDLE SetupDiGetClassDevsA(const GUID *ClassGuid, PCSTR Enumerator, - HWND hwndParent, DWORD Flags) { +WINAPI HANDLE SetupDiGetClassDevsA(const GUID *ClassGuid, PCSTR Enumerator, + HWND hwndParent, DWORD Flags) { pSetupDiGetClassDevsA pfunc = (pSetupDiGetClassDevsA)get_proc_address("SetupDiGetClassDevsA"); return pfunc(ClassGuid, Enumerator, hwndParent, Flags); } -__stdcall HANDLE SetupDiGetClassDevsW(const GUID *ClassGuid, PCWSTR Enumerator, - HWND hwndParent, DWORD Flags) { +WINAPI HANDLE SetupDiGetClassDevsW(const GUID *ClassGuid, PCWSTR Enumerator, + HWND hwndParent, DWORD Flags) { pSetupDiGetClassDevsW pfunc = (pSetupDiGetClassDevsW)get_proc_address("SetupDiGetClassDevsW"); return pfunc(ClassGuid, Enumerator, hwndParent, Flags); } -__stdcall BOOL SetupDiEnumDeviceInfo(HANDLE DeviceInfoSet, DWORD MemberIndex, - LPVOID DeviceInfoData) { +WINAPI BOOL SetupDiEnumDeviceInfo(HANDLE DeviceInfoSet, DWORD MemberIndex, + LPVOID DeviceInfoData) { pSetupDiEnumDeviceInfo pfunc = (pSetupDiEnumDeviceInfo)get_proc_address("SetupDiEnumDeviceInfo"); return pfunc(DeviceInfoSet, MemberIndex, DeviceInfoData); } -_stdcall BOOL SetupDiEnumDeviceInterfaces(HANDLE DeviceInfoSet, - LPVOID DeviceInfoData, - const GUID *InterfaceClassGuid, - DWORD MemberIndex, - HANDLE DeviceInterfaceData) { +WINAPI BOOL SetupDiEnumDeviceInterfaces(HANDLE DeviceInfoSet, + LPVOID DeviceInfoData, + const GUID *InterfaceClassGuid, + DWORD MemberIndex, + HANDLE DeviceInterfaceData) { pSetupDiEnumDeviceInterfaces pfunc = (pSetupDiEnumDeviceInterfaces)get_proc_address( "SetupDiEnumDeviceInterfaces"); @@ -944,11 +1285,10 @@ _stdcall BOOL SetupDiEnumDeviceInterfaces(HANDLE DeviceInfoSet, DeviceInterfaceData); } -__stdcall BOOL -SetupDiGetDevicePropertyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, - const LPVOID *PropertyKey, LPVOID *PropertyType, - PBYTE PropertyBuffer, DWORD PropertyBufferSize, - PDWORD RequiredSize, DWORD Flags) +WINAPI BOOL SetupDiGetDevicePropertyW( + HANDLE DeviceInfoSet, LPVOID DeviceInfoData, const LPVOID *PropertyKey, + LPVOID *PropertyType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, + PDWORD RequiredSize, DWORD Flags) { pSetupDiGetDevicePropertyW pfunc = @@ -957,67 +1297,68 @@ SetupDiGetDevicePropertyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, PropertyBuffer, PropertyBufferSize, RequiredSize, Flags); } -__stdcall BOOL SetupDiDestroyDeviceInfoList(HANDLE DeviceInfoSet) { +WINAPI BOOL SetupDiDestroyDeviceInfoList(HANDLE DeviceInfoSet) { pSetupDiDestroyDeviceInfoList pfunc = (pSetupDiDestroyDeviceInfoList)get_proc_address( "SetupDiDestroyDeviceInfoList"); return pfunc(DeviceInfoSet); } -__stdcall HANDLE SetupDiCreateDeviceInfoList(const GUID *ClassGuid, - HWND hwndParent) { +WINAPI HANDLE SetupDiCreateDeviceInfoList(const GUID *ClassGuid, + HWND hwndParent) { pSetupDiCreateDeviceInfoList pfunc = (pSetupDiCreateDeviceInfoList)get_proc_address( "SetupDiCreateDeviceInfoList"); return pfunc(ClassGuid, hwndParent); } -__stdcall BOOL -SetupDiSetDevicePropertyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, - const LPVOID *PropertyKey, LPVOID PropertyType, - const PBYTE PropertyBuffer, DWORD PropertyBufferSize, - DWORD Flags) { +WINAPI BOOL SetupDiSetDevicePropertyW(HANDLE DeviceInfoSet, + LPVOID DeviceInfoData, + const LPVOID *PropertyKey, + LPVOID PropertyType, + const PBYTE PropertyBuffer, + DWORD PropertyBufferSize, DWORD Flags) { pSetupDiSetDevicePropertyW pfunc = (pSetupDiSetDevicePropertyW)get_proc_address("SetupDiSetDevicePropertyW"); return pfunc(DeviceInfoSet, DeviceInfoData, PropertyKey, PropertyType, PropertyBuffer, PropertyBufferSize, Flags); } -__stdcall BOOL SetupDiCreateDeviceInfoW(HANDLE DeviceInfoSet, PCWSTR DeviceName, - const GUID *ClassGuid, - PCWSTR DeviceDescription, - HWND hwndParent, DWORD CreationFlags, - LPVOID DeviceInfoData) { +WINAPI BOOL SetupDiCreateDeviceInfoW(HANDLE DeviceInfoSet, PCWSTR DeviceName, + const GUID *ClassGuid, + PCWSTR DeviceDescription, HWND hwndParent, + DWORD CreationFlags, + LPVOID DeviceInfoData) { pSetupDiCreateDeviceInfoW pfunc = (pSetupDiCreateDeviceInfoW)get_proc_address("SetupDiCreateDeviceInfoW"); return pfunc(DeviceInfoSet, DeviceName, ClassGuid, DeviceDescription, hwndParent, CreationFlags, DeviceInfoData); } -__stdcall BOOL SetupDiOpenDeviceInfoW(HANDLE DeviceInfoSet, - PCWSTR DeviceInstanceId, HWND hwndParent, - DWORD OpenFlags, LPVOID DeviceInfoData) { +WINAPI BOOL SetupDiOpenDeviceInfoW(HANDLE DeviceInfoSet, + PCWSTR DeviceInstanceId, HWND hwndParent, + DWORD OpenFlags, LPVOID DeviceInfoData) { pSetupDiOpenDeviceInfoW pfunc = (pSetupDiOpenDeviceInfoW)get_proc_address("SetupDiOpenDeviceInfoW"); return pfunc(DeviceInfoSet, DeviceInstanceId, hwndParent, OpenFlags, DeviceInfoData); } -__stdcall BOOL SetupDiRegisterDeviceInfo(HANDLE DeviceInfoSet, - LPVOID DeviceInfoData, DWORD Flags, - LPVOID CompareProc, - PVOID CompareContext, - LPVOID DupDeviceInfoData) { +WINAPI BOOL SetupDiRegisterDeviceInfo(HANDLE DeviceInfoSet, + LPVOID DeviceInfoData, DWORD Flags, + LPVOID CompareProc, PVOID CompareContext, + LPVOID DupDeviceInfoData) { pSetupDiRegisterDeviceInfo pfunc = (pSetupDiRegisterDeviceInfo)get_proc_address("SetupDiRegisterDeviceInfo"); return pfunc(DeviceInfoSet, DeviceInfoData, Flags, CompareProc, CompareContext, DupDeviceInfoData); } -__stdcall BOOL -SetupDiSetDeviceRegistryPropertyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, - DWORD Property, const BYTE *PropertyBuffer, - DWORD PropertyBufferSize) +WINAPI BOOL SetupDiSetDeviceRegistryPropertyW(HANDLE DeviceInfoSet, + LPVOID DeviceInfoData, + DWORD Property, + const BYTE *PropertyBuffer, + DWORD PropertyBufferSize) { pSetupDiSetDeviceRegistryPropertyW pfunc = @@ -1027,19 +1368,16 @@ SetupDiSetDeviceRegistryPropertyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, PropertyBufferSize); } -__stdcall HKEY SetupDiCreateDevRegKeyW(HANDLE DeviceInfoSet, - LPVOID DeviceInfoData, DWORD Scope, - DWORD HwProfile, DWORD KeyType, - HANDLE InfHandle, - PCWSTR InfSectionName) { +WINAPI HKEY SetupDiCreateDevRegKeyW(HANDLE DeviceInfoSet, LPVOID DeviceInfoData, + DWORD Scope, DWORD HwProfile, DWORD KeyType, + HANDLE InfHandle, PCWSTR InfSectionName) { pSetupDiCreateDevRegKeyW pfunc = (pSetupDiCreateDevRegKeyW)get_proc_address("SetupDiCreateDevRegKeyW"); return pfunc(DeviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, InfHandle, InfSectionName); } -__stdcall BOOL SetupDiRemoveDevice(HANDLE DeviceInfoSet, - LPVOID DeviceInfoData) { +WINAPI BOOL SetupDiRemoveDevice(HANDLE DeviceInfoSet, LPVOID DeviceInfoData) { pSetupDiRemoveDevice pfunc = (pSetupDiRemoveDevice)get_proc_address("SetupDiRemoveDevice"); return pfunc(DeviceInfoSet, DeviceInfoData); diff --git a/wine/setupapi.dll b/wine/setupapi.dll old mode 100755 new mode 100644 index e6b1479..c66a7f7 Binary files a/wine/setupapi.dll and b/wine/setupapi.dll differ