diff --git a/minichlink/Makefile b/minichlink/Makefile index bc4b278347cbcd122a33551530f02000d486e70f..30c3a077de1a5e816b63634b395c3b855a532385 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 5c5bc9d7df70087c71144f3f2a3ec01458bbb2fa..0e6396737f64ad6c0a7b828e01760032c0a909eb 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 0000000000000000000000000000000000000000..00c249d101d29caddd2a7ce1b042e4808b0ed52d --- /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 0000000000000000000000000000000000000000..5cf9f287ed19777ee92078c3ef2027e031253ada --- /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 517924eab8f134eae7ad4579aaa001feaf8d245b..0000000000000000000000000000000000000000 --- 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