From 4cfe489d983df6b8b599416ecb05f40d0d299762 Mon Sep 17 00:00:00 2001
From: cnlohr <lohr85@gmail.com>
Date: Sun, 5 Mar 2023 12:26:57 -0500
Subject: [PATCH] Add basic high-level outline of how drivers can work (Does
 not currently compile).

---
 minichlink/Makefile        |   2 +-
 minichlink/minichlink.c    | 225 +++++++++++--------------
 minichlink/minichlink.h    |  36 ++++
 minichlink/wch-linke.c     | 330 +++++++++++++++++++++++++++++++++++++
 minichlink/wch_link_base.h | 129 ---------------
 5 files changed, 467 insertions(+), 255 deletions(-)
 create mode 100644 minichlink/minichlink.h
 create mode 100644 minichlink/wch-linke.c
 delete mode 100644 minichlink/wch_link_base.h

diff --git a/minichlink/Makefile b/minichlink/Makefile
index bc4b278..30c3a07 100644
--- a/minichlink/Makefile
+++ b/minichlink/Makefile
@@ -5,7 +5,7 @@ all : $(TOOLS)
 CFLAGS:=-O1 -g
 LDFLAGS:=-lpthread -lusb-1.0
 
-minichlink : minichlink.c
+minichlink : minichlink.c wch-linke.c
 	gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
 
 install_udev_rules :
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index 5c5bc9d..0e63967 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -5,59 +5,40 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include "wch_link_base.h"
-
-
-const uint8_t * bootloader = (const uint8_t*)
-"\x21\x11\x22\xca\x26\xc8\x93\x77\x15\x00\x99\xcf\xb7\x06\x67\x45" \
-"\xb7\x27\x02\x40\x93\x86\x36\x12\x37\x97\xef\xcd\xd4\xc3\x13\x07" \
-"\xb7\x9a\xd8\xc3\xd4\xd3\xd8\xd3\x93\x77\x25\x00\x9d\xc7\xb7\x27" \
-"\x02\x40\x98\x4b\xad\x66\x37\x33\x00\x40\x13\x67\x47\x00\x98\xcb" \
-"\x98\x4b\x93\x86\xa6\xaa\x13\x67\x07\x04\x98\xcb\xd8\x47\x05\x8b" \
-"\x63\x16\x07\x10\x98\x4b\x6d\x9b\x98\xcb\x93\x77\x45\x00\xa9\xcb" \
-"\x93\x07\xf6\x03\x99\x83\x2e\xc0\x2d\x63\x81\x76\x3e\xc4\xb7\x32" \
-"\x00\x40\xb7\x27\x02\x40\x13\x03\xa3\xaa\xfd\x16\x98\x4b\xb7\x03" \
-"\x02\x00\x33\x67\x77\x00\x98\xcb\x02\x47\xd8\xcb\x98\x4b\x13\x67" \
-"\x07\x04\x98\xcb\xd8\x47\x05\x8b\x69\xe7\x98\x4b\x75\x8f\x98\xcb" \
-"\x02\x47\x13\x07\x07\x04\x3a\xc0\x22\x47\x7d\x17\x3a\xc4\x79\xf7" \
-"\x93\x77\x85\x00\xf1\xcf\x93\x07\xf6\x03\x2e\xc0\x99\x83\x37\x27" \
-"\x02\x40\x3e\xc4\x1c\x4b\xc1\x66\x2d\x63\xd5\x8f\x1c\xcb\x37\x07" \
-"\x00\x20\x13\x07\x07\x20\xb7\x27\x02\x40\xb7\x03\x08\x00\xb7\x32" \
-"\x00\x40\x13\x03\xa3\xaa\x94\x4b\xb3\xe6\x76\x00\x94\xcb\xd4\x47" \
-"\x85\x8a\xf5\xfe\x82\x46\xba\x84\x37\x04\x04\x00\x36\xc2\xc1\x46" \
-"\x36\xc6\x92\x46\x84\x40\x11\x07\x84\xc2\x94\x4b\xc1\x8e\x94\xcb" \
-"\xd4\x47\x85\x8a\xb1\xea\x92\x46\xba\x84\x91\x06\x36\xc2\xb2\x46" \
-"\xfd\x16\x36\xc6\xf9\xfe\x82\x46\xd4\xcb\x94\x4b\x93\xe6\x06\x04" \
-"\x94\xcb\xd4\x47\x85\x8a\x85\xee\xd4\x47\xc1\x8a\x85\xce\xd8\x47" \
-"\xb7\x06\xf3\xff\xfd\x16\x13\x67\x07\x01\xd8\xc7\x98\x4b\x21\x45" \
-"\x75\x8f\x98\xcb\x52\x44\xc2\x44\x61\x01\x02\x90\x23\x20\xd3\x00" \
-"\xf5\xb5\x23\xa0\x62\x00\x3d\xb7\x23\xa0\x62\x00\x55\xb7\x23\xa0" \
-"\x62\x00\xc1\xb7\x82\x46\x93\x86\x06\x04\x36\xc0\xa2\x46\xfd\x16" \
-"\x36\xc4\xb5\xf2\x98\x4b\xb7\x06\xf3\xff\xfd\x16\x75\x8f\x98\xcb" \
-"\x41\x89\x05\xcd\x2e\xc0\x0d\x06\x02\xc4\x09\x82\xb7\x07\x00\x20" \
-"\x32\xc6\x93\x87\x07\x20\x98\x43\x13\x86\x47\x00\xa2\x47\x82\x46" \
-"\x8a\x07\xb6\x97\x9c\x43\x63\x1c\xf7\x00\xa2\x47\x85\x07\x3e\xc4" \
-"\xa2\x46\x32\x47\xb2\x87\xe3\xe0\xe6\xfe\x01\x45\x61\xb7\x41\x45" \
-"\x51\xb7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
-"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
-"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+#include "minichlink.h"
 
 static int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber );
 
-int bootloader_len = 512;
+extern struct MiniChlinkFunctions MCF;
 
 int main( int argc, char ** argv )
 {
+	void * dev = 0;
+	if( !(dev = TryInit_WCHLinkE() ) )
+	else if( !(dev = TryInit_ESP32S2CHFUN() ) )
+	else
+	{
+		fprintf( stderr, "Error: Could not initialize any supported programmers\n" );
+		return -32;
+	}
+
 	int status;
 	int must_be_end = 0;
 	uint8_t rbuff[1024];
 
-	libusb_device_handle * devh = wch_link_base_setup(0);
+	if( MCF.SetupInterface )
+	{
+		if( MCF.SetupInterface() < 0 )
+		{
+			fprintf( stderr, "Could not setup interface.\n" );
+			return -33;
+		}
+	}
 
 	int iarg;
+	char * argchar = argv[iarg];
 	for( iarg = 1; iarg < argc; iarg++ )
 	{
-		char * argchar = argv[iarg];
 		if( argchar[0] != '-' ) goto help;
 		if( must_be_end )
 		{
@@ -69,28 +50,63 @@ keep_going:
 		switch( argchar[1] )
 		{
 			default: goto help;
-			case '3': wch_link_command( devh, "\x81\x0d\x01\x09", 4, 0, 0, 0 ); break;
-			case '5': wch_link_command( devh, "\x81\x0d\x01\x0b", 4, 0, 0, 0 ); break;
-			case 't': wch_link_command( devh, "\x81\x0d\x01\x0a", 4, 0, 0, 0 ); break;
-			case 'f': wch_link_command( devh, "\x81\x0d\x01\x0c", 4, 0, 0, 0 ); break;
-			case 'u': wch_link_command( devh, "\x81\x0d\x01\x0f\x09", 5, 0, 0, 0 ); break;
+			case '3':
+				if( MCF.Control3v3 )
+					MCF.Control3v3( 1 );
+				else
+					goto unimplemented;
+				break;
+			case '5':
+				if( MCF.Control5 )
+					MCF.Control5( 1 );
+				else
+					goto unimplemented;
+				break;
+			case 't':
+				if( MCF.Control3v3 )
+					MCF.Control3v3( 0 );
+				else
+					goto unimplemented;
+				break;
+			case 'f':
+				if( MCF.Control5 )
+					MCF.Control5( 0 );
+				else
+					goto unimplemented;
+				break;
+			case 'u':
+				if( MCF.Unbrick )
+					MCF.Unbrick();
+				else
+					goto unimplemented;
+				break;
 			case 'r': 
-				// This is clearly not the "best" method to exit reset.  I don't know why this combination works.
-				wch_link_multicommands( devh, 3, 4, "\x81\x0b\x01\x01", 4, "\x81\x0d\x01\x02", 4, "\x81\x0d\x01\xff" );
+				if( MCF.HaltMode )
+					MCF.HaltMode( 0 );
+				else
+					goto unimplemented;
 				must_be_end = 'r';
 				break;
 			case 'R':
-				// Part one "immediately" places the part into reset.  Part 2 says when we're done, leave part in reset.
-				wch_link_multicommands( devh, 2, 4, "\x81\x0d\x01\x02", 4, "\x81\x0d\x01\x01" );
+				if( MCF.HaltMode )
+					MCF.HaltMode( 1 );
+				else
+					goto unimplemented;
 				must_be_end = 'R';
 				break;
 
 			// disable NRST pin (turn it into a GPIO)
 			case 'd':  // see "RSTMODE" in datasheet
-				wch_link_multicommands( devh, 2, 11, "\x81\x06\x08\x02\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+				if( MCF.ConfigureNRSTAsGPIO )
+					MCF.ConfigureNRSTAsGPIO( 0 );
+				else
+					goto unimplemented;
 				break;
 			case 'D': // see "RSTMODE" in datasheet
-				wch_link_multicommands( devh, 2, 11, "\x81\x06\x08\x02\xff\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+				if( MCF.ConfigureNRSTAsGPIO )
+					MCF.ConfigureNRSTAsGPIO( 1 );
+				else
+					goto unimplemented;
 				break;
 			// PROTECTION UNTESTED!
 			/*
@@ -142,50 +158,21 @@ keep_going:
 				}
 				uint32_t * readbuff = malloc( amount );
 				int readbuffplace = 0;
-				wch_link_command( devh, "\x81\x06\x01\x01", 4, 0, 0, 0 );
-
-				// Flush out any pending data.
-				libusb_bulk_transfer( devh, 0x82, rbuff, 1024, &transferred, 1 );
-
-				// 3/8 = Read Memory
-				// First 4 bytes are big-endian location.
-				// Next 4 bytes are big-endian amount.
-				uint8_t readop[11] = { 0x81, 0x03, 0x08, };
-				
-				readop[3] = (offset>>24)&0xff;
-				readop[4] = (offset>>16)&0xff;
-				readop[5] = (offset>>8)&0xff;
-				readop[6] = (offset>>0)&0xff;
-
-				readop[7] = (amount>>24)&0xff;
-				readop[8] = (amount>>16)&0xff;
-				readop[9] = (amount>>8)&0xff;
-				readop[10] = (amount>>0)&0xff;
-				
-				wch_link_command( devh, readop, 11, 0, 0, 0 );
 
-				// Perform operation
-				wch_link_command( devh, "\x81\x02\x01\x0c", 4, 0, 0, 0 );
-
-				uint32_t remain = amount;
-				while( remain )
+				if( MCF.ReadBinaryBlob )
 				{
-					transferred = 0;
-					WCHCHECK( libusb_bulk_transfer( devh, 0x82, rbuff, 1024, &transferred, WCHTIMEOUT ) );
-					memcpy( ((uint8_t*)readbuff) + readbuffplace, rbuff, transferred );
-					readbuffplace += transferred;
-					remain -= transferred;
-				}
-
-				// Flip internal endian.  Must be done separately in case something was unaligned when
-				// reading.
-				for( i = 0; i < readbuffplace/4; i++ )
+					if( MCF.ReadBinaryBlob( offset, amount, readbuff ) < 0 )
+					{
+						fprintf( stderr, "Fault reading device\n" );
+						return -12;
+					}
+				}				
+				else
 				{
-					uint32_t r = readbuff[i];
-					readbuff[i] = (r>>24) | ((r & 0xff0000) >> 8) | ((r & 0xff00)<<8) | (( r & 0xff )<<24); 
+					goto unimplemented;
 				}
-				
-				fwrite( readbuff, readbuffplace, 1, f );
+
+				fwrite( readbuff, amount, 1, f );
 
 				free( readbuff );
 
@@ -222,44 +209,17 @@ keep_going:
 					exit( -9 );
 				}
 
-				int transferred;
-				wch_link_command( devh, "\x81\x06\x01\x01", 4, 0, 0, 0 );
-				wch_link_command( devh, "\x81\x06\x01\x01", 4, 0, 0, 0 ); // Not sure why but it seems to work better when we request twice.
-
-				// This contains the write data quantity, in bytes.  (The last 2 octets)
-				// Then it just rollllls on in.
-				char rksbuff[11] = { 0x81, 0x01, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-				rksbuff[9] = len >> 8;
-				rksbuff[10] = len & 0xff;
-				wch_link_command( devh, rksbuff, 11, 0, 0, 0 );
-				
-				wch_link_command( devh, "\x81\x02\x01\x05", 4, 0, 0, 0 );
-				
-				int pplace = 0;
-				for( pplace = 0; pplace < bootloader_len; pplace += 64 )
-				{
-					WCHCHECK( libusb_bulk_transfer( devh, 0x02, (uint8_t*)(bootloader+pplace), 64, &transferred, WCHTIMEOUT ) );
-				}
-				
-				for( i = 0; i < 10; i++ )
+				if( MCF.WriteBinaryBlob )
 				{
-					wch_link_command( devh, "\x81\x02\x01\x07", 4, &transferred, rbuff, 1024 );
-					if( transferred == 4 && rbuff[0] == 0x82 && rbuff[1] == 0x02 && rbuff[2] == 0x01 && rbuff[3] == 0x07 )
+					if( MCF.WriteBinaryBlob( 0x08000000, len, image ) )
 					{
-						break;
+						fprintf( stderr, "Error: Fault writing image.\n" );
+						return -13;
 					}
-				} 
-				if( i == 10 )
-				{
-					fprintf( stderr, "Error, confusing respones to 02/01/07\n" );
-					exit( -109 );
 				}
-				
-				wch_link_command( devh, "\x81\x02\x01\x02", 4, 0, 0, 0 );
-
-				for( pplace = 0; pplace < padlen; pplace += 64 )
+				else
 				{
-					WCHCHECK( libusb_bulk_transfer( devh, 0x02,image+pplace, 64, &transferred, WCHTIMEOUT ) );
+					goto unimplemented;
 				}
 
 				// Waiting or something on 2.46.2???????  I don't know why the main system does this.
@@ -272,7 +232,8 @@ keep_going:
 		if( argchar && argchar[2] != 0 ) { argchar++; goto keep_going; }
 	}
 
-	wch_link_command( devh, "\x81\x0d\x01\xff", 4, 0, 0, 0);
+	if( MCF.ExitMode )
+		MCF.ExitMode( dev );
 
 	return 0;
 
@@ -284,7 +245,7 @@ help:
 	fprintf( stderr, " -5 Enable 5V\n" );
 	fprintf( stderr, " -t Disable 3.3V\n" );
 	fprintf( stderr, " -f Disable 5V\n" );
-	fprintf( stderr, " -u Clear all code flash - by power off\n" );
+	fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" );
 	fprintf( stderr, " -r Release from Reset\n" );
 	fprintf( stderr, " -R Place into Reset\n" );
 	fprintf( stderr, " -D Configure NRST as GPIO **WARNING** If you do this and you reconfig\n" );
@@ -295,6 +256,10 @@ help:
 	fprintf( stderr, " -w [binary image to write]\n" );
 	fprintf( stderr, " -o [memory address, decimal or 0x, try 0x08000000] [size, decimal or 0x, try 16384] [output binary image]\n" );
 	return -1;	
+
+unimplemented:
+	fprintf( stderr, "Error: Command '%c' unimplemented on this programmer.\n", argchar[1] );
+	return -1;
 }
 
 
@@ -326,3 +291,13 @@ static int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber )
 		return ret;
 	}
 }
+
+int SetupAutomaticHighLevelFunctions()
+{
+	// Will populate high-level functions from low-level functions.
+	if( MCF.WriteReg32 == 0 || MCF.ReadReg32 == 0 ) return -5;
+
+	// Else, TODO: Build the high level functions from low level functions.
+	// If a high-level function alrady exists, don't override.
+}
+
diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h
new file mode 100644
index 0000000..00c249d
--- /dev/null
+++ b/minichlink/minichlink.h
@@ -0,0 +1,36 @@
+#ifndef _MINICHLINK_H
+#define _MINICHLINK_H
+
+#include <stdint.h>
+
+struct MiniChlinkFunctions
+{
+	// All functions return 0 if OK, negative number if fault, positive number as status code.
+
+	// Low-level functions, if they exist.
+	int (*WriteReg32)( void * dev, uint8_t reg_7_bit, uint32_t command );
+	int (*ReadReg32)( void * dev, uint8_t reg_7_bit, uint32_t * commandresp );
+
+	// Higher-level functions can be generated automatically.
+	int (*SetupInterface)( void * dev );
+	int (*Control3v3)( void * dev, int bOn );
+	int (*Control5v)( void * dev, int bOn );
+	int (*Unbrick)( void * dev ); // Turns on chip, erases everything, powers off.
+
+	int (*HaltMode)( void * dev, int one_if_halt_zero_if_resume );
+	int (*ConfigureNRSTAsGPIO)( void * dev, int one_if_yes_gpio );
+	int (*WriteBinaryBlob)( void * dev, uint32_t address_to_write, uint32_t blob_size, void * blob );
+	int (*ReadBinaryBlob)( void * dev, uint32_t address_to_read_from, uint32_t read_size, void * blob );
+};
+
+extern struct MiniChlinkFunctions MCF;
+
+// Returns 'dev' on success, else 0.
+void * TryInit_WCHLinkE();
+void * TryInit_ESP32S2CHFUN();
+
+// Returns 0 if ok, populated, 1 if not populated.
+int SetupAutomaticHighLevelFunctions();
+
+#endif
+
diff --git a/minichlink/wch-linke.c b/minichlink/wch-linke.c
new file mode 100644
index 0000000..5cf9f28
--- /dev/null
+++ b/minichlink/wch-linke.c
@@ -0,0 +1,330 @@
+// Tricky: You need to use wch link to use WCH-LinkRV.
+//  you can always uninstall it in device manager.  It will be under USB devices or something like that at the bottom.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "libusb.h"
+#include "minichlink.h"
+
+#define WCHTIMEOUT 5000
+#define WCHCHECK(x) if( status = x ) { fprintf( stderr, "Bad USB Operation on " __FILE__ ":%d (%d)\n", __LINE__, status ); exit( status ); }
+
+const uint8_t * bootloader = (const uint8_t*)
+"\x21\x11\x22\xca\x26\xc8\x93\x77\x15\x00\x99\xcf\xb7\x06\x67\x45" \
+"\xb7\x27\x02\x40\x93\x86\x36\x12\x37\x97\xef\xcd\xd4\xc3\x13\x07" \
+"\xb7\x9a\xd8\xc3\xd4\xd3\xd8\xd3\x93\x77\x25\x00\x9d\xc7\xb7\x27" \
+"\x02\x40\x98\x4b\xad\x66\x37\x33\x00\x40\x13\x67\x47\x00\x98\xcb" \
+"\x98\x4b\x93\x86\xa6\xaa\x13\x67\x07\x04\x98\xcb\xd8\x47\x05\x8b" \
+"\x63\x16\x07\x10\x98\x4b\x6d\x9b\x98\xcb\x93\x77\x45\x00\xa9\xcb" \
+"\x93\x07\xf6\x03\x99\x83\x2e\xc0\x2d\x63\x81\x76\x3e\xc4\xb7\x32" \
+"\x00\x40\xb7\x27\x02\x40\x13\x03\xa3\xaa\xfd\x16\x98\x4b\xb7\x03" \
+"\x02\x00\x33\x67\x77\x00\x98\xcb\x02\x47\xd8\xcb\x98\x4b\x13\x67" \
+"\x07\x04\x98\xcb\xd8\x47\x05\x8b\x69\xe7\x98\x4b\x75\x8f\x98\xcb" \
+"\x02\x47\x13\x07\x07\x04\x3a\xc0\x22\x47\x7d\x17\x3a\xc4\x79\xf7" \
+"\x93\x77\x85\x00\xf1\xcf\x93\x07\xf6\x03\x2e\xc0\x99\x83\x37\x27" \
+"\x02\x40\x3e\xc4\x1c\x4b\xc1\x66\x2d\x63\xd5\x8f\x1c\xcb\x37\x07" \
+"\x00\x20\x13\x07\x07\x20\xb7\x27\x02\x40\xb7\x03\x08\x00\xb7\x32" \
+"\x00\x40\x13\x03\xa3\xaa\x94\x4b\xb3\xe6\x76\x00\x94\xcb\xd4\x47" \
+"\x85\x8a\xf5\xfe\x82\x46\xba\x84\x37\x04\x04\x00\x36\xc2\xc1\x46" \
+"\x36\xc6\x92\x46\x84\x40\x11\x07\x84\xc2\x94\x4b\xc1\x8e\x94\xcb" \
+"\xd4\x47\x85\x8a\xb1\xea\x92\x46\xba\x84\x91\x06\x36\xc2\xb2\x46" \
+"\xfd\x16\x36\xc6\xf9\xfe\x82\x46\xd4\xcb\x94\x4b\x93\xe6\x06\x04" \
+"\x94\xcb\xd4\x47\x85\x8a\x85\xee\xd4\x47\xc1\x8a\x85\xce\xd8\x47" \
+"\xb7\x06\xf3\xff\xfd\x16\x13\x67\x07\x01\xd8\xc7\x98\x4b\x21\x45" \
+"\x75\x8f\x98\xcb\x52\x44\xc2\x44\x61\x01\x02\x90\x23\x20\xd3\x00" \
+"\xf5\xb5\x23\xa0\x62\x00\x3d\xb7\x23\xa0\x62\x00\x55\xb7\x23\xa0" \
+"\x62\x00\xc1\xb7\x82\x46\x93\x86\x06\x04\x36\xc0\xa2\x46\xfd\x16" \
+"\x36\xc4\xb5\xf2\x98\x4b\xb7\x06\xf3\xff\xfd\x16\x75\x8f\x98\xcb" \
+"\x41\x89\x05\xcd\x2e\xc0\x0d\x06\x02\xc4\x09\x82\xb7\x07\x00\x20" \
+"\x32\xc6\x93\x87\x07\x20\x98\x43\x13\x86\x47\x00\xa2\x47\x82\x46" \
+"\x8a\x07\xb6\x97\x9c\x43\x63\x1c\xf7\x00\xa2\x47\x85\x07\x3e\xc4" \
+"\xa2\x46\x32\x47\xb2\x87\xe3\xe0\xe6\xfe\x01\x45\x61\xb7\x41\x45" \
+"\x51\xb7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+
+int bootloader_len = 512;
+
+libusb_device_handle * wch_linke_devh;
+
+void wch_link_command( libusb_device_handle * devh, const uint8_t * command, int commandlen, int * transferred, uint8_t * reply, int replymax )
+{
+	uint8_t buffer[1024];
+	int got_to_recv = 0;
+	int status;
+	int transferred_local;
+	if( !transferred ) transferred = &transferred_local;
+	status = libusb_bulk_transfer( devh, 0x01, (char*)command, commandlen, transferred, WCHTIMEOUT );
+	if( status ) goto sendfail;
+
+	got_to_recv = 1;
+	if( !reply )
+	{
+		reply = buffer; replymax = sizeof( buffer );
+	}
+	
+	status = libusb_bulk_transfer( devh, 0x81, (char*)reply, replymax, transferred, WCHTIMEOUT );
+	if( status ) goto sendfail;
+	return;
+sendfail:
+	fprintf( stderr, "Error sending WCH command (%s): ", got_to_recv?"on recv":"on send" );
+	int i;
+	for( i = 0; i < commandlen; i++ )
+	{
+		printf( "%02x ", command[i] );
+	}
+	printf( "\n" );
+	exit( status );
+}
+
+static void wch_link_multicommands( libusb_device_handle * devh, int nrcommands, ... )
+{
+	int i;
+	va_list argp;
+	va_start(argp, nrcommands);
+	for( i = 0; i < nrcommands; i++ )
+	{
+		int clen = va_arg(argp, int);
+		wch_link_command( devh, va_arg(argp, char *), clen, 0, 0, 0 );
+	}
+	va_end( argp );
+}
+
+static inline libusb_device_handle * wch_link_base_setup( int inhibit_startup )
+{
+	libusb_context * ctx = 0;
+	int status;
+	status = libusb_init(&ctx);
+	if (status < 0) {
+		fprintf( stderr, "Error: libusb_init_context() returned %d\n", status );
+		exit( status );
+	}
+	
+	libusb_device **list;
+	libusb_device *found = NULL;
+	ssize_t cnt = libusb_get_device_list(ctx, &list);
+	ssize_t i = 0;
+	int err = 0;
+	for (i = 0; i < cnt; i++) {
+		libusb_device *device = list[i];
+		struct libusb_device_descriptor desc;
+		int r = libusb_get_device_descriptor(device,&desc);
+		if( r == 0 && desc.idVendor == 0x1a86 && desc.idProduct == 0x8010 ) { found = device; }
+	}
+
+	if( !found )
+	{
+		fprintf( stderr, "Error: couldn't find wch link device\n" );
+		return 0;
+	}
+
+	libusb_device_handle * devh;
+	status = libusb_open( found, &devh );
+	if( status )
+	{
+		fprintf( stderr, "Error: couldn't open wch link device (libusb_open() = %d)\n", status );
+		return 0;
+	}
+		
+	WCHCHECK( libusb_claim_interface(devh, 0) );
+	
+	uint8_t rbuff[1024];
+	int transferred;
+	libusb_bulk_transfer( devh, 0x81, rbuff, 1024, &transferred, 1 ); // Clear out any pending transfers.  Don't wait though.
+
+	return devh;
+}
+
+static int LESetupInterface( void * dev )
+{
+	// Place part into reset.
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x01", 4, &transferred, rbuff, 1024 );	// Reply is: "\x82\x0d\x04\x02\x08\x02\x00"
+
+	// TODO: What in the world is this?  It doesn't appear to be needed.
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x0c\x02\x09\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
+
+	// This puts the processor on hold to allow the debugger to run.
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x02", 4, 0, 0, 0 ); // Reply: Ignored, 820d050900300500
+
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x11\x01\x09", 4, &transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
+	if( transferred != 20 )
+	{
+		fprintf( stderr, "Error: could not get part status\n" );
+		return -1;
+	}
+	fprintf( stderr, "Part Type (A): 0x%02x%02x (This is the capacity code, in KB)\n", rbuff[2], rbuff[3] );  // Is this Flash size?
+	fprintf( stderr, "Part UUID    : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", rbuff[4], rbuff[5], rbuff[6], rbuff[7], rbuff[8], rbuff[9], rbuff[10], rbuff[11] );
+	fprintf( stderr, "PFlags       : %02x-%02x-%02x-%02x\n", rbuff[12], rbuff[13], rbuff[14], rbuff[15] );
+	fprintf( stderr, "Part Type (B): %02x-%02x-%02x-%02x\n", rbuff[16], rbuff[17], rbuff[18], rbuff[19] );
+	return 0;
+}
+
+static int LEControl3v3( void * dev, int bOn )
+{
+	if( bOn )
+		wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x09", 4, 0, 0, 0 );
+	else
+		wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x09", 4, 0, 0, 0 );
+	return 0;
+}
+
+static int LEControl5v( void * dev, int bOn )
+{
+	if( bOn )
+		wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x0b", 4, 0, 0, 0 );
+	else
+		wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x0c", 4, 0, 0, 0 );
+	return 0;
+}
+
+static int LEUnbrick( void * dev )
+{
+	 wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x0f\x09", 5, 0, 0, 0 );
+}
+
+static int LEHaltMode( void * dev, int one_if_halt_zero_if_resume )
+{
+	if( one_if_halt_zero_if_resume )
+	{
+		// Part one "immediately" places the part into reset.  Part 2 says when we're done, leave part in reset.
+		wch_link_multicommands( (libusb_device_handle *)dev, 2, 4, "\x81\x0d\x01\x02", 4, "\x81\x0d\x01\x01" );
+	}
+	else
+	{
+		// This is clearly not the "best" method to exit reset.  I don't know why this combination works.
+		wch_link_multicommands( (libusb_device_handle *)dev, 3, 4, "\x81\x0b\x01\x01", 4, "\x81\x0d\x01\x02", 4, "\x81\x0d\x01\xff" );
+	}
+}
+
+int ConfigureNRSTAsGPIO( void * dev, int one_if_yes_gpio )
+{
+	if( one_if_yes_gpio )
+	{
+		wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x02\xff\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+	}
+	else
+	{
+		wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x02\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" );
+	}
+}
+
+int ReadBinaryBlob
+{
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x06\x01\x01", 4, 0, 0, 0 );
+
+	// Flush out any pending data.
+	libusb_bulk_transfer( (libusb_device_handle *)dev, 0x82, rbuff, 1024, &transferred, 1 );
+
+	// 3/8 = Read Memory
+	// First 4 bytes are big-endian location.
+	// Next 4 bytes are big-endian amount.
+	uint8_t readop[11] = { 0x81, 0x03, 0x08, };
+	
+	readop[3] = (offset>>24)&0xff;
+	readop[4] = (offset>>16)&0xff;
+	readop[5] = (offset>>8)&0xff;
+	readop[6] = (offset>>0)&0xff;
+
+	readop[7] = (amount>>24)&0xff;
+	readop[8] = (amount>>16)&0xff;
+	readop[9] = (amount>>8)&0xff;
+	readop[10] = (amount>>0)&0xff;
+	
+	wch_link_command( (libusb_device_handle *)dev, readop, 11, 0, 0, 0 );
+
+	// Perform operation
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x0c", 4, 0, 0, 0 );
+
+	uint32_t remain = amount;
+	while( remain )
+	{
+		transferred = 0;
+		WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x82, rbuff, 1024, &transferred, WCHTIMEOUT ) );
+		memcpy( ((uint8_t*)readbuff) + readbuffplace, rbuff, transferred );
+		readbuffplace += transferred;
+		remain -= transferred;
+	}
+
+	// Flip internal endian.  Must be done separately in case something was unaligned when
+	// reading.
+	for( i = 0; i < readbuffplace/4; i++ )
+	{
+		uint32_t r = readbuff[i];
+		readbuff[i] = (r>>24) | ((r & 0xff0000) >> 8) | ((r & 0xff00)<<8) | (( r & 0xff )<<24); 
+	}
+
+	return 0;
+}
+
+int WriteBinaryBlobLE( uint32_t address_to_write, uint32_t blob_size, void * blob )
+{
+	int transferred;
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x06\x01\x01", 4, 0, 0, 0 );
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x06\x01\x01", 4, 0, 0, 0 ); // Not sure why but it seems to work better when we request twice.
+
+	// This contains the write data quantity, in bytes.  (The last 2 octets)
+	// Then it just rollllls on in.
+	char rksbuff[11] = { 0x81, 0x01, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	rksbuff[9] = len >> 8;
+	rksbuff[10] = len & 0xff;
+	wch_link_command( (libusb_device_handle *)dev, rksbuff, 11, 0, 0, 0 );
+	
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x05", 4, 0, 0, 0 );
+	
+	int pplace = 0;
+	for( pplace = 0; pplace < bootloader_len; pplace += 64 )
+	{
+		WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, (uint8_t*)(bootloader+pplace), 64, &transferred, WCHTIMEOUT ) );
+	}
+	
+	for( i = 0; i < 10; i++ )
+	{
+		wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x07", 4, &transferred, rbuff, 1024 );
+		if( transferred == 4 && rbuff[0] == 0x82 && rbuff[1] == 0x02 && rbuff[2] == 0x01 && rbuff[3] == 0x07 )
+		{
+			break;
+		}
+	} 
+	if( i == 10 )
+	{
+		fprintf( stderr, "Error, confusing respones to 02/01/07\n" );
+		exit( -109 );
+	}
+	
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x02\x01\x02", 4, 0, 0, 0 );
+
+	for( pplace = 0; pplace < padlen; pplace += 64 )
+	{
+		WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02,image+pplace, 64, &transferred, WCHTIMEOUT ) );
+	}
+	return 0;
+}
+
+int ExitModeLE( void * dev )
+{
+	wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\xff", 4, 0, 0, 0);
+}
+
+void * TryInit_WCHLinkE()
+{
+	wch_linke_devh = wch_link_base_setup(0);
+	if( !devh ) return 0;
+
+	MCF.WriteReg32 = 0;
+	MCF.ReadReg32 = 0;
+
+	MCF.SetupInterface = LESetupInterface;
+	MCF.Control3v3 = LEControl3v3;
+	MCF.Control5v = LEControl5v;
+	MCF.Unbrick = LEUnbrick;
+	MCF.HaltMode = LEHaltMode;
+	MCF.ConfigureNRSTAsGPIO = LEConfigureNRSTAsGPIO;
+	MCF.WriteBinaryBlob = WriteBinaryBlobLE;
+	MCF.ReadBinaryBlob = ReadBinaryBlobLE;
+	MCF.ExitMode = ExitModeLE;
+	return devh;
+};
+
+
+
diff --git a/minichlink/wch_link_base.h b/minichlink/wch_link_base.h
deleted file mode 100644
index 517924e..0000000
--- a/minichlink/wch_link_base.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Tricky: You need to use wch link to use WCH-LinkRV.
-//  you can always uninstall it in device manager.  It will be under USB devices or something like that at the bottom.
-#ifndef _WCH_LINK_BASE_H
-#define _WCH_LINK_BASE_H
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include "libusb.h"
-
-#define WCHTIMEOUT 5000
-#define WCHCHECK(x) if( status = x ) { fprintf( stderr, "Bad USB Operation on " __FILE__ ":%d (%d)\n", __LINE__, status ); exit( status ); }
-
-void wch_link_command( libusb_device_handle * devh, const uint8_t * command, int commandlen, int * transferred, uint8_t * reply, int replymax )
-{
-	uint8_t buffer[1024];
-	int got_to_recv = 0;
-	int status;
-	int transferred_local;
-	if( !transferred ) transferred = &transferred_local;
-	status = libusb_bulk_transfer( devh, 0x01, (char*)command, commandlen, transferred, WCHTIMEOUT );
-	if( status ) goto sendfail;
-
-	got_to_recv = 1;
-	if( !reply )
-	{
-		reply = buffer; replymax = sizeof( buffer );
-	}
-	
-	status = libusb_bulk_transfer( devh, 0x81, (char*)reply, replymax, transferred, WCHTIMEOUT );
-	if( status ) goto sendfail;
-	return;
-sendfail:
-	fprintf( stderr, "Error sending WCH command (%s): ", got_to_recv?"on recv":"on send" );
-	int i;
-	for( i = 0; i < commandlen; i++ )
-	{
-		printf( "%02x ", command[i] );
-	}
-	printf( "\n" );
-	exit( status );
-}
-
-static void wch_link_multicommands( libusb_device_handle * devh, int nrcommands, ... )
-{
-	int i;
-	va_list argp;
-	va_start(argp, nrcommands);
-	for( i = 0; i < nrcommands; i++ )
-	{
-		int clen = va_arg(argp, int);
-		wch_link_command( devh, va_arg(argp, char *), clen, 0, 0, 0 );
-	}
-	va_end( argp );
-}
-
-static inline libusb_device_handle * wch_link_base_setup( int inhibit_startup )
-{
-	libusb_context * ctx = 0;
-	int status;
-	status = libusb_init(&ctx);
-	if (status < 0) {
-		fprintf( stderr, "Error: libusb_init_context() returned %d\n", status );
-		exit( status );
-	}
-	
-	libusb_device **list;
-	libusb_device *found = NULL;
-	ssize_t cnt = libusb_get_device_list(ctx, &list);
-	ssize_t i = 0;
-	int err = 0;
-	for (i = 0; i < cnt; i++) {
-		libusb_device *device = list[i];
-		struct libusb_device_descriptor desc;
-		int r = libusb_get_device_descriptor(device,&desc);
-		if( r == 0 && desc.idVendor == 0x1a86 && desc.idProduct == 0x8010 ) { found = device; }
-	}
-
-	if( !found )
-	{
-		fprintf( stderr, "Error: couldn't find wch link device\n" );
-		exit( -9 );
-	}
-
-	libusb_device_handle * devh;
-	status = libusb_open( found, &devh );
-	if( status )
-	{
-		fprintf( stderr, "Error: couldn't open wch link device (libusb_open() = %d)\n", status );
-		exit( -9 );
-	}
-		
-	WCHCHECK( libusb_claim_interface(devh, 0) );
-	
-	uint8_t rbuff[1024];
-	int transferred;
-	libusb_bulk_transfer( devh, 0x81, rbuff, 1024, &transferred, 1 ); // Clear out any pending transfers.  Don't wait though.
-
-
-	if( !inhibit_startup )
-	{
-		// Place part into reset.
-		wch_link_command( devh, "\x81\x0d\x01\x01", 4, &transferred, rbuff, 1024 );	// Reply is: "\x82\x0d\x04\x02\x08\x02\x00"
-
-		// TODO: What in the world is this?  It doesn't appear to be needed.
-		wch_link_command( devh, "\x81\x0c\x02\x09\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
-
-		// This puts the processor on hold to allow the debugger to run.
-		wch_link_command( devh, "\x81\x0d\x01\x02", 4, 0, 0, 0 ); // Reply: Ignored, 820d050900300500
-
-		wch_link_command( devh, "\x81\x11\x01\x09", 4, &transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
-		if( transferred != 20 )
-		{
-			fprintf( stderr, "Error: could not get part status\n" );
-			exit( -99 );
-		}
-		fprintf( stderr, "Part Type (A): 0x%02x%02x (This is the capacity code, in KB)\n", rbuff[2], rbuff[3] );  // Is this Flash size?
-		fprintf( stderr, "Part UUID    : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", rbuff[4], rbuff[5], rbuff[6], rbuff[7], rbuff[8], rbuff[9], rbuff[10], rbuff[11] );
-		fprintf( stderr, "PFlags       : %02x-%02x-%02x-%02x\n", rbuff[12], rbuff[13], rbuff[14], rbuff[15] );
-		fprintf( stderr, "Part Type (B): %02x-%02x-%02x-%02x\n", rbuff[16], rbuff[17], rbuff[18], rbuff[19] );
-	}
-	//for( i = 0; i < transferred; i++ )
-	//	printf( "%02x ", rbuff[i] );
-	//printf( "\n" );
-
-	return devh;
-}
-
-#endif
-- 
GitLab