mirror of
https://github.com/radiomanV/TL866.git
synced 2025-06-18 14:25:38 -04:00
1401 lines
51 KiB
C
1401 lines
51 KiB
C
/*
|
|
* 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 <glob.h>
|
|
#ifdef UDEV
|
|
#include <libudev.h>
|
|
#endif
|
|
#include <libusb-1.0/libusb.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <dbt.h>
|
|
#include <winbase.h>
|
|
#include <windef.h>
|
|
#include <winnt.h>
|
|
|
|
// Defines
|
|
#define TL866A_VID 0x04d8
|
|
#define TL866A_PID 0xe11c
|
|
#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 {
|
|
libusb_device_handle *handle;
|
|
UCHAR PipeID;
|
|
PUCHAR Buffer;
|
|
ULONG BufferLength;
|
|
PUINT LengthTransferred;
|
|
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
|
|
int debug = 0;
|
|
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;
|
|
|
|
HANDLE hotplug_thread;
|
|
HWND hWnd;
|
|
|
|
pMessageBoxA message_box;
|
|
pGetForegroundWindow get_foreground_window;
|
|
pSendMessageA send_message;
|
|
pRedrawWindow redraw_window;
|
|
|
|
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.
|
|
const unsigned char xgpro_open_devices_pattern1[] = {
|
|
0x53, 0x57, 0x6A, 0x00, 0x68, 0x80, 0x00, 0x00, 0x40, 0x6A, 0x03,
|
|
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};
|
|
|
|
// 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};
|
|
|
|
// Print given array in hex
|
|
void print_hex(const unsigned char *buffer, unsigned int size) {
|
|
unsigned int i;
|
|
for (i = 0; i < size; i++) {
|
|
printf("%02X ", buffer[i]);
|
|
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]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
// USB open/close function replacement
|
|
void close_devices() {
|
|
if (devs != NULL) {
|
|
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);
|
|
devs = NULL;
|
|
}
|
|
}
|
|
|
|
int open_devices() {
|
|
close_devices();
|
|
device_handle[0] = NULL;
|
|
device_handle[1] = NULL;
|
|
device_handle[2] = NULL;
|
|
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
|
|
#if LIBUSB_API_VERSION >= 0x01000106
|
|
libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, 3);
|
|
#else
|
|
libusb_set_debug(NULL, 3);
|
|
#endif
|
|
|
|
usb_handle[0] = 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;
|
|
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);
|
|
|
|
if (count < 0) {
|
|
return 0;
|
|
}
|
|
|
|
char name[128];
|
|
for (int i = 0; i < count; i++) {
|
|
ret = libusb_get_device_descriptor(devs[i], &desc);
|
|
if (ret != LIBUSB_SUCCESS) {
|
|
return 0;
|
|
}
|
|
|
|
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) ==
|
|
LIBUSB_SUCCESS) {
|
|
usb_handle[devices_found] = (HANDLE)devices_found;
|
|
if (device_vid == TL866II_VID) {
|
|
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++;
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Helper functions to set/get timeout from PipeID
|
|
int get_timeout(UCHAR PipeID) {
|
|
return endpoints[(PipeID > 80 ? 1 : 0)][(PipeID & 0x7f) - 1].timeout;
|
|
}
|
|
|
|
void set_timeout(UCHAR PipeID, int ep_timeout) {
|
|
endpoints[(PipeID > 80 ? 1 : 0)][(PipeID & 0x7f) - 1].timeout = ep_timeout;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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, EP3_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, EP3_IN=0x83, EP4_OUT=0x04, EP4_IN=0x84, *
|
|
* EP5_OUT=0x05, EP5_IN=0x85, EP6_OUT=0x06, EP6_IN=0x86 *
|
|
* EP7_OUT=0x07, EP7_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) {
|
|
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) {
|
|
return uwrite(0, lpInBuffer, nInBufferSize);
|
|
}
|
|
|
|
// 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) {
|
|
return uwrite(hDevice, lpInBuffer, nInBufferSize);
|
|
}
|
|
|
|
// USB read from specified device
|
|
int usb_read2(HANDLE hDevice, unsigned char *lpOutBuffer,
|
|
unsigned int nBytesToRead, unsigned int nOutBufferSize) {
|
|
return uread(hDevice, lpOutBuffer, nBytesToRead);
|
|
}
|
|
|
|
// 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) {
|
|
return -1;
|
|
}
|
|
|
|
struct udev_enumerate *enumerate;
|
|
struct udev_list_entry *devices, *dev_list_entry;
|
|
struct udev_device *dev;
|
|
|
|
enumerate = udev_enumerate_new(udev);
|
|
udev_enumerate_add_match_subsystem(enumerate, "usb");
|
|
udev_enumerate_scan_devices(enumerate);
|
|
devices = udev_enumerate_get_list_entry(enumerate);
|
|
int count = 0;
|
|
udev_list_entry_foreach(dev_list_entry, devices) {
|
|
dev = udev_device_new_from_syspath(
|
|
udev, udev_list_entry_get_name(dev_list_entry));
|
|
if (!dev)
|
|
return -1;
|
|
|
|
const char *vid = udev_device_get_sysattr_value(dev, "idVendor");
|
|
const char *pid = udev_device_get_sysattr_value(dev, "idProduct");
|
|
if (vid && pid && strtoul(vid, NULL, 16) == device_vid &&
|
|
strtoul(pid, NULL, 16) == device_pid)
|
|
count++;
|
|
udev_device_unref(dev);
|
|
}
|
|
udev_enumerate_unref(enumerate);
|
|
udev_unref(udev);
|
|
return count;
|
|
}
|
|
|
|
// Udev hotplug USB monitoring thread
|
|
void notifier_function() {
|
|
struct udev *udev;
|
|
struct udev_monitor *mon;
|
|
struct udev_device *dev;
|
|
|
|
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 udev_mon_fd = udev_monitor_get_fd(mon);
|
|
|
|
// Enter the monitoring loop
|
|
cancel = FALSE;
|
|
while (!cancel) {
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
int ret;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(udev_mon_fd, &fds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
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;
|
|
if (!strcasecmp(udev_device_get_action(dev), "add")) {
|
|
count_new = get_device_count();
|
|
if (count != count_new) {
|
|
count = count_new;
|
|
// printf("device added.\n");
|
|
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");
|
|
device_changed(DBT_DEVICEREMOVECOMPLETE);
|
|
}
|
|
}
|
|
udev_device_unref(dev);
|
|
}
|
|
}
|
|
usleep(50000);
|
|
}
|
|
udev_monitor_unref(mon);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
|
|
void *BaseAddress = GetModuleHandleA(NULL);
|
|
PIMAGE_NT_HEADERS NtHeader =
|
|
(PIMAGE_NT_HEADERS)((PBYTE)BaseAddress +
|
|
((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew);
|
|
PIMAGE_IMPORT_DESCRIPTOR ImpDesc =
|
|
(PIMAGE_IMPORT_DESCRIPTOR)((PBYTE)BaseAddress +
|
|
NtHeader->OptionalHeader
|
|
.DataDirectory
|
|
[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
|
.VirtualAddress);
|
|
|
|
// Search for library in the import directory
|
|
while (ImpDesc->Characteristics && ImpDesc->Name) {
|
|
if (strcasecmp(BaseAddress + ImpDesc->Name, library) == 0) {
|
|
break; // Found it!
|
|
}
|
|
ImpDesc++;
|
|
}
|
|
|
|
// check if the library was found in the import directory
|
|
if (!ImpDesc->Characteristics) {
|
|
printf("Library '%s' was not found in the import directory.\n", library);
|
|
return FALSE;
|
|
}
|
|
|
|
// If the desired library was found we can get the function address
|
|
DWORD_PTR ProcAddress =
|
|
(DWORD_PTR)GetProcAddress(GetModuleHandleA(library), func_name);
|
|
|
|
// 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 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)custom_func;
|
|
VirtualProtect(info.BaseAddress, info.RegionSize, info.Protect,
|
|
&dwOldProtection);
|
|
}
|
|
thunk++;
|
|
}
|
|
|
|
// check if the patch was ok.
|
|
if (!func_addr) {
|
|
printf("Function '%s' was not found in the IAT thunk table.\n", func_name);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// 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 = X86_PUSH;
|
|
*((DWORD *)((BYTE *)src + 1)) = (DWORD)dest;
|
|
*((BYTE *)src + 5) = X86_RET;
|
|
}
|
|
|
|
// 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
|
|
void *BaseAddress = GetModuleHandleA(NULL);
|
|
PIMAGE_NT_HEADERS NtHeader =
|
|
(PIMAGE_NT_HEADERS)((PBYTE)BaseAddress +
|
|
((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew);
|
|
|
|
// 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);
|
|
|
|
// Patch the Linux incompatible functions
|
|
if (!patch_function("user32.dll", "RegisterDeviceNotificationA",
|
|
&RegisterDeviceNotifications))
|
|
return FALSE;
|
|
|
|
if (!patch_function("winusb.dll", "WinUsb_SetPipePolicy",
|
|
&WinUsb_SetPipePolicy))
|
|
return FALSE;
|
|
|
|
if (!patch_function("winusb.dll", "WinUsb_WritePipe", &WinUsb_Transfer))
|
|
return FALSE;
|
|
|
|
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;
|
|
void *p_winusbhandle = NULL;
|
|
void *p_usbhandle = NULL;
|
|
void *p_devicescount = NULL;
|
|
|
|
// 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)
|
|
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)) +
|
|
(DWORD)((unsigned char *)p_opendevices + 9);
|
|
p_winusbhandle = (void *)(*(int *)((unsigned char *)p_closedevices + 0x12));
|
|
p_usbhandle = (void *)(*(int *)((unsigned char *)p_closedevices + 0x2));
|
|
p_devicescount = (void *)(*(int *)((unsigned char *)p_opendevices + 0xAF));
|
|
} else if (p_od2) {
|
|
p_opendevices = p_od2 - 0x41;
|
|
p_closedevices = (void *)(*(int *)((unsigned char *)p_opendevices + 8)) +
|
|
(DWORD)((unsigned char *)p_opendevices + 12);
|
|
p_winusbhandle = (void *)(*(int *)((unsigned char *)p_closedevices + 0x12));
|
|
p_usbhandle = (void *)(*(int *)((unsigned char *)p_closedevices + 0x2));
|
|
p_devicescount = (void *)(*(int *)((unsigned char *)p_opendevices + 0x28));
|
|
if (!patch_function("winusb.dll", "WinUsb_AbortPipe", &WinUsb_AbortPipe))
|
|
return FALSE;
|
|
|
|
if (!patch_function("winusb.dll", "WinUsb_FlushPipe", &WinUsb_FlushPipe))
|
|
return FALSE;
|
|
} else {
|
|
printf("Function signatures not found! Unsupported Xgpro version.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// Print 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 %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, 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);
|
|
|
|
// patch open_devices function to point to our implementation
|
|
patch(p_opendevices, &open_devices);
|
|
|
|
// 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);
|
|
return TRUE;
|
|
}
|
|
|
|
// 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
|
|
void *BaseAddress = GetModuleHandleA(NULL);
|
|
PIMAGE_NT_HEADERS NtHeader =
|
|
(PIMAGE_NT_HEADERS)((PBYTE)BaseAddress +
|
|
((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew);
|
|
|
|
unsigned char *version =
|
|
memmem(BaseAddress, NtHeader->OptionalHeader.SizeOfImage, "MiniPro v", 9);
|
|
if (!version) return FALSE;
|
|
printf("Found %s\n", version);
|
|
|
|
// Patch the Linux incompatible functions functions
|
|
if (!patch_function("user32.dll", "RegisterDeviceNotificationA",
|
|
&RegisterDeviceNotifications))
|
|
return FALSE;
|
|
|
|
// Searching for functions signature in code section.
|
|
void *p_opendevices =
|
|
memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode, &minipro_open_devices_pattern,
|
|
sizeof(minipro_open_devices_pattern)) -
|
|
0x28;
|
|
void *p_closedevices =
|
|
(void *)(*(int *)((unsigned char *)p_opendevices + 4)) +
|
|
(DWORD)((unsigned char *)p_opendevices + 8);
|
|
void *p_usbwrite = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode,
|
|
&usb_write_pattern, sizeof(usb_write_pattern)) -
|
|
0x0A;
|
|
void *p_usbwrite2 = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode,
|
|
&usb_write2_pattern, sizeof(usb_write2_pattern)) -
|
|
0x0A;
|
|
void *p_usbread = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode,
|
|
&usb_read_pattern, sizeof(usb_read_pattern));
|
|
void *p_usbread2 = memmem(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode,
|
|
&usb_read2_pattern, sizeof(usb_read2_pattern));
|
|
void *p_usbhandle = (void *)(*(int *)((unsigned char *)p_closedevices + 1));
|
|
|
|
// check if all pointers are o.k.
|
|
if (!p_opendevices || !p_usbwrite || !p_usbwrite2 || !p_usbread ||
|
|
!p_usbread2) {
|
|
printf("Function signatures not found! Unsupported MiniPro version.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// 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 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 %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.
|
|
|
|
// 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);
|
|
|
|
// patch open_devices function
|
|
patch(p_opendevices, &open_devices);
|
|
|
|
// patch close_devices function
|
|
patch(p_closedevices, &close_devices);
|
|
|
|
// patch usb_write function
|
|
patch(p_usbwrite, &usb_write);
|
|
|
|
// patch usb_read function
|
|
patch(p_usbread, &usb_read);
|
|
|
|
// patch usb_write2 function
|
|
patch(p_usbwrite2, &usb_write2);
|
|
|
|
// patch usb_read2 function
|
|
patch(p_usbread2, &usb_read2);
|
|
|
|
// patch the brick bug
|
|
if (p_brickbug) *(p_brickbug + 0x08) = X86_JMP;
|
|
|
|
// Restore the old READ_ONLY protection
|
|
VirtualProtect(BaseAddress + NtHeader->OptionalHeader.BaseOfCode,
|
|
NtHeader->OptionalHeader.SizeOfCode, dwOldProtection,
|
|
&dwOldProtection);
|
|
|
|
// Set the Minipro GUID
|
|
memcpy(&m_guid, &MINIPRO_GUID, sizeof(GUID));
|
|
|
|
// Set the vid/pid
|
|
device_vid = TL866A_VID;
|
|
device_pid = TL866A_PID;
|
|
return TRUE;
|
|
}
|
|
|
|
/*** DllMain ***/
|
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
|
// 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;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/********************** Exported functions section ***********************/
|
|
/*************************************************************************/
|
|
|
|
// 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 BOOL (*pSetupDiDestroyDeviceInfoList)(HANDLE DeviceInfoSet);
|
|
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);
|
|
typedef void (WINAPI *pInstallHinfSectionA)(HWND, HINSTANCE, PCSTR, INT);
|
|
typedef void (WINAPI *pInstallHinfSectionW)(HWND, HINSTANCE, PCWSTR, INT);
|
|
|
|
// 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 : %p\n", lpProcName, address);
|
|
return address;
|
|
}
|
|
|
|
WINAPI BOOL SetupDiGetDeviceInterfaceDetailW(
|
|
HANDLE DeviceInfoSet, HANDLE DeviceInterfaceData,
|
|
HANDLE DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize,
|
|
PDWORD RequiredSize, LPVOID DeviceInfoData) {
|
|
pSetupDiGetDeviceInterfaceDetailW pfunc =
|
|
(pSetupDiGetDeviceInterfaceDetailW)get_proc_address(
|
|
"SetupDiGetDeviceInterfaceDetailW");
|
|
return pfunc(DeviceInfoSet, DeviceInterfaceData, DeviceInterfaceDetailData,
|
|
DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiGetDeviceRegistryPropertyW(
|
|
HANDLE DeviceInfoSet, LPVOID DeviceInfoData, DWORD Property,
|
|
PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize,
|
|
PDWORD RequiredSize) {
|
|
pSetupDiGetDeviceRegistryPropertyW pfunc =
|
|
(pSetupDiGetDeviceRegistryPropertyW)get_proc_address(
|
|
"SetupDiGetDeviceRegistryPropertyW");
|
|
return pfunc(DeviceInfoSet, DeviceInfoData, Property, PropertyRegDataType,
|
|
PropertyBuffer, PropertyBufferSize, RequiredSize);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiCallClassInstaller(LPVOID InstallFunction,
|
|
HANDLE DeviceInfoSet,
|
|
LPVOID DeviceInfoDat) {
|
|
pSetupDiCallClassInstaller pfunc =
|
|
(pSetupDiCallClassInstaller)get_proc_address("SetupDiCallClassInstaller");
|
|
return pfunc(InstallFunction, DeviceInfoSet, DeviceInfoDat);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiEnumDeviceInfo(HANDLE DeviceInfoSet, DWORD MemberIndex,
|
|
LPVOID DeviceInfoData) {
|
|
pSetupDiEnumDeviceInfo pfunc =
|
|
(pSetupDiEnumDeviceInfo)get_proc_address("SetupDiEnumDeviceInfo");
|
|
return pfunc(DeviceInfoSet, MemberIndex, DeviceInfoData);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiEnumDeviceInterfaces(HANDLE DeviceInfoSet,
|
|
LPVOID DeviceInfoData,
|
|
const GUID *InterfaceClassGuid,
|
|
DWORD MemberIndex,
|
|
HANDLE DeviceInterfaceData) {
|
|
pSetupDiEnumDeviceInterfaces pfunc =
|
|
(pSetupDiEnumDeviceInterfaces)get_proc_address(
|
|
"SetupDiEnumDeviceInterfaces");
|
|
return pfunc(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex,
|
|
DeviceInterfaceData);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiGetDevicePropertyW(
|
|
HANDLE DeviceInfoSet, LPVOID DeviceInfoData, const LPVOID *PropertyKey,
|
|
LPVOID *PropertyType, PBYTE PropertyBuffer, DWORD PropertyBufferSize,
|
|
PDWORD RequiredSize, DWORD Flags)
|
|
|
|
{
|
|
pSetupDiGetDevicePropertyW pfunc =
|
|
(pSetupDiGetDevicePropertyW)get_proc_address("SetupDiGetDevicePropertyW");
|
|
return pfunc(DeviceInfoSet, DeviceInfoData, PropertyKey, PropertyType,
|
|
PropertyBuffer, PropertyBufferSize, RequiredSize, Flags);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiDestroyDeviceInfoList(HANDLE DeviceInfoSet) {
|
|
pSetupDiDestroyDeviceInfoList pfunc =
|
|
(pSetupDiDestroyDeviceInfoList)get_proc_address(
|
|
"SetupDiDestroyDeviceInfoList");
|
|
return pfunc(DeviceInfoSet);
|
|
}
|
|
|
|
WINAPI HANDLE SetupDiCreateDeviceInfoList(const GUID *ClassGuid,
|
|
HWND hwndParent) {
|
|
pSetupDiCreateDeviceInfoList pfunc =
|
|
(pSetupDiCreateDeviceInfoList)get_proc_address(
|
|
"SetupDiCreateDeviceInfoList");
|
|
return pfunc(ClassGuid, hwndParent);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiSetDeviceRegistryPropertyW(HANDLE DeviceInfoSet,
|
|
LPVOID DeviceInfoData,
|
|
DWORD Property,
|
|
const BYTE *PropertyBuffer,
|
|
DWORD PropertyBufferSize)
|
|
|
|
{
|
|
pSetupDiSetDeviceRegistryPropertyW pfunc =
|
|
(pSetupDiSetDeviceRegistryPropertyW)get_proc_address(
|
|
"SetupDiSetDeviceRegistryPropertyW");
|
|
return pfunc(DeviceInfoSet, DeviceInfoData, Property, PropertyBuffer,
|
|
PropertyBufferSize);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
WINAPI BOOL SetupDiRemoveDevice(HANDLE DeviceInfoSet, LPVOID DeviceInfoData) {
|
|
pSetupDiRemoveDevice pfunc =
|
|
(pSetupDiRemoveDevice)get_proc_address("SetupDiRemoveDevice");
|
|
return pfunc(DeviceInfoSet, DeviceInfoData);
|
|
}
|
|
|
|
WINAPI void InstallHinfSectionA(HWND Window, HINSTANCE ModuleHandle,
|
|
PCSTR CommandLine, INT ShowCommand) {
|
|
pInstallHinfSectionA pfunc =
|
|
(pInstallHinfSectionA)get_proc_address("InstallHinfSectionA");
|
|
return pfunc(Window, ModuleHandle, CommandLine, ShowCommand);
|
|
}
|
|
|
|
WINAPI void InstallHinfSectionW(HWND Window, HINSTANCE ModuleHandle,
|
|
PCWSTR CommandLine, INT ShowCommand) {
|
|
pInstallHinfSectionW pfunc =
|
|
(pInstallHinfSectionW)get_proc_address("InstallHinfSectionW");
|
|
return pfunc(Window, ModuleHandle, CommandLine, ShowCommand);
|
|
}
|