add minichlink
This commit is contained in:
parent
b9a3524daf
commit
0d77388545
23 changed files with 26050 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -27,4 +27,8 @@ V203/usb/scope/software/obj/*
|
|||
V203/usb/scope/software/qrc_src.cpp
|
||||
V203/usb/scope/software/ui_mainwindow.h
|
||||
V203/usb/spitest/*
|
||||
V203F6P6/programmer/software/programmer
|
||||
minichlink/minichlink
|
||||
|
||||
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -70,3 +70,13 @@ zásobníku. Ta gsm knihovna toho sežere asi moc. Nebo, a to je pravděpodobně
|
|||
u tohoto ořezaného jádra neumí se zásobníkem správně pracovat. To se ukazuje i v jiných
|
||||
přikladech (viz pwm). Prostě ta v003 je divná, podivně funguje i optimalizace LTO, vynechává
|
||||
některé potřebné funkce.
|
||||
|
||||
# V203F6P6
|
||||
|
||||
Koupil jsem omylem pár čipů CH32V003 ve 20. pinovém pouzdře, je to dost ořezané, ale
|
||||
použitelné, takže to má i vlastní hardware.
|
||||
|
||||
# minichlink
|
||||
|
||||
Tohle je převzato, ale bylo nutné to trochu poopravit, takže to dám sem.
|
||||
|
||||
|
|
12564
ch32v003fun/ch32v003fun.h
Normal file
12564
ch32v003fun/ch32v003fun.h
Normal file
File diff suppressed because it is too large
Load diff
49
minichlink/3564ce8b030428153a53003d6be79729463fa63b.patch
Normal file
49
minichlink/3564ce8b030428153a53003d6be79729463fa63b.patch
Normal file
|
@ -0,0 +1,49 @@
|
|||
diff -Naurw ./old/microgdbstub.h ./new/microgdbstub.h
|
||||
--- ./old/microgdbstub.h 2025-02-05 14:31:54.679397633 +0100
|
||||
+++ ./new/microgdbstub.h 2025-02-05 12:22:13.979925751 +0100
|
||||
@@ -286,6 +286,7 @@
|
||||
int mslen = strlen( MICROGDBSTUB_MEMORY_MAP ) + 32;
|
||||
char map[mslen];
|
||||
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
||||
+ printf("flash=0x%X, ss=%d, ram=0x%X\n", iss->flash_size, iss->sector_size, iss->ram_size);
|
||||
snprintf( map, mslen, MICROGDBSTUB_MEMORY_MAP, iss->flash_size, iss->sector_size, iss->ram_size );
|
||||
SendReplyFull( map );
|
||||
}
|
||||
diff -Naurw ./old/minichlink.c ./new/minichlink.c
|
||||
--- ./old/minichlink.c 2025-02-05 14:31:54.683397570 +0100
|
||||
+++ ./new/minichlink.c 2025-02-05 13:59:27.185611922 +0100
|
||||
@@ -78,7 +78,7 @@
|
||||
struct InternalState * iss = calloc( 1, sizeof( struct InternalState ) );
|
||||
((struct ProgrammerStructBase*)dev)->internal = iss;
|
||||
iss->ram_base = 0x20000000;
|
||||
- iss->ram_size = 2048;
|
||||
+ iss->ram_size = 4*0x400; // minimal size for CH32V003
|
||||
iss->sector_size = 64;
|
||||
iss->flash_size = 16384;
|
||||
iss->target_chip_type = 0;
|
||||
diff -Naurw ./old/pgm-wch-linke.c ./new/pgm-wch-linke.c
|
||||
--- ./old/pgm-wch-linke.c 2025-02-05 14:31:54.683397570 +0100
|
||||
+++ ./new/pgm-wch-linke.c 2025-02-05 13:40:17.011872846 +0100
|
||||
@@ -413,7 +413,8 @@
|
||||
fprintf( stderr, "Error: could not get part status\n" );
|
||||
return -1;
|
||||
}
|
||||
- fprintf( stderr, "Flash Storage: %d kB\n", (rbuff[2]<<8) | rbuff[3] ); // Is this Flash size?
|
||||
+ const uint32_t flskb = ((uint32_t)rbuff[2]<<8) | rbuff[3];
|
||||
+ fprintf( stderr, "Flash Storage: %d kB\n", flskb ); // 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] );
|
||||
@@ -431,7 +432,11 @@
|
||||
fprintf(stderr, "Read protection: disabled\n");
|
||||
}
|
||||
|
||||
- iss->flash_size = ((rbuff[2]<<8) | rbuff[3])*1024;
|
||||
+ iss->flash_size = flskb * 0x400u;
|
||||
+ if (chip == CHIP_CH32V20x) { // diferent version chip
|
||||
+ if (flskb > 32u) iss->ram_size = 20u * 0x400u; // 64K
|
||||
+ else iss->ram_size = 10u * 0x400u; // 32K
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
13
minichlink/99-minichlink.rules
Normal file
13
minichlink/99-minichlink.rules
Normal file
|
@ -0,0 +1,13 @@
|
|||
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="8010", GROUP="plugdev", MODE="0660"
|
||||
|
||||
# Programmer in ARM mode
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="8012", GROUP="plugdev", MODE="0660"
|
||||
|
||||
# Programmer in IAP mode
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="4348", ATTRS{idProduct}=="55e0", GROUP="plugdev", MODE="0660"
|
||||
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660"
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660"
|
||||
# rv003usb bootloader
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660"
|
||||
#KERNEL=="hiddev*", SUBSYSTEM=="usbmisc", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660"
|
52
minichlink/Makefile
Normal file
52
minichlink/Makefile
Normal file
|
@ -0,0 +1,52 @@
|
|||
TOOLS:=minichlink minichlink.so
|
||||
|
||||
CFLAGS:=-O0 -g3 -Wall -DCH32V003 -I.
|
||||
C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c ardulink.c serial_dev.c pgm-b003fun.c minichgdb.c
|
||||
|
||||
# General Note: To use with GDB, gdb-multiarch
|
||||
# gdb-multilib {file}
|
||||
# target remote :2345
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LDFLAGS:=-L. -lpthread -lusb-1.0 -lsetupapi -lws2_32
|
||||
CFLAGS:=-Os -s -Wall -D_WIN32_WINNT=0x0600 -DCH32V003 -I.
|
||||
TOOLS:=minichlink.exe
|
||||
else
|
||||
OS_NAME := $(shell uname -s | tr A-Z a-z)
|
||||
ifeq ($(OS_NAME),linux)
|
||||
LDFLAGS:=-lpthread -lusb-1.0 -ludev
|
||||
endif
|
||||
ifeq ($(OS_NAME),darwin)
|
||||
LDFLAGS:=-lpthread -lusb-1.0 -framework CoreFoundation -framework IOKit
|
||||
CFLAGS:=-O0 -Wall -Wno-asm-operand-widths -Wno-deprecated-declarations -Wno-deprecated-non-prototype -D__MACOSX__ -DCH32V003 -I.
|
||||
INCLUDES:=$(shell pkg-config --cflags-only-I libusb-1.0)
|
||||
LIBINCLUDES:=$(shell pkg-config --libs-only-L libusb-1.0)
|
||||
INCS:=$(INCLUDES) $(LIBINCLUDES)
|
||||
endif
|
||||
endif
|
||||
|
||||
all : $(TOOLS)
|
||||
|
||||
# will need mingw-w64-x86-64-dev gcc-mingw-w64-x86-64
|
||||
minichlink.exe : $(C_S)
|
||||
x86_64-w64-mingw32-gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
|
||||
|
||||
minichlink : $(C_S)
|
||||
gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) $(INCS)
|
||||
|
||||
minichlink.so : $(C_S)
|
||||
gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) $(INCS) -shared -fPIC
|
||||
|
||||
minichlink.dll : $(C_S)
|
||||
x86_64-w64-mingw32-gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) $(INCS) -shared -DMINICHLINK_AS_LIBRARY
|
||||
|
||||
install_udev_rules :
|
||||
cp 99-WCH-LinkE.rules /etc/udev/rules.d/
|
||||
service udev restart
|
||||
|
||||
inspect_bootloader : minichlink
|
||||
./minichlink -r test.bin launcher 0x780
|
||||
riscv64-unknown-elf-objdump -S -D test.bin -b binary -m riscv:rv32 | less
|
||||
|
||||
clean :
|
||||
rm -rf $(TOOLS)
|
42
minichlink/README.md
Normal file
42
minichlink/README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# minichlink
|
||||
|
||||
A free, open mechanism to use the CH-LinkE $4 programming dongle for the CH32V003.
|
||||
|
||||
If on Linux, be sure to type make sure to install the `99-WCH-LinkE.rules` build rule to `/etc/udev/rules.d/`
|
||||
|
||||
On Windows, if you need to you can install the WinUSB driver over the WCH interface 1.
|
||||
|
||||
The exe here is about 12kB and contains everything except for the libusb driver. In Linux you need `libusb-1.0-dev`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: minichlink [args]
|
||||
single-letter args may be combined, i.e. -3r
|
||||
multi-part args cannot.
|
||||
-3 Enable 3.3V
|
||||
-5 Enable 5V
|
||||
-t Disable 3.3V
|
||||
-f Disable 5V
|
||||
-u Clear all code flash - by power off (also can unbrick)
|
||||
-b Reboot out of Halt
|
||||
-e Resume from halt
|
||||
-a Place into Halt
|
||||
-D Configure NRST as GPIO
|
||||
-d Configure NRST as NRST
|
||||
-s [debug register] [value]
|
||||
-g [debug register]
|
||||
-w [binary image to write] [address, decimal or 0x, try0x08000000]
|
||||
-r [output binary image] [memory address, decimal or 0x, try 0x08000000] [size, decimal or 0x, try 16384]
|
||||
Note: for memory addresses, you can use 'flash' 'launcher' 'bootloader' 'option' 'ram' and say "ram+0x10" for instance
|
||||
For filename, you can use - for raw or + for hex.
|
||||
-T is a terminal. This MUST be the last argument.
|
||||
```
|
||||
# Kizarm NOTES
|
||||
Software je převzato z https://github.com/cnlohr/ch32v003fun/tree/master , projekt je živý, ale chyby
|
||||
v komunikaci s gdb nejsou opraveny. Ty, co mi nejvíce vadily - pro CH32V203 je špatně nastavena paměť,
|
||||
jak RAM, tak flash, takže se na některé proměnné prostě nedostanete - hlavně na zásobník.
|
||||
Pro ukázku, co chybí je zde 3564ce8b030428153a53003d6be79729463fa63b.patch pro revizi danou názvem.
|
||||
Nebudu se do toho autorům montovat, chtělo by to kompletně předělat pro různé verze čipů a v tom
|
||||
se vůbec nevyznám a z čínské dokumentace se toho taky moc nedozvíte.
|
||||
|
174
minichlink/ardulink.c
Normal file
174
minichlink/ardulink.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "serial_dev.h"
|
||||
#include "minichlink.h"
|
||||
|
||||
void * TryInit_Ardulink(const init_hints_t*);
|
||||
|
||||
static int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command);
|
||||
static int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp);
|
||||
static int ArdulinkFlushLLCommands(void * dev);
|
||||
static int ArdulinkDelayUS(void * dev, int microseconds);
|
||||
static int ArdulinkControl3v3(void * dev, int power_on);
|
||||
static int ArdulinkExit(void * dev);
|
||||
|
||||
typedef struct {
|
||||
struct ProgrammerStructBase psb;
|
||||
serial_dev_t serial;
|
||||
} ardulink_ctx_t;
|
||||
|
||||
int ArdulinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command)
|
||||
{
|
||||
uint8_t buf[6];
|
||||
buf[0] = 'w';
|
||||
buf[1] = reg_7_bit;
|
||||
|
||||
//fprintf(stderr, "WriteReg32: 0x%02x = 0x%08x\n", reg_7_bit, command);
|
||||
|
||||
buf[2] = command & 0xff;
|
||||
buf[3] = (command >> 8) & 0xff;
|
||||
buf[4] = (command >> 16) & 0xff;
|
||||
buf[5] = (command >> 24) & 0xff;
|
||||
|
||||
if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 6) == -1)
|
||||
return -errno;
|
||||
|
||||
if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 1) == -1)
|
||||
return -errno;
|
||||
|
||||
return buf[0] == '+' ? 0 : -71; // EPROTO
|
||||
}
|
||||
|
||||
int ArdulinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
buf[0] = 'r';
|
||||
buf[1] = reg_7_bit;
|
||||
|
||||
if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, buf, 2) == -1)
|
||||
return -errno;
|
||||
|
||||
if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, buf, 4) == -1)
|
||||
return -errno;
|
||||
|
||||
*commandresp = (uint32_t)buf[0] | (uint32_t)buf[1] << 8 | \
|
||||
(uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
|
||||
|
||||
//fprintf(stderr, "ReadReg32: 0x%02x = 0x%08x\n", reg_7_bit, *commandresp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ArdulinkFlushLLCommands(void * dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ArdulinkControl3v3(void * dev, int power_on) {
|
||||
char c;
|
||||
|
||||
fprintf(stderr, "Ardulink: target power %d\n", power_on);
|
||||
|
||||
c = power_on ? 'p' : 'P';
|
||||
if (serial_dev_write(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
|
||||
return -errno;
|
||||
|
||||
if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &c, 1) == -1)
|
||||
return -errno;
|
||||
|
||||
if (c != '+')
|
||||
return -71; // EPROTO
|
||||
|
||||
MCF.DelayUS(dev, 20000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ArdulinkDelayUS(void * dev, int microseconds) {
|
||||
//fprintf(stderr, "Ardulink: faking delay %d\n", microseconds);
|
||||
//usleep(microseconds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ArdulinkExit(void * dev)
|
||||
{
|
||||
serial_dev_close(&((ardulink_ctx_t*)dev)->serial);
|
||||
free(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ArdulinkSetupInterface( void * dev )
|
||||
{
|
||||
char first;
|
||||
// Let the bootloader do its thing.
|
||||
MCF.DelayUS(dev, 3UL*1000UL*1000UL);
|
||||
|
||||
if (serial_dev_read(&((ardulink_ctx_t*)dev)->serial, &first, 1) == -1) {
|
||||
perror("read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first != '!') {
|
||||
fprintf(stderr, "Ardulink: not the sync character.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * TryInit_Ardulink(const init_hints_t* hints)
|
||||
{
|
||||
ardulink_ctx_t *ctx;
|
||||
|
||||
if (!(ctx = calloc(sizeof(ardulink_ctx_t), 1))) {
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* serial_to_open = NULL;
|
||||
// Get the serial port that shall be opened.
|
||||
// First, if we have a directly set serial port hint, use that.
|
||||
// Otherwise, use the environment variable MINICHLINK_SERIAL.
|
||||
// If that also doesn't exist, fall back to the default serial.
|
||||
if (hints && hints->serial_port != NULL) {
|
||||
serial_to_open = hints->serial_port;
|
||||
}
|
||||
else if ((serial_to_open = getenv("MINICHLINK_SERIAL")) == NULL) {
|
||||
// fallback
|
||||
serial_to_open = DEFAULT_SERIAL_NAME;
|
||||
}
|
||||
|
||||
if (serial_dev_create(&ctx->serial, serial_to_open, 115200) == -1) {
|
||||
perror("create");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (serial_dev_open(&ctx->serial) == -1) {
|
||||
perror("open");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Arduino DTR reset.
|
||||
if (serial_dev_do_dtr_reset(&ctx->serial) == -1) {
|
||||
perror("dtr reset");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Flush anything that might be in the RX buffer, we need the sync char.
|
||||
if (serial_dev_flush_rx(&ctx->serial) == -1) {
|
||||
perror("flush rx");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Ardulink: synced.\n");
|
||||
|
||||
MCF.WriteReg32 = ArdulinkWriteReg32;
|
||||
MCF.ReadReg32 = ArdulinkReadReg32;
|
||||
MCF.FlushLLCommands = ArdulinkFlushLLCommands;
|
||||
MCF.Control3v3 = ArdulinkControl3v3;
|
||||
MCF.DelayUS = ArdulinkDelayUS;
|
||||
MCF.Exit = ArdulinkExit;
|
||||
MCF.SetupInterface = ArdulinkSetupInterface;
|
||||
|
||||
return ctx;
|
||||
}
|
7
minichlink/funconfig.h
Normal file
7
minichlink/funconfig.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef _FUNCONFIG_H
|
||||
#define _FUNCONFIG_H
|
||||
|
||||
#define MINICHLINK 1
|
||||
|
||||
#endif
|
||||
|
4477
minichlink/hidapi.c
Normal file
4477
minichlink/hidapi.c
Normal file
File diff suppressed because it is too large
Load diff
406
minichlink/hidapi.h
Normal file
406
minichlink/hidapi.h
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
|
||||
8/22/2009
|
||||
|
||||
Copyright 2009, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
http://github.com/signal11/hidapi .
|
||||
********************************************************/
|
||||
|
||||
/* Copy of LICENSE-orig.txt (compatible with MIT/x11 license)
|
||||
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Copyright 2009, Alan Ott, Signal 11 Software.
|
||||
All Rights Reserved.
|
||||
|
||||
This software may be used by anyone for any reason so
|
||||
long as the copyright notice in the source files
|
||||
remains intact.
|
||||
*/
|
||||
|
||||
|
||||
/** @file
|
||||
* @defgroup API hidapi API
|
||||
*/
|
||||
|
||||
#ifndef HIDAPI_H__
|
||||
#define HIDAPI_H__
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define HID_API_EXPORT __declspec(dllexport)
|
||||
#define HID_API_CALL
|
||||
#else
|
||||
#define HID_API_EXPORT /**< API export macro */
|
||||
#define HID_API_CALL /**< API call macro */
|
||||
#endif
|
||||
|
||||
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct hid_device_;
|
||||
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
|
||||
|
||||
/** hidapi info structure */
|
||||
struct hid_device_info {
|
||||
/** Platform-specific device path */
|
||||
char *path;
|
||||
/** Device Vendor ID */
|
||||
unsigned short vendor_id;
|
||||
/** Device Product ID */
|
||||
unsigned short product_id;
|
||||
/** Serial Number */
|
||||
wchar_t *serial_number;
|
||||
/** Device Release Number in binary-coded decimal,
|
||||
also known as Device Version Number */
|
||||
unsigned short release_number;
|
||||
/** Manufacturer String */
|
||||
wchar_t *manufacturer_string;
|
||||
/** Product string */
|
||||
wchar_t *product_string;
|
||||
/** Usage Page for this Device/Interface
|
||||
(Windows/Mac only). */
|
||||
unsigned short usage_page;
|
||||
/** Usage for this Device/Interface
|
||||
(Windows/Mac only).*/
|
||||
unsigned short usage;
|
||||
/** The USB interface which this logical device
|
||||
represents. Valid on both Linux implementations
|
||||
in all cases, and valid on the Windows implementation
|
||||
only if the device contains more than one interface. */
|
||||
int interface_number;
|
||||
|
||||
/** Pointer to the next device */
|
||||
struct hid_device_info *next;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Initialize the HIDAPI library.
|
||||
|
||||
This function initializes the HIDAPI library. Calling it is not
|
||||
strictly necessary, as it will be called automatically by
|
||||
hid_enumerate() and any of the hid_open_*() functions if it is
|
||||
needed. This function should be called at the beginning of
|
||||
execution however, if there is a chance of HIDAPI handles
|
||||
being opened by different threads simultaneously.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_init(void);
|
||||
|
||||
/** @brief Finalize the HIDAPI library.
|
||||
|
||||
This function frees all of the static data associated with
|
||||
HIDAPI. It should be called at the end of execution to avoid
|
||||
memory leaks.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_exit(void);
|
||||
|
||||
/** @brief Enumerate the HID Devices.
|
||||
|
||||
This function returns a linked list of all the HID devices
|
||||
attached to the system which match vendor_id and product_id.
|
||||
If @p vendor_id is set to 0 then any vendor matches.
|
||||
If @p product_id is set to 0 then any product matches.
|
||||
If @p vendor_id and @p product_id are both set to 0, then
|
||||
all HID devices will be returned.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the types of device
|
||||
to open.
|
||||
@param product_id The Product ID (PID) of the types of
|
||||
device to open.
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a linked list of type
|
||||
struct #hid_device, containing information about the HID devices
|
||||
attached to the system, or NULL in the case of failure. Free
|
||||
this linked list by calling hid_free_enumeration().
|
||||
*/
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
|
||||
|
||||
/** @brief Free an enumeration Linked List
|
||||
|
||||
This function frees a linked list created by hid_enumerate().
|
||||
|
||||
@ingroup API
|
||||
@param devs Pointer to a list of struct_device returned from
|
||||
hid_enumerate().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
|
||||
|
||||
/** @brief Open a HID device using a Vendor ID (VID), Product ID
|
||||
(PID) and optionally a serial number.
|
||||
|
||||
If @p serial_number is NULL, the first device with the
|
||||
specified VID and PID is opened.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the device to open.
|
||||
@param product_id The Product ID (PID) of the device to open.
|
||||
@param serial_number The Serial Number of the device to open
|
||||
(Optionally NULL).
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
|
||||
|
||||
/** @brief Open a HID device by its path name.
|
||||
|
||||
The path name be determined by calling hid_enumerate(), or a
|
||||
platform-specific path name can be used (eg: /dev/hidraw0 on
|
||||
Linux).
|
||||
|
||||
@ingroup API
|
||||
@param path The path name of the device to open
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
|
||||
|
||||
/** @brief Write an Output report to a HID device.
|
||||
|
||||
The first byte of @p data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
|
||||
hid_write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Read an Input report from a HID device with timeout.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
@param milliseconds timeout in milliseconds or -1 for blocking wait.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error. If no packet was available to be read within
|
||||
the timeout period, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
/** @brief Read an Input report from a HID device.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error. If no packet was available to be read and
|
||||
the handle is in non-blocking mode, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Set the device handle to be non-blocking.
|
||||
|
||||
In non-blocking mode calls to hid_read() will return
|
||||
immediately with a value of 0 if there is no data to be
|
||||
read. In blocking mode, hid_read() will wait (block) until
|
||||
there is data to read before returning.
|
||||
|
||||
Nonblocking can be turned on and off at any time.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param nonblock enable or not the nonblocking reads
|
||||
- 1 to enable nonblocking
|
||||
- 0 to disable nonblocking.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
|
||||
|
||||
/** @brief Send a Feature report to the device.
|
||||
|
||||
Feature reports are sent over the Control endpoint as a
|
||||
Set_Report transfer. The first byte of @p data[] must
|
||||
contain the Report ID. For devices which only support a
|
||||
single report, this must be set to 0x0. The remaining bytes
|
||||
contain the report data. Since the Report ID is mandatory,
|
||||
calls to hid_send_feature_report() will always contain one
|
||||
more byte than the report contains. For example, if a hid
|
||||
report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_send_feature_report(): the Report ID (or 0x0, for
|
||||
devices which do not use numbered reports), followed by the
|
||||
report data (16 bytes). In this example, the length passed
|
||||
in would be 17.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send, including
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Get a feature report from a HID device.
|
||||
|
||||
Set the first byte of @p data[] to the Report ID of the
|
||||
report to be read. Make sure to allow space for this
|
||||
extra byte in @p data[]. Upon return, the first byte will
|
||||
still contain the Report ID, and the report data will
|
||||
start in data[1].
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into, including
|
||||
the Report ID. Set the first byte of @p data[] to the
|
||||
Report ID of the report to be read, or set it to zero
|
||||
if your device does not use numbered reports.
|
||||
@param length The number of bytes to read, including an
|
||||
extra byte for the report ID. The buffer can be longer
|
||||
than the actual report.
|
||||
|
||||
@returns
|
||||
This function returns the number of bytes read plus
|
||||
one for the report ID (which is still in the first
|
||||
byte), or -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Close a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
|
||||
|
||||
/** @brief Get The Manufacturer String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Product String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Serial Number String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string from a HID device, based on its string index.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param string_index The index of the string to get.
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string describing the last error which occurred.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
|
||||
@returns
|
||||
This function returns a string containing the last error
|
||||
which occurred or NULL if none has occurred.
|
||||
*/
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
2133
minichlink/libusb.h
Normal file
2133
minichlink/libusb.h
Normal file
File diff suppressed because it is too large
Load diff
826
minichlink/microgdbstub.h
Normal file
826
minichlink/microgdbstub.h
Normal file
|
@ -0,0 +1,826 @@
|
|||
/*
|
||||
* Micro GDBStub Driver, for implementing a gdbserver.
|
||||
* Copyright (C) Charles Lohr 2023
|
||||
* You may freely license this file under the MIT-x11, or the 2- or 3- or New BSD Licenses.
|
||||
* You may also use this as though it is public domain.
|
||||
*
|
||||
* This project is based off of picorvd. https://github.com/aappleby/PicoRVD/
|
||||
*
|
||||
* Simply:
|
||||
* 1: define the RV_ Functions
|
||||
* 2: Call the MicroGDB* functions needed.
|
||||
* 3: Define MICROGDBSTUB_IMPLEMENTATION at least in one place this is included in your program.
|
||||
* 4: If you want to let this manage the server as a network device, simply #define MICROGDBSTUB_SOCKETS
|
||||
*
|
||||
* To connect to your GDBStub running, you can:
|
||||
* gdb-multiarch -ex 'target remote :2000' ./blink.elf
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MICROGDBSTUB_H
|
||||
#define _MICROGDBSTUB_H
|
||||
|
||||
// You must write these for your processor.
|
||||
void RVNetPoll(void * dev );
|
||||
int RVSendGDBHaltReason( void * dev );
|
||||
void RVNetConnect( void * dev );
|
||||
int RVReadCPURegister( void * dev, int regno, uint32_t * regret );
|
||||
int RVWriteCPURegister( void * dev, int regno, uint32_t value );
|
||||
void RVDebugExec( void * dev, int halt_reset_or_resume );
|
||||
int RVReadMem( void * dev, uint32_t memaddy, uint8_t * payload, int len );
|
||||
int RVHandleBreakpoint( void * dev, int set, uint32_t address );
|
||||
int RVWriteRAM(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload );
|
||||
void RVCommandResetPart( void * dev, int mode );
|
||||
void RVHandleDisconnect( void * dev );
|
||||
void RVHandleGDBBreakRequest( void * dev );
|
||||
void RVHandleKillRequest( void * dev );
|
||||
int RVErase( void * dev, uint32_t memaddy, uint32_t length );
|
||||
int RVWriteFlash( void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload );
|
||||
|
||||
#ifdef MICROGDBSTUB_SOCKETS
|
||||
int MicroGDBPollServer( void * dev );
|
||||
int MicroGDBStubStartup( void * dev );
|
||||
void MicroGDBExitServer( void * dev );
|
||||
#endif
|
||||
|
||||
// If you are not a network socket, you can pass in this data.
|
||||
void MicroGDBStubSendReply( const void * data, int len, int docs );
|
||||
void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len );
|
||||
|
||||
|
||||
#ifdef MICROGDBSTUB_IMPLEMENTATION
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Protocol Stuff
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef MICROGDBSTUB_SOCKETS
|
||||
#if defined( WIN32 ) || defined( _WIN32 )
|
||||
#include <winsock2.h>
|
||||
#if !defined( POLLIN )
|
||||
typedef struct pollfd { SOCKET fd; SHORT events; SHORT revents; };
|
||||
#define POLLIN 0x0001
|
||||
#define POLLERR 0x008
|
||||
#define POLLHUP 0x010
|
||||
int WSAAPI WSAPoll(struct pollfd * fdArray, ULONG fds, INT timeout );
|
||||
#endif
|
||||
#define poll WSAPoll
|
||||
#define socklen_t uint32_t
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#define MSG_NOSIGNAL 0
|
||||
#else
|
||||
#define closesocket close
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/in.h>
|
||||
#endif
|
||||
|
||||
char gdbbuffer[65536];
|
||||
uint8_t gdbchecksum = 0;
|
||||
int gdbbufferplace = 0;
|
||||
int gdbbufferstate = 0;
|
||||
int gdbrunningcsum = 0;
|
||||
|
||||
static inline char ToHEXNibble( int i )
|
||||
{
|
||||
i &= 0xf;
|
||||
return ( i < 10 )?('0' + i):('a' - 10 + i);
|
||||
}
|
||||
|
||||
static int fromhex( char c )
|
||||
{
|
||||
if( c >= '0' && c <= '9' ) c = c - '0';
|
||||
else if( c >= 'A' && c <= 'F' ) c = c - 'A' + 10;
|
||||
else if( c >= 'a' && c <= 'f' ) c = c - 'a' + 10;
|
||||
else return -1;
|
||||
return c;
|
||||
}
|
||||
|
||||
// output must have length of len / 2.
|
||||
static int DecodeHexToBytes(const char* hexstr, size_t string_len, void* output, size_t out_len) {
|
||||
// 2 hex chars make up one byte. out buffer needs to have >= slen/2 bytes.
|
||||
// further, we only want to decode even-length strings
|
||||
if (out_len < (string_len / 2) || (string_len % 2) != 0)
|
||||
return -1;
|
||||
uint8_t* out = (uint8_t*) output;
|
||||
for(size_t i = 0; i < string_len; i += 2) {
|
||||
int nibble1, nibble2;
|
||||
if((nibble1 = fromhex(hexstr[i])) < 0)
|
||||
return nibble1; // error
|
||||
if((nibble2 = fromhex(hexstr[i + 1])) < 0)
|
||||
return nibble2; // error
|
||||
out[i / 2] = ((uint8_t)nibble1 << 4u) | ((uint8_t)nibble2);
|
||||
}
|
||||
return string_len / 2; // number of output bytes written
|
||||
}
|
||||
|
||||
// if (numhex < 0)
|
||||
static int ReadHex( char ** instr, int numhex, uint32_t * outwrite )
|
||||
{
|
||||
if( !instr ) return -1;
|
||||
char * str = *instr;
|
||||
// If negative - error.
|
||||
// If positive - number of bytes read.
|
||||
|
||||
*outwrite = 0;
|
||||
|
||||
int scanhex = numhex;
|
||||
if( scanhex < 0 )
|
||||
scanhex = strlen( str );
|
||||
|
||||
int i;
|
||||
for( i = 0; i < scanhex; i++ )
|
||||
{
|
||||
int v = fromhex( *(str++) );
|
||||
if( v < 0 )
|
||||
{
|
||||
if( numhex < 0 )
|
||||
{
|
||||
str--;
|
||||
*instr = str;
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
*instr = str;
|
||||
return - i - 1;
|
||||
}
|
||||
}
|
||||
(*outwrite) = ((*outwrite) << 4) | v;
|
||||
}
|
||||
*instr = str;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int StringMatch( const char * haystack, const char * mat )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; mat[i] && haystack[i]; i++ )
|
||||
if( mat[i] != haystack[i] || haystack[i] == 0 ) break;
|
||||
return mat[i] == 0;
|
||||
}
|
||||
|
||||
void SendReplyFull( const char * replyMessage )
|
||||
{
|
||||
MicroGDBStubSendReply( replyMessage, -1, '$' );
|
||||
}
|
||||
|
||||
void MakeGDBPrintText(const char* msg) {
|
||||
// ASCII to hex conversion doubles size, plus 'O', plus NUL
|
||||
size_t buf_len = 2 * strlen(msg) + 2;
|
||||
char* buf = alloca(buf_len);
|
||||
memset(buf, 0, buf_len);
|
||||
buf[0] = 'O'; // for "Output"
|
||||
char* target = buf + 1;
|
||||
for(size_t i = 0; i < strlen(msg); i++) {
|
||||
target[2*i] = ToHEXNibble((msg[i] & 0xf0u) >> 4u);
|
||||
target[2*i + 1] = ToHEXNibble(msg[i] & 0x0fu);
|
||||
}
|
||||
SendReplyFull(buf);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// General Protocol
|
||||
|
||||
void HandleGDBPacket( void * dev, char * data, int len )
|
||||
{
|
||||
int i;
|
||||
char * odata = data;
|
||||
|
||||
// Got a packet?
|
||||
if( data[0] != '$' ) return;
|
||||
|
||||
data++;
|
||||
|
||||
char cmd = *(data++);
|
||||
switch( cmd )
|
||||
{
|
||||
case 'q':
|
||||
if( StringMatch( data, "Attached" ) )
|
||||
SendReplyFull( "1" ); //Attached to an existing process.
|
||||
else if( StringMatch( data, "Supported" ) )
|
||||
SendReplyFull( "PacketSize=f000;qXfer:memory-map:read+" );
|
||||
else if( StringMatch( data, "C") ) // Get Current Thread ID. (Can't be -1 or 0. Those are special)
|
||||
SendReplyFull( "QC1" );
|
||||
else if( StringMatch( data, "fThreadInfo" ) ) // Query all active thread IDs (Can't be 0 or 1)
|
||||
SendReplyFull( "m1" );
|
||||
else if( StringMatch( data, "sThreadInfo" ) ) // Query all active thread IDs, continued
|
||||
SendReplyFull( "l" );
|
||||
// Unimplemented commands.
|
||||
else if( StringMatch( data, "Offsets" ) ) // Trace-Status
|
||||
SendReplyFull( "" );
|
||||
else if( StringMatch( data, "Symbol" ) ) // Trace-Status
|
||||
SendReplyFull( "" );
|
||||
else if( StringMatch( data, "TStatus" ) ) // Trace-Status
|
||||
SendReplyFull( "" );
|
||||
else if( StringMatch( data, "Rcmd," ) ) // "monitor <command>"
|
||||
{
|
||||
// will e.g. be "Rcmd,7265736574#"
|
||||
// hex-decode the rest of it back to ASCII
|
||||
char* cmdHex = data + strlen("Rcmd,");
|
||||
// check if cmd is empty (no character or only '#' following)
|
||||
if(strlen(cmdHex) > 1) {
|
||||
char cmd[128];
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
if ( DecodeHexToBytes(cmdHex, strlen(cmdHex) - 1, cmd, sizeof(cmd) - 1) < 0 ) {
|
||||
// decoding failed
|
||||
SendReplyFull( "" );
|
||||
break;
|
||||
}
|
||||
printf("Got monitor command: %s\n", cmd);
|
||||
// Support commands that OpenOCD also does:
|
||||
// https://openocd.org/doc/html/General-Commands.html
|
||||
if(StringMatch(cmd, "halt")) {
|
||||
// only halt
|
||||
RVCommandResetPart( dev, HALT_MODE_HALT_BUT_NO_RESET);
|
||||
SendReplyFull( "+" );
|
||||
}
|
||||
else if(StringMatch(cmd, "reset halt")) {
|
||||
// reset and keep halted after reset
|
||||
RVCommandResetPart( dev, HALT_MODE_HALT_AND_RESET);
|
||||
SendReplyFull( "+" );
|
||||
}
|
||||
else if(StringMatch(cmd, "reset run")) {
|
||||
// reset and run (i.e., reboot)
|
||||
RVCommandResetPart( dev, HALT_MODE_REBOOT);
|
||||
SendReplyFull( "+" );
|
||||
}
|
||||
else if(StringMatch(cmd, "reset")) {
|
||||
// same as reset run per OpenOCD
|
||||
RVCommandResetPart( dev, HALT_MODE_REBOOT);
|
||||
SendReplyFull( "+" );
|
||||
} else if(StringMatch(cmd, "resume")) {
|
||||
// just resume
|
||||
RVCommandResetPart( dev, HALT_MODE_RESUME);
|
||||
SendReplyFull( "+" );
|
||||
} else if(StringMatch(cmd, "help")) {
|
||||
static const char helptext[] =
|
||||
"minichlink GDB monitor help:\n"
|
||||
"- halt: Halt execution\n"
|
||||
"- resume: Resume execution\n"
|
||||
"- reset [halt, run]: Reset and optionally halt or run\n";
|
||||
MakeGDBPrintText(helptext);
|
||||
SendReplyFull( "+" );
|
||||
}
|
||||
else {
|
||||
printf("Unknown monitor command '%s', use 'monitor help'.\n", cmd);
|
||||
MakeGDBPrintText("Unknown monitor command, use 'monitor help'\n");
|
||||
SendReplyFull( "-" );
|
||||
}
|
||||
} else {
|
||||
MakeGDBPrintText("No monitor command given, use 'monitor help'\n");
|
||||
SendReplyFull( "-" );
|
||||
}
|
||||
}
|
||||
else if( StringMatch( data, "Xfer:memory-map" ) )
|
||||
{
|
||||
int mslen = strlen( MICROGDBSTUB_MEMORY_MAP ) + 32;
|
||||
char map[mslen];
|
||||
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
||||
printf("flash=0x%X, ss=%d, ram=0x%X\n", iss->flash_size, iss->sector_size, iss->ram_size);
|
||||
snprintf( map, mslen, MICROGDBSTUB_MEMORY_MAP, iss->flash_size, iss->sector_size, iss->ram_size );
|
||||
SendReplyFull( map );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Unknown command: %s\n", data );
|
||||
SendReplyFull( "" );
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
case 'C':
|
||||
RVDebugExec( dev, (cmd == 's' )?9:(cmd == 'C')?4:2 );
|
||||
SendReplyFull( "OK" );
|
||||
break;
|
||||
case 's':
|
||||
RVDebugExec( dev, 4 );
|
||||
SendReplyFull( "OK" );
|
||||
//RVHandleGDBBreakRequest( dev );
|
||||
RVSendGDBHaltReason( dev );
|
||||
break;
|
||||
case 'D':
|
||||
// Handle disconnect.
|
||||
RVHandleDisconnect( dev );
|
||||
break;
|
||||
case 'k':
|
||||
RVHandleKillRequest( dev ); // no reply.
|
||||
break;
|
||||
case 'P': // Set register
|
||||
{
|
||||
uint32_t reg, value;
|
||||
if( ReadHex( &data, -1, ® ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &value ) < 0 ) goto err;
|
||||
printf( "REG: %02x = %08x\n", reg, value );
|
||||
RVWriteCPURegister( dev, reg, value );
|
||||
break;
|
||||
}
|
||||
case 'Z': // set
|
||||
case 'z': // unset
|
||||
{
|
||||
uint32_t type = 0;
|
||||
uint32_t addr = 0;
|
||||
uint32_t time = 0;
|
||||
if( ReadHex( &data, -1, &type ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &addr ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &time ) < 0 ) goto err;
|
||||
if( RVHandleBreakpoint( dev, cmd == 'Z', addr ) == 0 )
|
||||
{
|
||||
SendReplyFull( "OK" );
|
||||
}
|
||||
else
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
{
|
||||
// Read memory (Binary)
|
||||
uint32_t address_to_read = 0;
|
||||
uint32_t length_to_read = 0;
|
||||
if( ReadHex( &data, -1, &address_to_read ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &length_to_read ) < 0 ) goto err;
|
||||
|
||||
uint8_t * pl = alloca( length_to_read * 2 );
|
||||
if( RVReadMem( dev, address_to_read, pl, length_to_read ) < 0 )
|
||||
goto err;
|
||||
char * repl = alloca( length_to_read * 2 );
|
||||
int i;
|
||||
for( i = 0; i < length_to_read; i++ )
|
||||
{
|
||||
sprintf( repl + i * 2, "%02x", pl[i] );
|
||||
}
|
||||
SendReplyFull( repl );
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
{
|
||||
uint32_t address_to_write = 0;
|
||||
uint32_t length_to_write = 0;
|
||||
if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err;
|
||||
if( *(data++) != ':' ) goto err;
|
||||
|
||||
uint8_t * meml = alloca( length_to_write );
|
||||
int i;
|
||||
for( i = 0; i < length_to_write; i++ )
|
||||
{
|
||||
uint32_t rv;
|
||||
if( ReadHex( &data, 2, &rv ) < 0 ) goto err;
|
||||
meml[i] = rv;
|
||||
}
|
||||
if( RVWriteRAM( dev, address_to_write, length_to_write, meml ) < 0 ) goto err;
|
||||
SendReplyFull( "OK" );
|
||||
break;
|
||||
}
|
||||
case 'X':
|
||||
{
|
||||
// Write memory, binary.
|
||||
uint32_t address_to_write = 0;
|
||||
uint32_t length_to_write = 0;
|
||||
if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err;
|
||||
if( *(data++) != ':' ) goto err;
|
||||
if( RVWriteRAM( dev, address_to_write, length_to_write, (uint8_t*)data ) < 0 ) goto err;
|
||||
SendReplyFull( "OK" );
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
if( StringMatch( data, "Cont" ) ) // vCont?
|
||||
{
|
||||
// Request a list of actions supported by the ‘vCont’ packet.
|
||||
// We don't support vCont
|
||||
SendReplyFull( "vCont;s;c;;" ); //no ;t because we don't implement them.
|
||||
}
|
||||
else if( StringMatch( data, "MustReplyEmpty" ) ) //vMustReplyEmpty
|
||||
{
|
||||
SendReplyFull( "" );
|
||||
}
|
||||
else if( StringMatch( data, "FlashDone" ) ) //vFlashDone
|
||||
{
|
||||
SendReplyFull( "OK" );
|
||||
}
|
||||
else if( StringMatch( data, "FlashErase" ) ) //vFlashErase
|
||||
{
|
||||
data += 10; // FlashErase
|
||||
if( *(data++) != ':' ) goto err;
|
||||
uint32_t address_to_erase = 0;
|
||||
uint32_t length_to_erase = 0;
|
||||
if( ReadHex( &data, -1, &address_to_erase ) < 0 ) goto err;
|
||||
if( *(data++) != ',' ) goto err;
|
||||
if( ReadHex( &data, -1, &length_to_erase ) < 0 ) goto err;
|
||||
|
||||
if( RVErase( dev, address_to_erase, length_to_erase ) == 0 )
|
||||
SendReplyFull( "OK" );
|
||||
else
|
||||
SendReplyFull( "E 93" );
|
||||
}
|
||||
else if( StringMatch( data, "FlashWrite" ) ) //vFlashWrite
|
||||
{
|
||||
data += 10; // FlashWrite
|
||||
|
||||
printf( "Write\n" );
|
||||
if( *(data++) != ':' ) goto err;
|
||||
uint32_t address_to_write = 0;
|
||||
if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err;
|
||||
if( *(data++) != ':' ) goto err;
|
||||
int toflash = len - (data - odata) - 1;
|
||||
printf( "LEN: %08x %d %d %c\n", address_to_write, len, toflash, data[0] );
|
||||
if( RVWriteFlash( dev, address_to_write, len, (uint8_t*)data ) == 0 )
|
||||
{
|
||||
printf( "Write OK\n" );
|
||||
SendReplyFull( "OK" );
|
||||
}
|
||||
else
|
||||
SendReplyFull( "E 93" );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Warning: Unknown v command %s\n", data );
|
||||
SendReplyFull( "E 01" );
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
{
|
||||
// Register Read (All regs)
|
||||
char cts[17*8+1];
|
||||
for( i = 0; i < 17; i++ )
|
||||
{
|
||||
uint32_t regret;
|
||||
if( RVReadCPURegister( dev, i, ®ret ) ) goto err;
|
||||
sprintf( cts + i * 8, "%08x", (uint32_t)htonl( regret ) );
|
||||
}
|
||||
SendReplyFull( cts );
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
uint32_t regno;
|
||||
// Register Read (Specific Reg)
|
||||
if( ReadHex( &data, 2, ®no ) < 0 )
|
||||
SendReplyFull( "E 10" );
|
||||
else
|
||||
{
|
||||
char cts[9];
|
||||
uint32_t regret;
|
||||
if( RVReadCPURegister( dev, regno, ®ret ) ) goto err;
|
||||
sprintf( cts, "%08x", (uint32_t)htonl( regret ) );
|
||||
SendReplyFull( cts );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '?': // Query reason for target halt.
|
||||
RVSendGDBHaltReason( dev );
|
||||
break;
|
||||
case 'H':
|
||||
// This is for things like selecting threads.
|
||||
// I am going to leave this stubbed out.
|
||||
SendReplyFull( "" );
|
||||
break;
|
||||
default:
|
||||
printf( "UNKNOWN PACKET: %d (%s)\n", len, data-1 );
|
||||
for( i = 0; i < len; i++ )
|
||||
{
|
||||
printf( "%02x ", data[i] );
|
||||
}
|
||||
printf( "\n" );
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
err:
|
||||
SendReplyFull( "E 00" );
|
||||
}
|
||||
|
||||
void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len )
|
||||
{
|
||||
int pl = 0;
|
||||
for( pl = 0; pl < len; pl++ )
|
||||
{
|
||||
int c = rxdata[pl];
|
||||
if( c == '$' && gdbbufferstate == 0 )
|
||||
{
|
||||
gdbrunningcsum = 0;
|
||||
gdbbufferplace = 0;
|
||||
gdbbufferstate = 1;
|
||||
}
|
||||
if( c == 3 && gdbbufferstate == 0 )
|
||||
{
|
||||
RVHandleGDBBreakRequest( dev );
|
||||
continue;
|
||||
}
|
||||
switch( gdbbufferstate )
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case 1:
|
||||
if( c != '#' ) gdbrunningcsum += (uint8_t)c;
|
||||
if( gdbbufferplace < sizeof( gdbbuffer ) - 2 )
|
||||
{
|
||||
if( c == '}' ) { gdbbufferstate = 9; break; }
|
||||
gdbbuffer[gdbbufferplace++] = c;
|
||||
}
|
||||
if( c == '#' ) gdbbufferstate = 2;
|
||||
break;
|
||||
case 9: // escape
|
||||
gdbrunningcsum += (uint8_t)c;
|
||||
if( gdbbufferplace < sizeof( gdbbuffer ) - 2 )
|
||||
{
|
||||
char escaped = c ^ 0x20;
|
||||
gdbbuffer[gdbbufferplace++] = escaped;
|
||||
printf( "ESCAPED @ %02x -> %c [%d]\n", gdbbufferplace, escaped, escaped );
|
||||
gdbbufferstate = 1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
c = fromhex( c );
|
||||
if( gdbbufferstate == 2 )
|
||||
{
|
||||
gdbchecksum = c << 4;
|
||||
gdbbufferstate++;
|
||||
break;
|
||||
}
|
||||
if( gdbbufferstate == 3 ) gdbchecksum |= c;
|
||||
|
||||
gdbbuffer[gdbbufferplace] = 0;
|
||||
|
||||
gdbrunningcsum = (gdbrunningcsum - '$')&0xff;
|
||||
|
||||
if( gdbrunningcsum == gdbchecksum )
|
||||
{
|
||||
MicroGDBStubSendReply( "+", -1, 0 );
|
||||
HandleGDBPacket( dev, (char*)gdbbuffer, gdbbufferplace );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Checksum Error: Got %02x expected %02x / len: %d\n", gdbrunningcsum, gdbchecksum, gdbbufferplace );
|
||||
int i;
|
||||
for( i = 0; i < gdbbufferplace; i++ )
|
||||
{
|
||||
int c = ((uint8_t*)gdbbuffer)[i];
|
||||
printf( "%02x [%c] ", c, (c>=32 && c < 128)?c:' ');
|
||||
if( ( i & 0xf ) == 0xf ) printf( "\n" );
|
||||
}
|
||||
printf( "\n" );
|
||||
MicroGDBStubSendReply( "-", -1, 0 );
|
||||
}
|
||||
|
||||
gdbbufferplace = 0;
|
||||
gdbbufferstate = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef MICROGDBSTUB_SOCKETS
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int listenMode; // 0 for uninit. 1 for server, 2 for client.
|
||||
int serverSocket;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Network layer.
|
||||
|
||||
|
||||
void MicroGDBStubHandleDisconnect( void * dev )
|
||||
{
|
||||
RVHandleDisconnect( dev );
|
||||
}
|
||||
|
||||
static int GDBListen( void * dev )
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
//Make sure the socket worked.
|
||||
if( serverSocket == -1 )
|
||||
{
|
||||
fprintf( stderr, "Error: Cannot create socket.\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Disable SO_LINGER (Well, enable it but turn it way down)
|
||||
#if defined( WIN32 ) || defined( _WIN32 )
|
||||
struct linger lx;
|
||||
lx.l_onoff = 1;
|
||||
lx.l_linger = 0;
|
||||
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) );
|
||||
|
||||
//Enable SO_REUSEADDR
|
||||
int reusevar = 1;
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar));
|
||||
#else
|
||||
struct linger lx;
|
||||
lx.l_onoff = 1;
|
||||
lx.l_linger = 0;
|
||||
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) );
|
||||
|
||||
//Enable SO_REUSEADDR
|
||||
int reusevar = 1;
|
||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar));
|
||||
#endif
|
||||
//Setup socket for listening address.
|
||||
memset( &sin, 0, sizeof( sin ) );
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
sin.sin_port = htons( MICROGDBSTUB_PORT );
|
||||
|
||||
//Actually bind to the socket
|
||||
if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 )
|
||||
{
|
||||
fprintf( stderr, "Could not bind to socket: %d\n", MICROGDBSTUB_PORT );
|
||||
closesocket( serverSocket );
|
||||
serverSocket = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Finally listen.
|
||||
if( listen( serverSocket, 5 ) == -1 )
|
||||
{
|
||||
fprintf(stderr, "Could not lieten to socket.");
|
||||
closesocket( serverSocket );
|
||||
serverSocket = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf( stderr, "gdbserver running on port %d\n", MICROGDBSTUB_PORT );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MicroGDBPollServer( void * dev )
|
||||
{
|
||||
if( !serverSocket ) return -4;
|
||||
|
||||
int pollct = 1;
|
||||
struct pollfd allpolls[1] = { 0 };
|
||||
allpolls[0].fd = serverSocket;
|
||||
#if defined( WIN32 ) || defined( _WIN32 )
|
||||
allpolls[0].events = 0x00000100; //POLLRDNORM;
|
||||
#else
|
||||
allpolls[0].events = POLLIN;
|
||||
#endif
|
||||
int r = poll( allpolls, pollct, 0 );
|
||||
|
||||
if( r < 0 )
|
||||
{
|
||||
printf( "R: %d\n", r );
|
||||
}
|
||||
|
||||
//If there's faults, bail.
|
||||
if( allpolls[0].revents & (POLLERR|POLLHUP) )
|
||||
{
|
||||
closesocket( serverSocket );
|
||||
if( listenMode == 1 )
|
||||
{
|
||||
// Some sort of weird fatal close? Is this even possible?
|
||||
fprintf( stderr, "Error: serverSocke was forcibly closed\n" );
|
||||
exit( -4 );
|
||||
}
|
||||
else if( listenMode == 2 )
|
||||
{
|
||||
MicroGDBStubHandleDisconnect( dev );
|
||||
if( serverSocket ) close( serverSocket );
|
||||
serverSocket = 0;
|
||||
listenMode = 1;
|
||||
GDBListen( dev );
|
||||
}
|
||||
}
|
||||
if( allpolls[0].revents & POLLIN )
|
||||
{
|
||||
if( listenMode == 1 )
|
||||
{
|
||||
struct sockaddr_in tin;
|
||||
socklen_t addrlen = sizeof(tin);
|
||||
memset( &tin, 0, addrlen );
|
||||
int tsocket = accept( serverSocket, (struct sockaddr *)&tin, (void*)&addrlen );
|
||||
closesocket( serverSocket );
|
||||
serverSocket = tsocket;
|
||||
listenMode = 2;
|
||||
gdbbufferstate = 0;
|
||||
RVNetConnect( dev );
|
||||
fprintf( stderr, "Connection established to gdbserver backend\n" );
|
||||
// Established.
|
||||
}
|
||||
else if( listenMode == 2 )
|
||||
{
|
||||
// Got data from a peer.
|
||||
uint8_t buffer[16384];
|
||||
ssize_t rx = recv( serverSocket, (char*)buffer, sizeof( buffer ), MSG_NOSIGNAL );
|
||||
if( rx == 0 )
|
||||
{
|
||||
MicroGDBStubHandleDisconnect( dev );
|
||||
close( serverSocket );
|
||||
serverSocket = 0;
|
||||
listenMode = 1;
|
||||
GDBListen( dev );
|
||||
}
|
||||
else
|
||||
MicroGDBStubHandleClientData( dev, buffer, (int)rx );
|
||||
}
|
||||
}
|
||||
|
||||
if( listenMode == 2 )
|
||||
{
|
||||
RVNetPoll( dev );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MicroGDBExitServer( void * dev )
|
||||
{
|
||||
shutdown( serverSocket, SHUT_RDWR );
|
||||
if( listenMode == 2 )
|
||||
{
|
||||
MicroGDBStubHandleDisconnect( dev );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MicroGDBStubSendReply( const void * data, int len, int docs )
|
||||
{
|
||||
if( len < 0 ) len = strlen( data );
|
||||
if( docs )
|
||||
{
|
||||
uint8_t * localbuffer = alloca( len ) + 5;
|
||||
localbuffer[0] = '$';
|
||||
uint8_t checksum = 0;
|
||||
int i;
|
||||
for( i = 0; i < len; i++ )
|
||||
{
|
||||
uint8_t v = ((const uint8_t*)data)[i];
|
||||
checksum += v;
|
||||
localbuffer[i+1] = v;
|
||||
}
|
||||
localbuffer[len+1] = '#';
|
||||
localbuffer[len+2] = ToHEXNibble( checksum >> 4 );
|
||||
localbuffer[len+3] = ToHEXNibble( checksum );
|
||||
localbuffer[len+4] = 0;
|
||||
data = (void*)localbuffer;
|
||||
len += 4;
|
||||
}
|
||||
|
||||
if( listenMode == 2 )
|
||||
{
|
||||
//printf( ">>>>%s<<<<(%d)\n", data );
|
||||
send( serverSocket, data, len, MSG_NOSIGNAL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MicroGDBStubStartup( void * dev )
|
||||
{
|
||||
#if defined( WIN32 ) || defined( _WIN32 )
|
||||
{
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
wVersionRequested = MAKEWORD(2, 2);
|
||||
|
||||
err = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (err != 0) {
|
||||
fprintf( stderr, "WSAStartup failed with error: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
listenMode = 1;
|
||||
|
||||
return GDBListen( dev );
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
467
minichlink/minichgdb.c
Normal file
467
minichlink/minichgdb.c
Normal file
|
@ -0,0 +1,467 @@
|
|||
// This file is loosely based on aappleby's GDBServer.
|
||||
|
||||
// Connect in with:
|
||||
// gdb-multiarch -ex 'target remote :2000' ./blink.elf
|
||||
|
||||
#include "minichlink.h"
|
||||
|
||||
#define MICROGDBSTUB_IMPLEMENTATION
|
||||
#define MICROGDBSTUB_SOCKETS
|
||||
#define MICROGDBSTUB_PORT 2000
|
||||
|
||||
const char* MICROGDBSTUB_MEMORY_MAP = "l<?xml version=\"1.0\"?>"
|
||||
"<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"
|
||||
"<memory-map>"
|
||||
" <memory type=\"flash\" start=\"0x00000000\" length=\"0x%x\">"
|
||||
" <property name=\"blocksize\">%d</property>"
|
||||
" </memory>"
|
||||
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x%x\">"
|
||||
" <property name=\"blocksize\">1</property>"
|
||||
" </memory>"
|
||||
" <memory type=\"ram\" start=\"0x40000000\" length=\"0x10000000\">"
|
||||
" <property name=\"blocksize\">4</property>"
|
||||
" </memory>"
|
||||
"</memory-map>";
|
||||
|
||||
#include "microgdbstub.h"
|
||||
|
||||
void SendReplyFull( const char * replyMessage );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Actual Chip Operations
|
||||
|
||||
// Several pieces from picorvd. https://github.com/aappleby/PicoRVD/
|
||||
int shadow_running_state = 1;
|
||||
int last_halt_reason = 5;
|
||||
uint32_t backup_regs[17];
|
||||
|
||||
#define MAX_SOFTWARE_BREAKPOINTS 128
|
||||
int num_software_breakpoints = 0;
|
||||
uint8_t software_breakpoint_type[MAX_SOFTWARE_BREAKPOINTS]; // 0 = not in use, 1 = 32-bit, 2 = 16-bit.
|
||||
uint32_t software_breakpoint_addy[MAX_SOFTWARE_BREAKPOINTS];
|
||||
uint32_t previous_word_at_breakpoint_address[MAX_SOFTWARE_BREAKPOINTS];
|
||||
|
||||
int IsGDBServerInShadowHaltState( void * dev ) { return !shadow_running_state; }
|
||||
|
||||
static int InternalClearFlashOfSoftwareBreakpoint( void * dev, int i );
|
||||
static int InternalWriteBreakpointIntoAddress( void * v, int i );
|
||||
|
||||
|
||||
void RVCommandPrologue( void * dev )
|
||||
{
|
||||
if( !MCF.ReadCPURegister )
|
||||
{
|
||||
fprintf( stderr, "Error: Programmer does not support register reading\n" );
|
||||
exit( -5 );
|
||||
}
|
||||
|
||||
MCF.WriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable autoexec.
|
||||
if( MCF.ReadAllCPURegisters( dev, backup_regs ) )
|
||||
{
|
||||
fprintf( stderr, "WARNING: failed to preserve registers\n" );
|
||||
}
|
||||
MCF.VoidHighLevelState( dev );
|
||||
}
|
||||
|
||||
void RVCommandEpilogue( void * dev )
|
||||
{
|
||||
MCF.WriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable autoexec.
|
||||
MCF.WriteAllCPURegisters( dev, backup_regs );
|
||||
MCF.VoidHighLevelState( dev );
|
||||
MCF.WriteReg32( dev, DMDATA0, 0 );
|
||||
}
|
||||
|
||||
void RVCommandResetPart( void * dev , int mode)
|
||||
{
|
||||
MCF.HaltMode( dev, mode );
|
||||
RVCommandPrologue( dev );
|
||||
}
|
||||
|
||||
void RVNetConnect( void * dev )
|
||||
{
|
||||
// ??? Should we actually halt?
|
||||
MCF.HaltMode( dev, 5 );
|
||||
MCF.SetEnableBreakpoints( dev, 1, 0 );
|
||||
RVCommandPrologue( dev );
|
||||
shadow_running_state = 0;
|
||||
}
|
||||
|
||||
int RVSendGDBHaltReason( void * dev )
|
||||
{
|
||||
char st[5];
|
||||
sprintf( st, "T%02x", last_halt_reason );
|
||||
SendReplyFull( st );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RVNetPoll(void * dev )
|
||||
{
|
||||
if( !MCF.ReadReg32 )
|
||||
{
|
||||
fprintf( stderr, "Error: Can't poll GDB because no ReadReg32 supported on this programmer\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t status;
|
||||
if( MCF.ReadReg32( dev, DMSTATUS, &status ) )
|
||||
{
|
||||
fprintf( stderr, "Error: Could not get part status\n" );
|
||||
return;
|
||||
}
|
||||
int statusrunning = ((status & (1<<10)));
|
||||
|
||||
static int laststatus;
|
||||
if( status != laststatus )
|
||||
{
|
||||
//printf( "DMSTATUS: %08x => %08x\n", laststatus, status );
|
||||
laststatus = status;
|
||||
}
|
||||
if( statusrunning != shadow_running_state )
|
||||
{
|
||||
// If was running but now is halted.
|
||||
if( statusrunning == 0 )
|
||||
{
|
||||
RVCommandPrologue( dev );
|
||||
last_halt_reason = 5;//((dscr>>6)&3)+5;
|
||||
RVSendGDBHaltReason( dev );
|
||||
}
|
||||
shadow_running_state = statusrunning;
|
||||
}
|
||||
}
|
||||
|
||||
int RVReadCPURegister( void * dev, int regno, uint32_t * regret )
|
||||
{
|
||||
if( shadow_running_state )
|
||||
{
|
||||
MCF.HaltMode( dev, 5 );
|
||||
RVCommandPrologue( dev );
|
||||
shadow_running_state = 0;
|
||||
}
|
||||
|
||||
if( regno == 32 ) regno = 16; // Hack - Make 32 also 16 for old GDBs.
|
||||
if( regno > 16 ) return 0; // Invalid register.
|
||||
|
||||
*regret = backup_regs[regno];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int RVWriteCPURegister( void * dev, int regno, uint32_t value )
|
||||
{
|
||||
if( shadow_running_state )
|
||||
{
|
||||
MCF.HaltMode( dev, 5 );
|
||||
RVCommandPrologue( dev );
|
||||
shadow_running_state = 0;
|
||||
}
|
||||
|
||||
if( regno == 32 ) regno = 16; // Hack - Make 32 also 16 for old GDBs.
|
||||
if( regno > 16 ) return 0; // Invalid register.
|
||||
|
||||
backup_regs[regno] = value;
|
||||
|
||||
if( !MCF.WriteAllCPURegisters )
|
||||
{
|
||||
fprintf( stderr, "ERROR: MCF.WriteAllCPURegisters is not implemented on this platform\n" );
|
||||
return -99;
|
||||
}
|
||||
|
||||
int r;
|
||||
if( ( r = MCF.WriteAllCPURegisters( dev, backup_regs ) ) )
|
||||
{
|
||||
fprintf( stderr, "Error: WriteAllCPURegisters failed (%d)\n", r );
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RVDebugExec( void * dev, int halt_reset_or_resume )
|
||||
{
|
||||
if( !MCF.HaltMode )
|
||||
{
|
||||
fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" );
|
||||
exit( -6 );
|
||||
}
|
||||
|
||||
// Special case halt_reset_or_resume = 4: Skip instruction and resume.
|
||||
if( halt_reset_or_resume == 4 || halt_reset_or_resume == 2 )
|
||||
{
|
||||
// First see if we already know about this breakpoint
|
||||
int matchingbreakpoint = -1;
|
||||
// For this we want to advance PC.
|
||||
uint32_t exceptionptr = backup_regs[16];
|
||||
uint32_t instruction = 0;
|
||||
|
||||
int i;
|
||||
for( i = 0; i < MAX_SOFTWARE_BREAKPOINTS; i++ )
|
||||
{
|
||||
if( exceptionptr == software_breakpoint_addy[i] && software_breakpoint_type[i] )
|
||||
{
|
||||
matchingbreakpoint = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( matchingbreakpoint >= 0 )
|
||||
{
|
||||
// This is a known breakpoint. Need to set it back. Single Step. Then continue.
|
||||
InternalClearFlashOfSoftwareBreakpoint( dev, matchingbreakpoint );
|
||||
MCF.SetEnableBreakpoints( dev, 1, 1 );
|
||||
InternalWriteBreakpointIntoAddress( dev, matchingbreakpoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown breakpoint (was originally in the firmware)
|
||||
// Just proceed past it.
|
||||
if( exceptionptr & 2 )
|
||||
{
|
||||
uint32_t part1, part2;
|
||||
MCF.ReadWord( dev, exceptionptr & ~3, &part1 );
|
||||
MCF.ReadWord( dev, (exceptionptr & ~3)+4, &part2 );
|
||||
instruction = (part1 >> 16) | (part2 << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
MCF.ReadWord( dev, exceptionptr, &instruction );
|
||||
}
|
||||
if( instruction == 0x00100073 )
|
||||
backup_regs[16]+=4;
|
||||
else if( ( instruction & 0xffff ) == 0x9002 )
|
||||
backup_regs[16]+=2;
|
||||
else
|
||||
; //No change, it is a normal instruction.
|
||||
|
||||
if( halt_reset_or_resume == 4 )
|
||||
{
|
||||
MCF.SetEnableBreakpoints( dev, 1, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
halt_reset_or_resume = HALT_MODE_RESUME;
|
||||
}
|
||||
|
||||
if( shadow_running_state != ( halt_reset_or_resume >= 2 ) )
|
||||
{
|
||||
if( halt_reset_or_resume < 2 )
|
||||
{
|
||||
RVCommandPrologue( dev );
|
||||
}
|
||||
else
|
||||
{
|
||||
RVCommandEpilogue( dev );
|
||||
}
|
||||
MCF.HaltMode( dev, halt_reset_or_resume );
|
||||
}
|
||||
|
||||
shadow_running_state = halt_reset_or_resume >= 2;
|
||||
}
|
||||
|
||||
int RVReadMem( void * dev, uint32_t memaddy, uint8_t * payload, int len )
|
||||
{
|
||||
if( !MCF.ReadBinaryBlob )
|
||||
{
|
||||
fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" );
|
||||
exit( -6 );
|
||||
}
|
||||
int ret = MCF.ReadBinaryBlob( dev, memaddy, len, payload );
|
||||
if( ret < 0 )
|
||||
{
|
||||
fprintf( stderr, "Error reading binary blob at %08x\n", memaddy );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int InternalClearFlashOfSoftwareBreakpoint( void * dev, int i )
|
||||
{
|
||||
int r;
|
||||
if( software_breakpoint_type[i] == 1 )
|
||||
{
|
||||
//32-bit instruction
|
||||
r = MCF.WriteBinaryBlob( dev, software_breakpoint_addy[i], 4, (uint8_t*)&previous_word_at_breakpoint_address[i] );
|
||||
}
|
||||
else
|
||||
{
|
||||
//16-bit instruction
|
||||
r = MCF.WriteBinaryBlob( dev, software_breakpoint_addy[i], 2, (uint8_t*)&previous_word_at_breakpoint_address[i] );
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int InternalWriteBreakpointIntoAddress( void * dev, int i )
|
||||
{
|
||||
int r;
|
||||
uint32_t address = software_breakpoint_addy[i];
|
||||
if( software_breakpoint_type[i] == 1 )
|
||||
{
|
||||
//32-bit instruction
|
||||
uint32_t ebreak = 0x00100073; // ebreak
|
||||
r = MCF.WriteBinaryBlob( dev, address, 4, (uint8_t*)&ebreak );
|
||||
}
|
||||
else
|
||||
{
|
||||
//16-bit instruction
|
||||
uint32_t ebreak = 0x9002; // c.ebreak
|
||||
r = MCF.WriteBinaryBlob( dev, address, 2, (uint8_t*)&ebreak );
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int InternalDisableBreakpoint( void * dev, int i )
|
||||
{
|
||||
int r;
|
||||
r = InternalClearFlashOfSoftwareBreakpoint( dev, i );
|
||||
previous_word_at_breakpoint_address[i] = 0;
|
||||
software_breakpoint_type[i] = 0;
|
||||
software_breakpoint_addy[i] = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
int RVHandleBreakpoint( void * dev, int set, uint32_t address )
|
||||
{
|
||||
int i;
|
||||
int first_free = -1;
|
||||
for( i = 0; i < MAX_SOFTWARE_BREAKPOINTS; i++ )
|
||||
{
|
||||
if( software_breakpoint_type[i] && software_breakpoint_addy[i] == address )
|
||||
break;
|
||||
if( first_free < 0 && software_breakpoint_type[i] == 0 )
|
||||
first_free = i;
|
||||
}
|
||||
|
||||
if( i != MAX_SOFTWARE_BREAKPOINTS )
|
||||
{
|
||||
// There is already a break slot here.
|
||||
if( !set )
|
||||
{
|
||||
InternalDisableBreakpoint( dev, i );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already set.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( first_free == -1 )
|
||||
{
|
||||
fprintf( stderr, "Error: Too many breakpoints\n" );
|
||||
return -1;
|
||||
}
|
||||
if( set )
|
||||
{
|
||||
i = first_free;
|
||||
uint32_t readval_at_addy;
|
||||
int r = MCF.ReadBinaryBlob( dev, address, 4, (uint8_t*)&readval_at_addy );
|
||||
if( r ) return -5;
|
||||
if( ( readval_at_addy & 3 ) == 3 ) // Check opcode LSB's.
|
||||
{
|
||||
// 32-bit instruction.
|
||||
software_breakpoint_type[i] = 1;
|
||||
software_breakpoint_addy[i] = address;
|
||||
previous_word_at_breakpoint_address[i] = readval_at_addy;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16-bit instructions
|
||||
software_breakpoint_type[i] = 2;
|
||||
software_breakpoint_addy[i] = address;
|
||||
previous_word_at_breakpoint_address[i] = readval_at_addy & 0xffff;
|
||||
}
|
||||
InternalWriteBreakpointIntoAddress( dev, i );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already unset.
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RVWriteRAM(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload )
|
||||
{
|
||||
if( !MCF.WriteBinaryBlob )
|
||||
{
|
||||
fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" );
|
||||
exit( -6 );
|
||||
}
|
||||
|
||||
int r = MCF.WriteBinaryBlob( dev, memaddy, length, payload );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int RVWriteFlash(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload )
|
||||
{
|
||||
if( (memaddy & 0xff000000 ) == 0 )
|
||||
{
|
||||
memaddy |= 0x08000000;
|
||||
}
|
||||
return RVWriteRAM( dev, memaddy, length, payload );
|
||||
}
|
||||
|
||||
int RVErase( void * dev, uint32_t memaddy, uint32_t length )
|
||||
{
|
||||
if( !MCF.Erase )
|
||||
{
|
||||
fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" );
|
||||
exit( -6 );
|
||||
}
|
||||
|
||||
int r = MCF.Erase( dev, memaddy, length, 0 ); // 0 = not whole chip.
|
||||
return r;
|
||||
}
|
||||
|
||||
void RVHandleDisconnect( void * dev )
|
||||
{
|
||||
MCF.HaltMode( dev, 5 );
|
||||
MCF.SetEnableBreakpoints( dev, 0, 0 );
|
||||
|
||||
int i;
|
||||
for( i = 0; i < MAX_SOFTWARE_BREAKPOINTS; i++ )
|
||||
{
|
||||
if( software_breakpoint_type[i] )
|
||||
{
|
||||
InternalDisableBreakpoint( dev, i );
|
||||
}
|
||||
}
|
||||
|
||||
if( shadow_running_state == 0 )
|
||||
{
|
||||
RVCommandEpilogue( dev );
|
||||
}
|
||||
MCF.HaltMode( dev, 2 );
|
||||
shadow_running_state = 1;
|
||||
}
|
||||
|
||||
void RVHandleGDBBreakRequest( void * dev )
|
||||
{
|
||||
if( shadow_running_state )
|
||||
{
|
||||
MCF.HaltMode( dev, 5 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int PollGDBServer( void * dev )
|
||||
{
|
||||
return MicroGDBPollServer( dev );
|
||||
}
|
||||
|
||||
void ExitGDBServer( void * dev )
|
||||
{
|
||||
MicroGDBExitServer( dev );
|
||||
}
|
||||
|
||||
|
||||
int SetupGDBServer( void * dev )
|
||||
{
|
||||
return MicroGDBStubStartup( dev );
|
||||
}
|
||||
|
||||
void RVHandleKillRequest( void * dev )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
2056
minichlink/minichlink.c
Normal file
2056
minichlink/minichlink.c
Normal file
File diff suppressed because it is too large
Load diff
205
minichlink/minichlink.h
Normal file
205
minichlink/minichlink.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
#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 );
|
||||
int (*FlushLLCommands)( void * dev );
|
||||
int (*DelayUS)( void * dev, int microseconds );
|
||||
|
||||
// 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 (*Exit)( void * dev );
|
||||
|
||||
int (*HaltMode)( void * dev, int mode ); //0 for halt, 1 for reset, 2 for resume
|
||||
int (*ConfigureNRSTAsGPIO)( void * dev, int one_if_yes_gpio );
|
||||
int (*ConfigureReadProtection)( void * dev, int one_if_yes_protect );
|
||||
|
||||
// No boundary or limit rules. Must support any combination of alignment and size.
|
||||
int (*WriteBinaryBlob)( void * dev, uint32_t address_to_write, uint32_t blob_size, uint8_t * blob );
|
||||
int (*ReadBinaryBlob)( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob );
|
||||
|
||||
int (*Erase)( void * dev, uint32_t address, uint32_t length, int type ); //type = 0 for fast, 1 for whole-chip
|
||||
|
||||
// MUST be 4-byte-aligned.
|
||||
int (*VoidHighLevelState)( void * dev );
|
||||
int (*WriteWord)( void * dev, uint32_t address_to_write, uint32_t data );
|
||||
int (*ReadWord)( void * dev, uint32_t address_to_read, uint32_t * data );
|
||||
|
||||
// Debugging operations.
|
||||
// Note: You must already be in break mode to use these otherwise they
|
||||
// will return nonsensical data.
|
||||
// For x0...xN, use 0x1000 + regno.
|
||||
// For PC, use 0x7b1
|
||||
int (*ReadCPURegister)( void * dev, uint32_t regno, uint32_t * regret );
|
||||
int (*WriteCPURegister)( void * dev, uint32_t regno, uint32_t regval );
|
||||
|
||||
// Actually returns 17 registers (All 16 CPU registers + the debug register)
|
||||
int (*ReadAllCPURegisters)( void * dev, uint32_t * regret );
|
||||
int (*WriteAllCPURegisters)( void * dev, uint32_t * regret );
|
||||
|
||||
int (*SetEnableBreakpoints)( void * dev, int halt_on_break, int single_step );
|
||||
|
||||
int (*PrepForLongOp)( void * dev ); // Called before the command that will take a while.
|
||||
int (*WaitForFlash)( void * dev );
|
||||
int (*WaitForDoneOp)( void * dev, int ignore );
|
||||
|
||||
int (*PrintChipInfo)( void * dev );
|
||||
|
||||
// Geared for flash, but could be anything. Note: If in flash, must also erase.
|
||||
int (*BlockWrite64)( void * dev, uint32_t address_to_write, uint8_t * data );
|
||||
|
||||
// Returns positive if received text.
|
||||
// Returns negative if error.
|
||||
// Returns 0 if no text waiting.
|
||||
// Note: YOU CANNOT make lsb of leaveflagA bit in place 0x80 be high!!!
|
||||
int (*PollTerminal)( void * dev, uint8_t * buffer, int maxlen, uint32_t leaveflagA, int leaveflagB );
|
||||
|
||||
int (*PerformSongAndDance)( void * dev );
|
||||
|
||||
int (*VendorCommand)( void * dev, const char * command );
|
||||
|
||||
// Probably no need to override these. The base layer handles them.
|
||||
int (*WriteHalfWord)( void * dev, uint32_t address_to_write, uint16_t data );
|
||||
int (*ReadHalfWord)( void * dev, uint32_t address_to_read, uint16_t * data );
|
||||
|
||||
int (*WriteByte)( void * dev, uint32_t address_to_write, uint8_t data );
|
||||
int (*ReadByte)( void * dev, uint32_t address_to_read, uint8_t * data );
|
||||
};
|
||||
|
||||
/** If you are writing a driver, the minimal number of functions you can implement are:
|
||||
WriteReg32
|
||||
ReadReg32
|
||||
FlushLLCommands
|
||||
*/
|
||||
|
||||
inline static int IsAddressFlash( uint32_t addy ) { return ( addy & 0xff000000 ) == 0x08000000 || ( addy & 0x1FFFF000 ) == 0x1FFFF000; }
|
||||
|
||||
#define HALT_MODE_HALT_AND_RESET 0
|
||||
#define HALT_MODE_REBOOT 1
|
||||
#define HALT_MODE_RESUME 2
|
||||
#define HALT_MODE_GO_TO_BOOTLOADER 3
|
||||
#define HALT_MODE_HALT_BUT_NO_RESET 5
|
||||
|
||||
// Convert a 4-character string to an int.
|
||||
#define STTAG( x ) (*((uint32_t*)(x)))
|
||||
|
||||
struct InternalState;
|
||||
|
||||
struct ProgrammerStructBase
|
||||
{
|
||||
struct InternalState * internal;
|
||||
// You can put other things here.
|
||||
};
|
||||
|
||||
#define MAX_FLASH_SECTORS 262144
|
||||
|
||||
enum RiscVChip {
|
||||
CHIP_CH32V10x = 0x01,
|
||||
CHIP_CH57x = 0x02,
|
||||
CHIP_CH56x = 0x03,
|
||||
CHIP_CH32V20x = 0x05,
|
||||
CHIP_CH32V30x = 0x06,
|
||||
CHIP_CH58x = 0x07,
|
||||
CHIP_CH32V003 = 0x09
|
||||
};
|
||||
|
||||
struct InternalState
|
||||
{
|
||||
uint32_t statetag;
|
||||
uint32_t currentstateval;
|
||||
uint32_t flash_unlocked;
|
||||
int lastwriteflags;
|
||||
int processor_in_mode;
|
||||
int autoincrement;
|
||||
uint32_t ram_base;
|
||||
uint32_t ram_size;
|
||||
int sector_size;
|
||||
int flash_size;
|
||||
enum RiscVChip target_chip_type;
|
||||
uint8_t flash_sector_status[MAX_FLASH_SECTORS]; // 0 means unerased/unknown. 1 means erased.
|
||||
};
|
||||
|
||||
|
||||
#define DMDATA0 0x04
|
||||
#define DMDATA1 0x05
|
||||
#define DMCONTROL 0x10
|
||||
#define DMSTATUS 0x11
|
||||
#define DMHARTINFO 0x12
|
||||
#define DMABSTRACTCS 0x16
|
||||
#define DMCOMMAND 0x17
|
||||
#define DMABSTRACTAUTO 0x18
|
||||
#define DMPROGBUF0 0x20
|
||||
#define DMPROGBUF1 0x21
|
||||
#define DMPROGBUF2 0x22
|
||||
#define DMPROGBUF3 0x23
|
||||
#define DMPROGBUF4 0x24
|
||||
#define DMPROGBUF5 0x25
|
||||
#define DMPROGBUF6 0x26
|
||||
#define DMPROGBUF7 0x27
|
||||
|
||||
#define DMCPBR 0x7C
|
||||
#define DMCFGR 0x7D
|
||||
#define DMSHDWCFGR 0x7E
|
||||
|
||||
#if defined( WIN32 ) || defined( _WIN32 )
|
||||
#if defined( MINICHLINK_AS_LIBRARY )
|
||||
#define DLLDECORATE __declspec(dllexport)
|
||||
#elif defined( MINICHLINK_IMPORT )
|
||||
#define DLLDECORATE __declspec(dllimport)
|
||||
#else
|
||||
#define DLLDECORATE
|
||||
#endif
|
||||
#else
|
||||
#define DLLDECORATE
|
||||
#endif
|
||||
|
||||
/* initialization hints for init functions */
|
||||
/* could be expanded with more in the future (e.g., PID/VID hints, priorities, ...)*/
|
||||
/* not all init functions currently need these hints. */
|
||||
typedef struct {
|
||||
const char * serial_port;
|
||||
const char * specific_programmer;
|
||||
} init_hints_t;
|
||||
|
||||
void * MiniCHLinkInitAsDLL(struct MiniChlinkFunctions ** MCFO, const init_hints_t* init_hints) DLLDECORATE;
|
||||
extern struct MiniChlinkFunctions MCF;
|
||||
|
||||
// Returns 'dev' on success, else 0.
|
||||
void * TryInit_WCHLinkE(void);
|
||||
void * TryInit_ESP32S2CHFUN(void);
|
||||
void * TryInit_NHCLink042(void);
|
||||
void * TryInit_B003Fun(void);
|
||||
void * TryInit_Ardulink(const init_hints_t*);
|
||||
|
||||
// Returns 0 if ok, populated, 1 if not populated.
|
||||
int SetupAutomaticHighLevelFunctions( void * dev );
|
||||
|
||||
// Useful for converting numbers like 0x, etc.
|
||||
int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber );
|
||||
|
||||
// For drivers to call
|
||||
int DefaultVoidHighLevelState( void * dev );
|
||||
int InternalUnlockBootloader( void * dev );
|
||||
int InternalIsMemoryErased( struct InternalState * iss, uint32_t address );
|
||||
void InternalMarkMemoryNotErased( struct InternalState * iss, uint32_t address );
|
||||
int InternalUnlockFlash( void * dev, struct InternalState * iss );
|
||||
|
||||
// GDBSever Functions
|
||||
int SetupGDBServer( void * dev );
|
||||
int PollGDBServer( void * dev );
|
||||
int IsGDBServerInShadowHaltState( void * dev );
|
||||
void ExitGDBServer( void * dev );
|
||||
|
||||
#endif
|
||||
|
163
minichlink/nhc-link042.c
Normal file
163
minichlink/nhc-link042.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "minichlink.h"
|
||||
#include "libusb.h"
|
||||
|
||||
void * TryInit_NHCLink042(void);
|
||||
|
||||
static int NHCLinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command);
|
||||
static int NHCLinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp);
|
||||
static int NHCLinkFlushLLCommands(void * dev);
|
||||
static int NHCLinkDelayUS(void * dev, int microseconds);
|
||||
static int NHCLinkExit(void * dev);
|
||||
|
||||
static libusb_device_handle *hdev = 0;
|
||||
|
||||
int NHCLinkWriteReg32(void * dev, uint8_t reg_7_bit, uint32_t command)
|
||||
{
|
||||
uint8_t buff[64];
|
||||
int32_t len;
|
||||
int status;
|
||||
|
||||
buff[0] = 0xa3;
|
||||
buff[1] = reg_7_bit;
|
||||
buff[2] = (command >> 0);
|
||||
buff[3] = (command >> 8);
|
||||
buff[4] = (command >> 16);
|
||||
buff[5] = (command >> 24);
|
||||
|
||||
status = libusb_bulk_transfer(dev, 0x01, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NHCLinkReadReg32(void * dev, uint8_t reg_7_bit, uint32_t * commandresp)
|
||||
{
|
||||
uint8_t buff[64];
|
||||
int32_t len;
|
||||
uint32_t tmp;
|
||||
int status;
|
||||
|
||||
buff[0] = 0xa2;
|
||||
buff[1] = reg_7_bit;
|
||||
|
||||
status = libusb_bulk_transfer(dev, 0x01, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
status = libusb_bulk_transfer(dev, 0x81, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!buff[0])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp = buff[4];
|
||||
tmp <<= 8;
|
||||
tmp += buff[3];
|
||||
tmp <<= 8;
|
||||
tmp += buff[2];
|
||||
tmp <<= 8;
|
||||
tmp += buff[1];
|
||||
|
||||
*commandresp = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NHCLinkFlushLLCommands(void * dev)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NHCLinkDelayUS(void * dev, int microseconds)
|
||||
{
|
||||
uint8_t buff[64];
|
||||
int32_t len;
|
||||
uint32_t tmp;
|
||||
int status;
|
||||
|
||||
tmp = microseconds;
|
||||
buff[0] = 0xa6;
|
||||
buff[1] = (tmp >> 0);
|
||||
buff[2] = (tmp >> 8);
|
||||
buff[3] = (tmp >> 16);
|
||||
buff[4] = (tmp >> 24);
|
||||
|
||||
status = libusb_bulk_transfer(dev, 0x01, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NHCLinkExit(void * dev)
|
||||
{
|
||||
uint8_t buff[64];
|
||||
int32_t len;
|
||||
int status;
|
||||
|
||||
buff[0] = 0xa1;
|
||||
|
||||
status = libusb_bulk_transfer(dev, 0x01, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * TryInit_NHCLink042(void)
|
||||
{
|
||||
libusb_context * ctx = 0;
|
||||
int status;
|
||||
uint8_t buff[64];
|
||||
int32_t len;
|
||||
|
||||
status = libusb_init(&ctx);
|
||||
if (status < 0) {
|
||||
fprintf( stderr, "Error: libusb_init_context() returned %d\n", status );
|
||||
exit( status );
|
||||
}
|
||||
|
||||
hdev = libusb_open_device_with_vid_pid(ctx, 0x1986, 0x0034);
|
||||
|
||||
if( !hdev )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
libusb_claim_interface(hdev, 0);
|
||||
|
||||
buff[0] = 0xa0;
|
||||
|
||||
status = libusb_bulk_transfer(hdev, 0x01, buff, 64, &len, 5000);
|
||||
if ((status) || (len != 64))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MCF.WriteReg32 = NHCLinkWriteReg32;
|
||||
MCF.ReadReg32 = NHCLinkReadReg32;
|
||||
MCF.DelayUS = NHCLinkDelayUS;
|
||||
MCF.FlushLLCommands = NHCLinkFlushLLCommands;
|
||||
MCF.Exit = NHCLinkExit;
|
||||
|
||||
return hdev;
|
||||
}
|
777
minichlink/pgm-b003fun.c
Normal file
777
minichlink/pgm-b003fun.c
Normal file
|
@ -0,0 +1,777 @@
|
|||
#include <stdint.h>
|
||||
#include "hidapi.h"
|
||||
#include "minichlink.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../ch32v003fun/ch32v003fun.h"
|
||||
|
||||
//#define DEBUG_B003
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
||||
void Sleep(uint32_t dwMilliseconds);
|
||||
#define usleep( x ) Sleep( x / 1000 );
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct B003FunProgrammerStruct
|
||||
{
|
||||
void * internal; // Part of struct ProgrammerStructBase
|
||||
|
||||
hid_device * hd;
|
||||
uint8_t commandbuffer[128];
|
||||
uint8_t respbuffer[128];
|
||||
int commandplace;
|
||||
int prepping_for_erase;
|
||||
};
|
||||
|
||||
static const unsigned char byte_wise_read_blob[] = { // No alignment restrictions.
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x94, 0x21, 0x14, 0xa3, 0x85, 0x05, 0x05, 0x07,
|
||||
0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char half_wise_read_blob[] = { // size and address must be aligned by 2.
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x96, 0x21, 0x16, 0xa3, 0x89, 0x05, 0x09, 0x07,
|
||||
0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char word_wise_read_blob[] = { // size and address must be aligned by 4.
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x94, 0x41, 0x14, 0xc3, 0x91, 0x05, 0x11, 0x07,
|
||||
0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char word_wise_write_blob[] = { // size and address must be aligned by 4.
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x14, 0x43, 0x94, 0xc1, 0x91, 0x05, 0x11, 0x07,
|
||||
0xe3, 0xcc, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff, 0x14, 0xc1, 0x82, 0x80, // NOTE: No readback!
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
/*
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x14, 0x43, 0x94, 0xc1, 0x94, 0x41, 0x14, 0xc3, // With readback.
|
||||
0x91, 0x05, 0x11, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
|
||||
0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 */
|
||||
};
|
||||
|
||||
static const unsigned char write64_flash[] = { // size and address must be aligned by 4.
|
||||
0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x13, 0x86, 0x05, 0x04, 0x5c, 0x43,
|
||||
0x8c, 0xc7, 0x14, 0x47, 0x94, 0xc1, 0xb7, 0x06, 0x05, 0x00, 0xd4, 0xc3,
|
||||
0x94, 0x41, 0x91, 0x05, 0x11, 0x07, 0xe3, 0xc8, 0xc5, 0xfe, 0xc1, 0x66,
|
||||
0x93, 0x86, 0x06, 0x04, 0xd4, 0xc3, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80
|
||||
};
|
||||
|
||||
static const unsigned char half_wise_write_blob[] = { // size and address must be aligned by 2
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x16, 0x23, 0x96, 0xa1, 0x96, 0x21, 0x16, 0xa3,
|
||||
0x89, 0x05, 0x09, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
|
||||
0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char byte_wise_write_blob[] = { // no division requirements.
|
||||
0x23, 0xa0, 0x05, 0x00, 0x13, 0x07, 0x45, 0x03, 0x0c, 0x43, 0x50, 0x43,
|
||||
0x2e, 0x96, 0x21, 0x07, 0x14, 0x23, 0x94, 0xa1, 0x94, 0x21, 0x14, 0xa3,
|
||||
0x85, 0x05, 0x05, 0x07, 0xe3, 0xca, 0xc5, 0xfe, 0x93, 0x06, 0xf0, 0xff,
|
||||
0x14, 0xc1, 0x82, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// Just set the countdown to 0 to avoid any issues.
|
||||
// li a3, 0; sw a3, 0(a1); li a3, -1; sw a3, 0(a0); ret;
|
||||
static const unsigned char halt_wait_blob[] = {
|
||||
0x81, 0x46, 0x94, 0xc1, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80 };
|
||||
|
||||
// Set the countdown to -1 to cause main system to execute.
|
||||
// li a3, -1; sw a3, 0(a1); li a3, -1; sw a3, 0(a0); ret;
|
||||
//static const unsigned char run_app_blob[] = {
|
||||
// 0xfd, 0x56, 0x94, 0xc1, 0xfd, 0x56, 0x14, 0xc1, 0x82, 0x80 };
|
||||
//
|
||||
// Alternatively, we do it ourselves.
|
||||
static const unsigned char run_app_blob[] = {
|
||||
0x37, 0x07, 0x67, 0x45, 0xb7, 0x27, 0x02, 0x40, 0x13, 0x07, 0x37, 0x12,
|
||||
0x98, 0xd7, 0x37, 0x97, 0xef, 0xcd, 0x13, 0x07, 0xb7, 0x9a, 0x98, 0xd7,
|
||||
0x23, 0xa6, 0x07, 0x00, 0x13, 0x07, 0x00, 0x08, 0x98, 0xcb, 0xb7, 0xf7,
|
||||
0x00, 0xe0, 0x37, 0x07, 0x00, 0x80, 0x23, 0xa8, 0xe7, 0xd0, 0x82, 0x80,
|
||||
};
|
||||
|
||||
|
||||
static void ResetOp( struct B003FunProgrammerStruct * eps )
|
||||
{
|
||||
memset( eps->commandbuffer, 0, sizeof( eps->commandbuffer ) );
|
||||
memcpy( eps->commandbuffer, "\xaa\x00\x00\x00", 4 );
|
||||
eps->commandplace = 4;
|
||||
}
|
||||
|
||||
static void WriteOp4( struct B003FunProgrammerStruct * eps, uint32_t opsend )
|
||||
{
|
||||
int place = eps->commandplace;
|
||||
int newend = place + 4;
|
||||
if( newend < sizeof( eps->commandbuffer ) )
|
||||
{
|
||||
memcpy( eps->commandbuffer + place, &opsend, 4 );
|
||||
}
|
||||
eps->commandplace = newend;
|
||||
}
|
||||
|
||||
|
||||
static void WriteOpArb( struct B003FunProgrammerStruct * eps, const uint8_t * data, int len )
|
||||
{
|
||||
int place = eps->commandplace;
|
||||
int newend = place + len;
|
||||
if( newend < sizeof( eps->commandbuffer ) )
|
||||
{
|
||||
memcpy( eps->commandbuffer + place, data, len );
|
||||
}
|
||||
eps->commandplace = newend;
|
||||
}
|
||||
|
||||
static int CommitOp( struct B003FunProgrammerStruct * eps )
|
||||
{
|
||||
int retries = 0;
|
||||
int r;
|
||||
|
||||
uint32_t magic_go = 0x1234abcd;
|
||||
memcpy( eps->commandbuffer + 124, &magic_go, 4 );
|
||||
|
||||
#ifdef DEBUG_B003
|
||||
{
|
||||
int i;
|
||||
printf( "Commit TX: %lu bytes\n", sizeof(eps->commandbuffer) );
|
||||
for( i = 0; i < sizeof(eps->commandbuffer) ; i++ )
|
||||
{
|
||||
printf( "%02x ", eps->commandbuffer[i] );
|
||||
if( ( i & 0xf ) == 0xf ) printf( "\n" );
|
||||
}
|
||||
if( ( i & 0xf ) != 0xf ) printf( "\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
resend:
|
||||
r = hid_send_feature_report( eps->hd, eps->commandbuffer, sizeof(eps->commandbuffer) );
|
||||
#ifdef DEBUG_B003
|
||||
printf( "hid_send_feature_report = %d\n", r );
|
||||
#endif
|
||||
if( r < 0 )
|
||||
{
|
||||
fprintf( stderr, "Warning: Issue with hid_send_feature_report. Retrying\n" );
|
||||
if( retries++ > 10 )
|
||||
return r;
|
||||
else
|
||||
goto resend;
|
||||
}
|
||||
|
||||
|
||||
if( eps->prepping_for_erase )
|
||||
{
|
||||
usleep(4000);
|
||||
}
|
||||
|
||||
int timeout = 0;
|
||||
|
||||
do
|
||||
{
|
||||
eps->respbuffer[0] = 0xaa;
|
||||
r = hid_get_feature_report( eps->hd, eps->respbuffer, sizeof(eps->respbuffer) );
|
||||
|
||||
#ifdef DEBUG_B003
|
||||
{
|
||||
int i;
|
||||
printf( "Commit RX: %d bytes\n", r );
|
||||
for( i = 0; i < r; i++ )
|
||||
{
|
||||
printf( "%02x ", eps->respbuffer[i] );
|
||||
if( ( i & 0xf ) == 0xf ) printf( "\n" );
|
||||
}
|
||||
if( ( i & 0xf ) != 0xf ) printf( "\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( r < 0 )
|
||||
{
|
||||
if( retries++ > 10 ) return r;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( eps->respbuffer[1] == 0xff ) break;
|
||||
|
||||
if( timeout++ > 20 )
|
||||
{
|
||||
printf( "Error: Timed out waiting for stub to complete\n" );
|
||||
return -99;
|
||||
}
|
||||
} while( 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int B003FunFlushLLCommands( void * dev )
|
||||
{
|
||||
// All commands are synchronous anyway.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int B003FunWaitForDoneOp( void * dev, int ignore )
|
||||
{
|
||||
// It's synchronous, so no issue here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int B003FunDelayUS( void * dev, int microseconds )
|
||||
{
|
||||
usleep( microseconds );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Does not handle erasing
|
||||
static int InternalB003FunWriteBinaryBlob( void * dev, uint32_t address_to_write_to, uint32_t write_size, const uint8_t * blob )
|
||||
{
|
||||
struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct *)dev;
|
||||
|
||||
int is_flash = IsAddressFlash( address_to_write_to );
|
||||
|
||||
if( ( address_to_write_to & 0x1 ) && write_size > 0 )
|
||||
{
|
||||
// Need to do byte-wise writing in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, byte_wise_write_blob, sizeof(byte_wise_write_blob) );
|
||||
WriteOp4( eps, address_to_write_to ); // Base address to write.
|
||||
WriteOp4( eps, 1 ); // write 1 bytes.
|
||||
memcpy( &eps->commandbuffer[60], blob, 1 );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
if( is_flash && memcmp( &eps->commandbuffer[60], blob, 1 ) ) goto verifyfail;
|
||||
blob++;
|
||||
write_size --;
|
||||
address_to_write_to++;
|
||||
}
|
||||
if( ( address_to_write_to & 0x2 ) && write_size > 1 )
|
||||
{
|
||||
// Need to do byte-wise writing in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, half_wise_write_blob, sizeof(half_wise_write_blob) );
|
||||
WriteOp4( eps, address_to_write_to ); // Base address to write.
|
||||
WriteOp4( eps, 2 ); // write 2 bytes.
|
||||
memcpy( &eps->commandbuffer[60], blob, 2 );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
if( is_flash && memcmp( &eps->commandbuffer[60], blob, 2 ) ) goto verifyfail;
|
||||
blob += 2;
|
||||
write_size -= 2;
|
||||
address_to_write_to+=2;
|
||||
}
|
||||
while( write_size > 3 )
|
||||
{
|
||||
int to_write_this_time = write_size & (~3);
|
||||
if( to_write_this_time > 64 ) to_write_this_time = 64;
|
||||
|
||||
// Need to do byte-wise writing in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, word_wise_write_blob, sizeof(word_wise_write_blob) );
|
||||
WriteOp4( eps, address_to_write_to ); // Base address to write.
|
||||
WriteOp4( eps, to_write_this_time ); // write 4 bytes.
|
||||
memcpy( &eps->commandbuffer[60], blob, to_write_this_time );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
if( is_flash && memcmp( &eps->commandbuffer[60], blob, to_write_this_time ) ) goto verifyfail;
|
||||
blob += to_write_this_time;
|
||||
write_size -= to_write_this_time;
|
||||
address_to_write_to += to_write_this_time;
|
||||
}
|
||||
if( write_size > 1 )
|
||||
{
|
||||
// Need to do byte-wise writing in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, half_wise_write_blob, sizeof(half_wise_write_blob) );
|
||||
WriteOp4( eps, address_to_write_to ); // Base address to write.
|
||||
WriteOp4( eps, 2 ); // write 2 bytes.
|
||||
memcpy( &eps->commandbuffer[60], blob, 2 );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
if( is_flash && memcmp( &eps->commandbuffer[60], blob, 2 ) ) goto verifyfail;
|
||||
blob += 2;
|
||||
write_size -= 2;
|
||||
address_to_write_to += 2;
|
||||
}
|
||||
if( write_size )
|
||||
{
|
||||
// Need to do byte-wise writing in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, byte_wise_write_blob, sizeof(byte_wise_write_blob) );
|
||||
WriteOp4( eps, address_to_write_to ); // Base address to write.
|
||||
WriteOp4( eps, 1 ); // write 1 byte.
|
||||
memcpy( &eps->commandbuffer[60], blob, 1 );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
if( is_flash && memcmp( &eps->commandbuffer[60], blob, 1 ) ) goto verifyfail;
|
||||
blob += 1;
|
||||
write_size -= 1;
|
||||
address_to_write_to+=1;
|
||||
}
|
||||
eps->prepping_for_erase = 0;
|
||||
return 0;
|
||||
verifyfail:
|
||||
fprintf( stderr, "Error: Write Binary Blob: %d bytes to %08x\n", write_size, address_to_write_to );
|
||||
return -6;
|
||||
}
|
||||
|
||||
static int B003FunReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob )
|
||||
{
|
||||
struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct *)dev;
|
||||
|
||||
#ifdef DEBUG_B003
|
||||
printf( "Read Binary Blob: %d bytes from %08x\n", read_size, address_to_read_from );
|
||||
#endif
|
||||
|
||||
if( ( address_to_read_from & 0x1 ) && read_size > 0 )
|
||||
{
|
||||
// Need to do byte-wise reading in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, byte_wise_read_blob, sizeof(byte_wise_read_blob) );
|
||||
WriteOp4( eps, address_to_read_from ); // Base address to read.
|
||||
WriteOp4( eps, 1 ); // Read 1 bytes.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
memcpy( blob, &eps->respbuffer[60], 1 );
|
||||
blob++;
|
||||
read_size --;
|
||||
address_to_read_from++;
|
||||
}
|
||||
if( ( address_to_read_from & 0x2 ) && read_size > 1 )
|
||||
{
|
||||
// Need to do byte-wise reading in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, half_wise_read_blob, sizeof(half_wise_read_blob) );
|
||||
WriteOp4( eps, address_to_read_from ); // Base address to read.
|
||||
WriteOp4( eps, 2 ); // Read 2 bytes.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
memcpy( blob, &eps->respbuffer[60], 2 );
|
||||
blob += 2;
|
||||
read_size -= 2;
|
||||
address_to_read_from+=2;
|
||||
}
|
||||
while( read_size > 3 )
|
||||
{
|
||||
int to_read_this_time = read_size & (~3);
|
||||
if( to_read_this_time > 64 ) to_read_this_time = 64;
|
||||
|
||||
// Need to do byte-wise reading in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, word_wise_read_blob, sizeof(word_wise_read_blob) );
|
||||
WriteOp4( eps, address_to_read_from ); // Base address to read.
|
||||
WriteOp4( eps, to_read_this_time ); // Read 4 bytes.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
memcpy( blob, &eps->respbuffer[60], to_read_this_time );
|
||||
blob += to_read_this_time;
|
||||
read_size -= to_read_this_time;
|
||||
address_to_read_from += to_read_this_time;
|
||||
}
|
||||
if( read_size > 1 )
|
||||
{
|
||||
// Need to do byte-wise reading in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, half_wise_read_blob, sizeof(half_wise_read_blob) );
|
||||
WriteOp4( eps, address_to_read_from ); // Base address to read.
|
||||
WriteOp4( eps, 2 ); // Read 2 bytes.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
memcpy( blob, &eps->respbuffer[60], 2 );
|
||||
blob += 2;
|
||||
read_size -= 2;
|
||||
address_to_read_from += 2;
|
||||
}
|
||||
if( read_size )
|
||||
{
|
||||
// Need to do byte-wise reading in front to line up with word alignment.
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, byte_wise_read_blob, sizeof(byte_wise_read_blob) );
|
||||
WriteOp4( eps, address_to_read_from ); // Base address to read.
|
||||
WriteOp4( eps, 1 ); // Read 1 byte.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
memcpy( blob, &eps->respbuffer[60], 1 );
|
||||
blob += 1;
|
||||
read_size -= 1;
|
||||
address_to_read_from+=1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InternalB003FunBoot( void * dev )
|
||||
{
|
||||
struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
|
||||
|
||||
printf( "Booting\n" );
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, run_app_blob, sizeof(run_app_blob) );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int B003FunSetupInterface( void * dev )
|
||||
{
|
||||
struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
|
||||
printf( "Halting Boot Countdown\n" );
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, halt_wait_blob, sizeof(halt_wait_blob) );
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int B003FunExit( void * dev )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MUST be 4-byte-aligned.
|
||||
static int B003FunWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
|
||||
{
|
||||
return InternalB003FunWriteBinaryBlob( dev, address_to_write, 4, (uint8_t*)&data );
|
||||
}
|
||||
|
||||
static int B003FunReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
|
||||
{
|
||||
return B003FunReadBinaryBlob( dev, address_to_read, 4, (uint8_t*)data );
|
||||
}
|
||||
|
||||
static int B003FunBlockWrite64( void * dev, uint32_t address_to_write, uint8_t * data )
|
||||
{
|
||||
struct B003FunProgrammerStruct * eps = (struct B003FunProgrammerStruct*) dev;
|
||||
struct InternalState * iss = eps->internal;
|
||||
|
||||
if( IsAddressFlash( address_to_write ) )
|
||||
{
|
||||
if( !iss->flash_unlocked )
|
||||
{
|
||||
int rw;
|
||||
if( ( rw = InternalUnlockFlash( dev, iss ) ) )
|
||||
return rw;
|
||||
}
|
||||
|
||||
if( !InternalIsMemoryErased( iss, address_to_write ) )
|
||||
{
|
||||
if( MCF.Erase( dev, address_to_write, 64, 0 ) )
|
||||
{
|
||||
fprintf( stderr, "Error: Failed to erase sector at %08x\n", address_to_write );
|
||||
return -9;
|
||||
}
|
||||
}
|
||||
|
||||
// Not actually needed.
|
||||
MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010
|
||||
MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG | CR_BUF_RST); // (intptr_t)&FLASH->CTLR = 0x40022010
|
||||
|
||||
ResetOp( eps );
|
||||
WriteOpArb( eps, write64_flash, sizeof(write64_flash) );
|
||||
WriteOp4( eps, address_to_write ); // Base address to write. @52
|
||||
WriteOp4( eps, 0x4002200c ); // FLASH STATR base address. @ 56
|
||||
memcpy( &eps->commandbuffer[60], data, 64 ); // @60
|
||||
if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev ); // Give the programmer a headsup this next operation could take a while.
|
||||
if( CommitOp( eps ) ) return -5;
|
||||
|
||||
// This is actually built-in.
|
||||
// MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set); // (intptr_t)&FLASH->CTLR = 0x40022010 (actually commit)
|
||||
}
|
||||
else
|
||||
{
|
||||
return InternalB003FunWriteBinaryBlob( dev, address_to_write, 64, data );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int B003FunWriteHalfWord( void * dev, uint32_t address_to_write, uint16_t data )
|
||||
{
|
||||
return InternalB003FunWriteBinaryBlob( dev, address_to_write, 2, (uint8_t*)&data );
|
||||
}
|
||||
|
||||
static int B003FunReadHalfWord( void * dev, uint32_t address_to_read, uint16_t * data )
|
||||
{
|
||||
return B003FunReadBinaryBlob( dev, address_to_read, 2, (uint8_t*)data );
|
||||
}
|
||||
|
||||
static int B003FunWriteByte( void * dev, uint32_t address_to_write, uint8_t data )
|
||||
{
|
||||
return InternalB003FunWriteBinaryBlob( dev, address_to_write, 1, &data );
|
||||
}
|
||||
|
||||
static int B003FunReadByte( void * dev, uint32_t address_to_read, uint8_t * data )
|
||||
{
|
||||
return B003FunReadBinaryBlob( dev, address_to_read, 1, data );
|
||||
}
|
||||
|
||||
|
||||
static int B003FunHaltMode( void * dev, int mode )
|
||||
{
|
||||
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
||||
switch ( mode )
|
||||
{
|
||||
case HALT_MODE_HALT_BUT_NO_RESET: // Don't reboot.
|
||||
case HALT_MODE_HALT_AND_RESET: // Reboot and halt
|
||||
// This programmer is always halted anyway.
|
||||
break;
|
||||
|
||||
case HALT_MODE_REBOOT: // Actually boot?
|
||||
InternalB003FunBoot( dev );
|
||||
break;
|
||||
|
||||
case HALT_MODE_RESUME:
|
||||
fprintf( stderr, "Warning: this programmer cannot resume\n" );
|
||||
// We can't do this.
|
||||
break;
|
||||
|
||||
case HALT_MODE_GO_TO_BOOTLOADER:
|
||||
fprintf( stderr, "Warning: this programmer is already a bootloader. Can't go into bootloader\n" );
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf( stderr, "Error: Unknown halt mode %d\n", mode );
|
||||
}
|
||||
|
||||
iss->processor_in_mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int B003FunPrepForLongOp( void * dev )
|
||||
{
|
||||
struct B003FunProgrammerStruct * d = (struct B003FunProgrammerStruct*)dev;
|
||||
d->prepping_for_erase = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void * TryInit_B003Fun()
|
||||
{
|
||||
#define VID 0x1209
|
||||
#define PID 0xb003
|
||||
hid_init();
|
||||
hid_device * hd = hid_open( VID, PID, 0); // third parameter is "serial"
|
||||
if( !hd ) return 0;
|
||||
|
||||
//extern int g_hidapiSuppress;
|
||||
//g_hidapiSuppress = 1; // Suppress errors for this device. (don't do this yet)
|
||||
|
||||
struct B003FunProgrammerStruct * eps = malloc( sizeof( struct B003FunProgrammerStruct ) );
|
||||
memset( eps, 0, sizeof( *eps ) );
|
||||
eps->hd = hd;
|
||||
eps->commandplace = 1;
|
||||
|
||||
memset( &MCF, 0, sizeof( MCF ) );
|
||||
MCF.WriteReg32 = 0;
|
||||
MCF.ReadReg32 = 0;
|
||||
MCF.FlushLLCommands = B003FunFlushLLCommands;
|
||||
MCF.DelayUS = B003FunDelayUS;
|
||||
MCF.Control3v3 = 0;
|
||||
MCF.SetupInterface = B003FunSetupInterface;
|
||||
MCF.Exit = B003FunExit;
|
||||
MCF.HaltMode = 0;
|
||||
MCF.VoidHighLevelState = 0;
|
||||
MCF.PollTerminal = 0;
|
||||
|
||||
// These are optional. Disabling these is a good mechanismto make sure the core functions still work.
|
||||
MCF.WriteWord = B003FunWriteWord;
|
||||
MCF.ReadWord = B003FunReadWord;
|
||||
|
||||
MCF.WriteHalfWord = B003FunWriteHalfWord;
|
||||
MCF.ReadHalfWord = B003FunReadHalfWord;
|
||||
|
||||
MCF.WriteByte = B003FunWriteByte;
|
||||
MCF.ReadByte = B003FunReadByte;
|
||||
|
||||
MCF.WaitForDoneOp = B003FunWaitForDoneOp;
|
||||
MCF.BlockWrite64 = B003FunBlockWrite64;
|
||||
MCF.ReadBinaryBlob = B003FunReadBinaryBlob;
|
||||
|
||||
MCF.PrepForLongOp = B003FunPrepForLongOp;
|
||||
|
||||
MCF.HaltMode = B003FunHaltMode;
|
||||
|
||||
return eps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Utility for generating bootloader code:
|
||||
|
||||
// make rv003usb.bin && xxd -i -s 100 -l 44 rv003usb.bin
|
||||
|
||||
/*
|
||||
// Read data, arbitrarily from memory. (byte-wise)
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
XW_C_LBU(a3, a1, 0); //lbu a3, 0(a1) // Read from RAM
|
||||
XW_C_SB(a3, a4, 0); //sb a3, 0(a4) // Store into scratchpad
|
||||
c.addi a1, 1 // Advance pointers
|
||||
c.addi a4, 1
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0,0
|
||||
*/
|
||||
|
||||
/*
|
||||
// Read data, arbitrarily from memory. (half-wise)
|
||||
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
XW_C_LHU(a3, a1, 0); //lhu a3, 0(a1) // Read from RAM
|
||||
XW_C_SH(a3, a4, 0); //sh a3, 0(a4) // Store into scratchpad
|
||||
c.addi a1, 2 // Advance pointers
|
||||
c.addi a4, 2
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0,0
|
||||
*/
|
||||
|
||||
/*
|
||||
// Read data, arbitrarily from memory. (word-wise)
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
lw a3, 0(a1); //lw a3, 0(a1) // Read from RAM
|
||||
sw a3, 0(a4); //sw a3, 0(a4) // Store into scratchpad
|
||||
c.addi a1, 4 // Advance pointers
|
||||
c.addi a4, 4
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0,0
|
||||
*/
|
||||
/*
|
||||
// Write data, arbitrarily to memory. (word-wise)
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
lw a3, 0(a4); //lw a3, 0(a1) // Read from RAM
|
||||
sw a3, 0(a1); //sw a3, 0(a4) // Store into scratchpad
|
||||
lw a3, 0(a1); // Read-back
|
||||
sw a3, 0(a4);
|
||||
c.addi a1, 4 // Advance pointers
|
||||
c.addi a4, 4
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0
|
||||
*/
|
||||
|
||||
/*
|
||||
// Write data, arbitrarily to memory. (word-wise)
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
XW_C_LHU(a3, a4, 0); //lbu a3, 0(a4) // Read from scratchpad
|
||||
XW_C_SH(a3, a1, 0); //sb a3, 0(a1) // Store into RAM
|
||||
XW_C_LHU(a3, a1, 0); //lbu a3, 0(a4) // Read back
|
||||
XW_C_SH(a3, a4, 0); //sb a3, 0(a1)
|
||||
c.addi a1, 2 // Advance pointers
|
||||
c.addi a4, 2
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0
|
||||
*/
|
||||
|
||||
/*
|
||||
// Write data, arbitrarily to memory. (byte-wise)
|
||||
. = 0x66
|
||||
sw x0, 0(a1); // Stop Countdown
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // Get starting address to read
|
||||
c.lw a2, 4(a4); // Get length to read.
|
||||
c.add a2, a1 // a2 is now ending address.
|
||||
c.addi a4, 8 // start writing back at byte 60.
|
||||
1:
|
||||
XW_C_LBU(a3, a4, 0); //lbu a3, 0(a4) // Read from scratchpad
|
||||
XW_C_SB(a3, a1, 0); //sb a3, 0(a1) // Store into RAM
|
||||
XW_C_LBU(a3, a1, 0); //Read back
|
||||
XW_C_SB(a3, a4, 0);
|
||||
c.addi a1, 1 // Advance pointers
|
||||
c.addi a4, 1
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
addi a3, x0, -1
|
||||
sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
.long 0,0,0,0,0,0
|
||||
*/
|
||||
|
||||
|
||||
/* Run app blob
|
||||
FLASH->BOOT_MODEKEYR = FLASH_KEY1;
|
||||
FLASH->BOOT_MODEKEYR = FLASH_KEY2;
|
||||
FLASH->STATR = 0; // 1<<14 is zero, so, boot user code.
|
||||
FLASH->CTLR = CR_LOCK_Set;
|
||||
PFIC->SCTLR = 1<<31;
|
||||
*/
|
||||
|
||||
|
||||
/* Write flash block 64.
|
||||
|
||||
. = 0x66
|
||||
addi a4, a0, 52; // Start reading properties, starting from scratchpad + 52.
|
||||
c.lw a1, 0(a4); // a1 = Address to write to.
|
||||
addi a2, a1, 64 // a2 = end of section to write to
|
||||
c.lw a5, 4(a4); // a5 = Get flash address (0x40022010)
|
||||
|
||||
// Must be done outside.
|
||||
// li a3, 0x00080000 | 0x00010000;
|
||||
//c.sw a3, 0(a5); //FLASH->CTLR = CR_BUF_RST | CR_PAGE_PG
|
||||
c.sw a1, 8(a5); //FLASH->ADDR = writing location.
|
||||
|
||||
1:
|
||||
c.lw a3, 8(a4); //lw a3, 0(a1) // Read from RAM (Starting @60)
|
||||
c.sw a3, 0(a1); //sw a3, 0(a4) // Store into flash
|
||||
|
||||
li a3, 0x00010000 | 0x00040000; // CR_PAGE_PG | FLASH_CTLR_BUF_LOAD
|
||||
c.sw a3, 4(a5); // Load into flash write buffer.
|
||||
|
||||
c.lw a3, 0(a1); //Tricky: By reading from flash here, we force it to wait for completion.
|
||||
c.addi a1, 4 // Advance pointers
|
||||
c.addi a4, 4
|
||||
|
||||
// // Wait for write to complete.
|
||||
// 2: c.lw a3, 0(a5) // read FLASH->STATR
|
||||
// c.andi a3, 1 // Mask off BUSY bit.
|
||||
// c.bnez a3, 2b
|
||||
|
||||
|
||||
blt a1, a2, 1b // Loop til all read.
|
||||
|
||||
li a3, 0x00010000 | 0x00000040 //CR_PAGE_PG|CR_STRT_Set
|
||||
c.sw a3, 4(a5); //FLASH->CTRL = CR_PAGE_PG|CR_STRT_Set
|
||||
li a3, -1
|
||||
c.sw a3, 0(a0) // Write -1 into 0x00 indicating all done.
|
||||
ret
|
||||
*/
|
456
minichlink/pgm-esp32s2-ch32xx.c
Normal file
456
minichlink/pgm-esp32s2-ch32xx.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
#include <stdint.h>
|
||||
#include "hidapi.c"
|
||||
#include "minichlink.h"
|
||||
|
||||
struct ESP32ProgrammerStruct
|
||||
{
|
||||
void * internal;
|
||||
|
||||
hid_device * hd;
|
||||
uint32_t state;
|
||||
uint8_t commandbuffer[256];
|
||||
int commandplace;
|
||||
uint8_t reply[256];
|
||||
int replylen;
|
||||
|
||||
int dev_version;
|
||||
};
|
||||
|
||||
int ESPFlushLLCommands( void * dev );
|
||||
|
||||
static inline int SRemain( struct ESP32ProgrammerStruct * e )
|
||||
{
|
||||
return sizeof( e->commandbuffer ) - e->commandplace - 2; //Need room for EOF.
|
||||
}
|
||||
|
||||
static inline void Write4LE( struct ESP32ProgrammerStruct * e, uint32_t val )
|
||||
{
|
||||
if( SRemain( e ) < 4 ) return;
|
||||
uint8_t * d = e->commandbuffer + e->commandplace;
|
||||
d[0] = val & 0xff;
|
||||
d[1] = (val>>8) & 0xff;
|
||||
d[2] = (val>>16) & 0xff;
|
||||
d[3] = (val>>24) & 0xff;
|
||||
e->commandplace += 4;
|
||||
}
|
||||
|
||||
static inline void Write2LE( struct ESP32ProgrammerStruct * e, uint16_t val )
|
||||
{
|
||||
if( SRemain( e ) < 2 ) return;
|
||||
uint8_t * d = e->commandbuffer + e->commandplace;
|
||||
d[0] = val & 0xff;
|
||||
d[1] = (val>>8) & 0xff;
|
||||
e->commandplace += 2;
|
||||
}
|
||||
|
||||
static inline void Write1( struct ESP32ProgrammerStruct * e, uint8_t val )
|
||||
{
|
||||
if( SRemain( e ) < 1 ) return;
|
||||
uint8_t * d = e->commandbuffer + e->commandplace;
|
||||
d[0] = val & 0xff;
|
||||
e->commandplace ++;
|
||||
}
|
||||
|
||||
|
||||
static int ESPWriteReg32( void * dev, uint8_t reg_7_bit, uint32_t value )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
// printf( "WriteReg: %02x -> %08x\n", reg_7_bit, value );
|
||||
|
||||
|
||||
if( SRemain( eps ) < 5 ) ESPFlushLLCommands( eps );
|
||||
|
||||
Write1( eps, (reg_7_bit<<1) | 1 );
|
||||
Write4LE( eps, value );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ESPReadReg32( void * dev, uint8_t reg_7_bit, uint32_t * commandresp )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
ESPFlushLLCommands( eps );
|
||||
Write1( eps, (reg_7_bit<<1) | 0 );
|
||||
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
// printf( "ReadReg: %02x -> %d\n", reg_7_bit,eps->replylen );
|
||||
|
||||
if( eps->replylen < 6 )
|
||||
{
|
||||
return -9;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( commandresp, eps->reply+2, 4 );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ESPFlushLLCommands( void * dev )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
|
||||
if( eps->commandplace >= sizeof( eps->commandbuffer ) )
|
||||
{
|
||||
fprintf( stderr, "Error: Command buffer overflow\n" );
|
||||
return -5;
|
||||
}
|
||||
|
||||
if( eps->commandplace == 1 ) return 0;
|
||||
|
||||
int r;
|
||||
|
||||
eps->commandbuffer[0] = 0xad; // Key report ID
|
||||
eps->commandbuffer[eps->commandplace] = 0xff;
|
||||
|
||||
#if 0
|
||||
int i;
|
||||
for( i = 0; i < eps->commandplace; i++ )
|
||||
{
|
||||
if( ( i & 0xff ) == 0 ) printf( "\n" );
|
||||
printf( "%02x ", eps->commandbuffer[i] );
|
||||
}
|
||||
printf("\n" );
|
||||
#endif
|
||||
|
||||
r = hid_send_feature_report( eps->hd, eps->commandbuffer, 255 );
|
||||
eps->commandplace = 1;
|
||||
if( r < 0 )
|
||||
{
|
||||
fprintf( stderr, "Error: Got error %d when sending hid feature report.\n", r );
|
||||
exit( -9 );
|
||||
}
|
||||
retry:
|
||||
eps->reply[0] = 0xad; // Key report ID
|
||||
r = hid_get_feature_report( eps->hd, eps->reply, sizeof( eps->reply ) );
|
||||
/*
|
||||
int i;
|
||||
printf( "RESP: %d\n",eps->reply[0] );
|
||||
|
||||
for( i = 0; i < eps->reply[0]; i++ )
|
||||
{
|
||||
printf( "%02x ", eps->reply[i+1] );
|
||||
if( (i % 16) == 15 ) printf( "\n" );
|
||||
}
|
||||
printf( "\n" );*/
|
||||
|
||||
if( eps->reply[0] == 0xff ) goto retry;
|
||||
//printf( ">:::%d: %02x %02x %02x %02x %02x %02x\n", eps->replylen, eps->reply[0], eps->reply[1], eps->reply[2], eps->reply[3], eps->reply[4], eps->reply[5] );
|
||||
if( r < 0 )
|
||||
{
|
||||
fprintf( stderr, "Error: Got error %d when sending hid feature report.\n", r );
|
||||
return r;
|
||||
}
|
||||
eps->replylen = eps->reply[0] + 1; // Include the header byte.
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int ESPControl3v3( void * dev, int bOn )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
|
||||
if( SRemain( eps ) < 2 )
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
if( bOn )
|
||||
Write2LE( eps, 0x03fe );
|
||||
else
|
||||
Write2LE( eps, 0x02fe );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ESPReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
//printf( "READ: %08x\n", address_to_read );
|
||||
if( SRemain( eps ) < 6 )
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
Write2LE( eps, 0x09fe );
|
||||
Write4LE( eps, address_to_read );
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
// printf( "Got: %d\n", eps->replylen );
|
||||
if( eps->replylen < 5 )
|
||||
{
|
||||
return -9;
|
||||
}
|
||||
int tail = eps->replylen-5;
|
||||
memcpy( data, eps->reply + tail + 1, 4 );
|
||||
// printf( "Read Mem: %08x => %08x\n", address_to_read, *data );
|
||||
return eps->reply[tail];
|
||||
}
|
||||
|
||||
int ESPWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
|
||||
//printf( "WRITE: %08x\n", address_to_write );
|
||||
|
||||
if( SRemain( eps ) < 10 )
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
Write2LE( eps, 0x08fe );
|
||||
Write4LE( eps, address_to_write );
|
||||
Write4LE( eps, data );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ESPDelayUS( void * dev, int microseconds )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
if( SRemain( eps ) < 6 )
|
||||
ESPFlushLLCommands( eps );
|
||||
|
||||
Write2LE( eps, 0x04fe );
|
||||
Write2LE( eps, microseconds );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ESPWaitForFlash( void * dev )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
if( SRemain( eps ) < 2 )
|
||||
ESPFlushLLCommands( eps );
|
||||
Write2LE( eps, 0x06fe );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ESPWaitForDoneOp( void * dev, int ignore )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
if( SRemain( eps ) < 2 )
|
||||
ESPFlushLLCommands( dev );
|
||||
Write2LE( eps, 0x07fe );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ESPExit( void * dev )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
hid_close( eps->hd );
|
||||
free( eps );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ESPBlockWrite64( void * dev, uint32_t address_to_write, uint8_t * data )
|
||||
{
|
||||
int writeretry = 0;
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
ESPFlushLLCommands( dev );
|
||||
|
||||
retry:
|
||||
|
||||
if( eps->dev_version >= 2 && InternalIsMemoryErased( (struct InternalState*)eps->internal, address_to_write ) )
|
||||
Write2LE( eps, 0x0efe );
|
||||
else
|
||||
Write2LE( eps, 0x0bfe );
|
||||
Write4LE( eps, address_to_write );
|
||||
|
||||
int i;
|
||||
int timeout = 0;
|
||||
for( i = 0; i < 64; i++ ) Write1( eps, data[i] );
|
||||
|
||||
InternalMarkMemoryNotErased( (struct InternalState*)eps->internal, address_to_write );
|
||||
|
||||
do
|
||||
{
|
||||
ESPFlushLLCommands( dev );
|
||||
timeout++;
|
||||
if( timeout > 1000 )
|
||||
{
|
||||
fprintf( stderr, "Error: Timed out block-writing 64\n" );
|
||||
return -49;
|
||||
}
|
||||
} while( eps->replylen < 2 );
|
||||
|
||||
if( eps->reply[1] )
|
||||
{
|
||||
fprintf( stderr, "Error: Got code %d from ESP write algo. %d [%02x %02x %02x]\n", (char)eps->reply[1], eps->replylen, eps->reply[0], eps->reply[1], eps->reply[2] );
|
||||
if( writeretry < 10 )
|
||||
{
|
||||
writeretry++;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return (char)eps->reply[1];
|
||||
}
|
||||
|
||||
int ESPPerformSongAndDance( void * dev )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
Write2LE( eps, 0x01fe );
|
||||
ESPFlushLLCommands( dev );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ESPVoidHighLevelState( void * dev )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
Write2LE( eps, 0x05fe );
|
||||
ESPFlushLLCommands( dev );
|
||||
DefaultVoidHighLevelState( dev );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ESPVendorCommand( void * dev, const char * cmd )
|
||||
{
|
||||
char command[10] = { 0 };
|
||||
char tbuf[10] = { 0 };
|
||||
int fields[10];
|
||||
char c;
|
||||
int i = 0;
|
||||
int f = 0;
|
||||
while( (c = *cmd++) )
|
||||
{
|
||||
if( c == ':' ) break;
|
||||
if( c == '\0' ) break;
|
||||
if( i + 1 >= sizeof( command )) break;
|
||||
command[i++] = c;
|
||||
command[i] = 0;
|
||||
}
|
||||
i = 0;
|
||||
f = 0;
|
||||
while( 1 )
|
||||
{
|
||||
c = *cmd++;
|
||||
if( c == ':' || c == '\0' )
|
||||
{
|
||||
fields[f++] = SimpleReadNumberInt( tbuf, 0 );
|
||||
puts( tbuf );
|
||||
if( f == 10 ) break;
|
||||
tbuf[0] = 0;
|
||||
i = 0;
|
||||
if( c == '\0' ) break;
|
||||
continue;
|
||||
}
|
||||
if( i + 1 >= sizeof( tbuf )) break;
|
||||
tbuf[i++] = c;
|
||||
tbuf[i] = 0;
|
||||
}
|
||||
printf( "Got Vendor Command \"%s\"\n", command );
|
||||
ESPFlushLLCommands( dev );
|
||||
if( strcasecmp( command, "ECLK" ) == 0 )
|
||||
{
|
||||
printf( "Setting up external clock on pin.\n" );
|
||||
if( f < 5 )
|
||||
{
|
||||
fprintf( stderr, "Error: Need fields :use_apll:sdm0:sdm1:sdm2:odiv try 1:0:0:8:3 for 24MHz\n" );
|
||||
fprintf( stderr, "Definition:\n\
|
||||
use_apll = Configures APLL = 480 / 4 = 120\n\
|
||||
40 * (SDM2 + SDM1/(2^8) + SDM0/(2^16) + 4) / ( 2 * (ODIV+2) );\n\
|
||||
Datasheet recommends that numerator is between 300 and 500MHz.\n ");
|
||||
return -9;
|
||||
}
|
||||
Write2LE( dev, 0x0cfe );
|
||||
Write1( dev, fields[0] );
|
||||
Write1( dev, fields[1] );
|
||||
Write1( dev, fields[2] );
|
||||
Write1( dev, fields[3] );
|
||||
Write1( dev, fields[4] );
|
||||
Write1( dev, 0 );
|
||||
Write1( dev, 0 );
|
||||
Write1( dev, 0 );
|
||||
ESPFlushLLCommands( dev );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Error: Unknown vendor command %s\n", command );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ESPPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leaveflagA, int leaveflagB )
|
||||
{
|
||||
struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev;
|
||||
ESPFlushLLCommands( dev );
|
||||
Write1( dev, 0xfe );
|
||||
Write1( dev, 0x0d );
|
||||
Write4LE( dev, leaveflagA );
|
||||
Write4LE( dev, leaveflagB );
|
||||
Write1( dev, 0xff );
|
||||
|
||||
ESPFlushLLCommands( dev );
|
||||
|
||||
int rlen = eps->reply[0];
|
||||
if( rlen < 1 ) return -8;
|
||||
|
||||
|
||||
#if 0
|
||||
int i;
|
||||
|
||||
printf( "RESP (ML %d): %d\n", maxlen,eps->reply[0] );
|
||||
|
||||
for( i = 0; i < eps->reply[0]; i++ )
|
||||
{
|
||||
printf( "%02x ", eps->reply[i+1] );
|
||||
if( (i % 16) == 15 ) printf( "\n" );
|
||||
}
|
||||
printf( "\n" );
|
||||
#endif
|
||||
|
||||
int errc = eps->reply[1];
|
||||
if( errc > 7 ) return -7;
|
||||
|
||||
if( rlen - 1 >= maxlen ) return -6;
|
||||
|
||||
memcpy( buffer, eps->reply + 2, rlen - 1 );
|
||||
|
||||
return rlen - 1;
|
||||
}
|
||||
|
||||
|
||||
void * TryInit_ESP32S2CHFUN()
|
||||
{
|
||||
#define VID 0x303a
|
||||
#define PID 0x4004
|
||||
hid_init();
|
||||
hid_device * hd = hid_open( VID, PID, L"s2-ch32xx-pgm-v0"); // third parameter is "serial"
|
||||
if( !hd ) return 0;
|
||||
|
||||
struct ESP32ProgrammerStruct * eps = malloc( sizeof( struct ESP32ProgrammerStruct ) );
|
||||
memset( eps, 0, sizeof( *eps ) );
|
||||
eps->hd = hd;
|
||||
eps->commandplace = 1;
|
||||
eps->dev_version = 0;
|
||||
|
||||
memset( &MCF, 0, sizeof( MCF ) );
|
||||
MCF.WriteReg32 = ESPWriteReg32;
|
||||
MCF.ReadReg32 = ESPReadReg32;
|
||||
MCF.FlushLLCommands = ESPFlushLLCommands;
|
||||
MCF.DelayUS = ESPDelayUS;
|
||||
MCF.Control3v3 = ESPControl3v3;
|
||||
MCF.Exit = ESPExit;
|
||||
MCF.VoidHighLevelState = ESPVoidHighLevelState;
|
||||
MCF.PollTerminal = ESPPollTerminal;
|
||||
|
||||
// These are optional. Disabling these is a good mechanismto make sure the core functions still work.
|
||||
MCF.WriteWord = ESPWriteWord;
|
||||
MCF.ReadWord = ESPReadWord;
|
||||
|
||||
MCF.WaitForFlash = ESPWaitForFlash;
|
||||
MCF.WaitForDoneOp = ESPWaitForDoneOp;
|
||||
|
||||
MCF.PerformSongAndDance = ESPPerformSongAndDance;
|
||||
|
||||
MCF.BlockWrite64 = ESPBlockWrite64;
|
||||
MCF.VendorCommand = ESPVendorCommand;
|
||||
|
||||
// Reset internal programmer state.
|
||||
Write2LE( eps, 0x0afe );
|
||||
ESPFlushLLCommands( eps );
|
||||
Write2LE( eps, 0xfefe );
|
||||
ESPFlushLLCommands( eps );
|
||||
if( eps->replylen > 1 )
|
||||
{
|
||||
eps->dev_version = eps->reply[1];
|
||||
}
|
||||
return eps;
|
||||
}
|
||||
|
||||
|
786
minichlink/pgm-wch-linke.c
Normal file
786
minichlink/pgm-wch-linke.c
Normal file
|
@ -0,0 +1,786 @@
|
|||
// The "bootloader" blob is (C) WCH.
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libusb.h"
|
||||
#include "minichlink.h"
|
||||
|
||||
struct LinkEProgrammerStruct
|
||||
{
|
||||
void * internal;
|
||||
libusb_device_handle * devh;
|
||||
int lasthaltmode; // For non-003 chips
|
||||
};
|
||||
|
||||
static void printChipInfo(enum RiscVChip chip) {
|
||||
switch(chip) {
|
||||
case CHIP_CH32V10x:
|
||||
fprintf(stderr, "Detected: CH32V10x\n");
|
||||
break;
|
||||
case CHIP_CH57x:
|
||||
fprintf(stderr, "Detected: CH57x\n");
|
||||
break;
|
||||
case CHIP_CH56x:
|
||||
fprintf(stderr, "Detected: CH56x\n");
|
||||
break;
|
||||
case CHIP_CH32V20x:
|
||||
fprintf(stderr, "Detected: CH32V20x\n");
|
||||
break;
|
||||
case CHIP_CH32V30x:
|
||||
fprintf(stderr, "Detected: CH32V30x\n");
|
||||
break;
|
||||
case CHIP_CH58x:
|
||||
fprintf(stderr, "Detected: CH58x\n");
|
||||
break;
|
||||
case CHIP_CH32V003:
|
||||
fprintf(stderr, "Detected: CH32V003\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int checkChip(enum RiscVChip chip) {
|
||||
switch(chip) {
|
||||
case CHIP_CH32V003:
|
||||
return 0; // Use direct mode
|
||||
case CHIP_CH32V10x:
|
||||
case CHIP_CH32V20x:
|
||||
case CHIP_CH32V30x:
|
||||
return 1; // Use binary blob mode
|
||||
case CHIP_CH56x:
|
||||
case CHIP_CH57x:
|
||||
case CHIP_CH58x:
|
||||
default:
|
||||
return -1; // Not supported yet
|
||||
}
|
||||
}
|
||||
|
||||
// For non-ch32v003 chips.
|
||||
//static int LEReadBinaryBlob( void * d, uint32_t offset, uint32_t amount, uint8_t * readbuff );
|
||||
static int InternalLinkEHaltMode( void * d, int mode );
|
||||
static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len, uint8_t * blob );
|
||||
|
||||
#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 void * command_v, int commandlen, int * transferred, uint8_t * reply, int replymax )
|
||||
{
|
||||
uint8_t * command = (uint8_t*)command_v;
|
||||
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, command, commandlen, transferred, WCHTIMEOUT );
|
||||
if( status ) goto sendfail;
|
||||
got_to_recv = 1;
|
||||
if( !reply )
|
||||
{
|
||||
reply = buffer; replymax = sizeof( buffer );
|
||||
}
|
||||
|
||||
// printf("wch_link_command send (%d)", commandlen); for(int i = 0; i< commandlen; printf(" %02x",command[i++])); printf("\n");
|
||||
|
||||
status = libusb_bulk_transfer( devh, 0x81, reply, replymax, transferred, WCHTIMEOUT );
|
||||
|
||||
// printf("wch_link_command reply (%d)", *transferred); for(int i = 0; i< *transferred; printf(" %02x",reply[i++])); printf("\n");
|
||||
|
||||
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;
|
||||
ssize_t cnt = libusb_get_device_list(ctx, &list);
|
||||
ssize_t i = 0;
|
||||
|
||||
libusb_device *found = NULL;
|
||||
libusb_device * found_arm_programmer = NULL;
|
||||
libusb_device * found_programmer_in_iap = NULL;
|
||||
|
||||
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( r == 0 && desc.idVendor == 0x1a86 && desc.idProduct == 0x8012) { found_arm_programmer = device; }
|
||||
if( r == 0 && desc.idVendor == 0x4348 && desc.idProduct == 0x55e0) { found_programmer_in_iap = device; }
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
// On a lark see if we have a programmer which got stuck in IAP mode.
|
||||
|
||||
if (found_arm_programmer) {
|
||||
fprintf( stderr, "Warning: found at least one WCH-LinkE in ARM programming mode. Attempting automatic switch to RISC-V. Will need a to re-attempt.\n" );
|
||||
fprintf( stderr, "For more information, you may need to change it to RISC-V mode as per https://github.com/cnlohr/ch32v003fun/issues/227\n" );
|
||||
|
||||
// Just in case we got stuck in IAP mode, try sending 0x83 to eject.
|
||||
libusb_device_handle * devh = 0;
|
||||
status = libusb_open( found_arm_programmer, &devh );
|
||||
if( status )
|
||||
{
|
||||
fprintf( stderr, "Found programmer in ARM mode, but couldn't open it.\n" );
|
||||
exit( -10 );
|
||||
}
|
||||
|
||||
// https://github.com/wagiminator/MCU-Flash-Tools/blob/main/rvmode.py
|
||||
uint8_t rbuff[4] = { 0x81, 0xff, 0x01, 0x52 };
|
||||
int transferred = 0;
|
||||
libusb_bulk_transfer( devh, 0x02, rbuff, 4, &transferred, 1 );
|
||||
fprintf( stderr, "RISC-V command sent (%d)\n", transferred );
|
||||
exit( -3 );
|
||||
}
|
||||
|
||||
|
||||
if( found_programmer_in_iap )
|
||||
{
|
||||
// Just in case we got stuck in IAP mode, try sending 0x83 to eject.
|
||||
fprintf( stderr, "Found programmer in IAP mode. Attempting to eject it out of IAP.\n" );
|
||||
libusb_device_handle * devh = 0;
|
||||
status = libusb_open( found_programmer_in_iap, &devh );
|
||||
if( status )
|
||||
{
|
||||
fprintf( stderr, "Found programmer in IAP mode, but couldn't open it.\n" );
|
||||
exit( -10 );
|
||||
}
|
||||
uint8_t rbuff[4];
|
||||
int transferred = 0;
|
||||
rbuff[0] = 0x83;
|
||||
libusb_bulk_transfer( devh, 0x02, rbuff, 1, &transferred, 1 );
|
||||
fprintf( stderr, "Eject command sent (%d)\n", transferred );
|
||||
exit( -3 );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// DMI_OP decyphered From https://github.com/karlp/openocd-hacks/blob/27af153d4a373f29ad93dab28a01baffb7894363/src/jtag/drivers/wlink.c
|
||||
// Thanks, CW2 for pointing this out. See DMI_OP for more info.
|
||||
int LEWriteReg32( void * dev, uint8_t reg_7_bit, uint32_t command )
|
||||
{
|
||||
libusb_device_handle * devh = ((struct LinkEProgrammerStruct*)dev)->devh;
|
||||
|
||||
const uint8_t iOP = 2; // op 2 = write
|
||||
uint8_t req[] = {
|
||||
0x81, 0x08, 0x06, reg_7_bit,
|
||||
(command >> 24) & 0xff,
|
||||
(command >> 16) & 0xff,
|
||||
(command >> 8) & 0xff,
|
||||
(command >> 0) & 0xff,
|
||||
iOP };
|
||||
|
||||
uint8_t resp[128];
|
||||
int resplen;
|
||||
wch_link_command( devh, req, sizeof(req), &resplen, resp, sizeof(resp) );
|
||||
if( resplen != 9 || resp[8] == 0x02 || resp[8] == 0x03 ) //|| resp[3] != reg_7_bit )
|
||||
{
|
||||
fprintf( stderr, "Error setting write reg. Tell cnlohr. Maybe we should allow retries here?\n" );
|
||||
fprintf( stderr, "RR: %d :", resplen );
|
||||
int i;
|
||||
for( i = 0; i < resplen; i++ )
|
||||
{
|
||||
fprintf( stderr, "%02x ", resp[i] );
|
||||
}
|
||||
fprintf( stderr, "\n" );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LEReadReg32( void * dev, uint8_t reg_7_bit, uint32_t * commandresp )
|
||||
{
|
||||
libusb_device_handle * devh = ((struct LinkEProgrammerStruct*)dev)->devh;
|
||||
const uint8_t iOP = 1; // op 1 = read
|
||||
uint32_t transferred;
|
||||
uint8_t rbuff[128] = { 0 };
|
||||
uint8_t req[] = {
|
||||
0x81, 0x08, 0x06, reg_7_bit,
|
||||
0, 0, 0, 0,
|
||||
iOP };
|
||||
wch_link_command( devh, req, sizeof( req ), (int*)&transferred, rbuff, sizeof( rbuff ) );
|
||||
*commandresp = ( rbuff[4]<<24 ) | (rbuff[5]<<16) | (rbuff[6]<<8) | (rbuff[7]<<0);
|
||||
if( transferred != 9 || rbuff[8] == 0x02 || rbuff[8] == 0x03 ) //|| rbuff[3] != reg_7_bit )
|
||||
{
|
||||
fprintf( stderr, "Error setting write reg. Tell cnlohr. Maybe we should allow retries here?\n" );
|
||||
fprintf( stderr, "RR: %d :", transferred );
|
||||
int i;
|
||||
for( i = 0; i < transferred; i++ )
|
||||
{
|
||||
fprintf( stderr, "%02x ", rbuff[i] );
|
||||
}
|
||||
fprintf( stderr, "\n" );
|
||||
}
|
||||
/*
|
||||
printf( "RR: %d :", transferred );
|
||||
int i;
|
||||
for( i = 0; i < transferred; i++ )
|
||||
{
|
||||
printf( "%02x ", rbuff[i] );
|
||||
}
|
||||
printf( "\n" );
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LEFlushLLCommands( void * dev )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LESetupInterface( void * d )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)d)->internal);
|
||||
uint8_t rbuff[1024];
|
||||
uint32_t transferred = 0;
|
||||
|
||||
// This puts the processor on hold to allow the debugger to run.
|
||||
wch_link_command( dev, "\x81\x0d\x01\x03", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
|
||||
|
||||
// Place part into reset.
|
||||
wch_link_command( dev, "\x81\x0d\x01\x01", 4, (int*)&transferred, rbuff, 1024 ); // Reply is: "\x82\x0d\x04\x02\x08\x02\x00"
|
||||
switch(rbuff[5]) {
|
||||
case 1:
|
||||
fprintf(stderr, "WCH Programmer is CH549 version %d.%d\n",rbuff[3], rbuff[4]);
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "WCH Programmer is CH32V307 version %d.%d\n",rbuff[3], rbuff[4]);
|
||||
break;
|
||||
case 3:
|
||||
fprintf(stderr, "WCH Programmer is CH32V203 version %d.%d\n",rbuff[3], rbuff[4]);
|
||||
break;
|
||||
case 4:
|
||||
fprintf(stderr, "WCH Programmer is LinkB version %d.%d\n",rbuff[3], rbuff[4]);
|
||||
break;
|
||||
case 18:
|
||||
fprintf(stderr, "WCH Programmer is LinkE version %d.%d\n",rbuff[3], rbuff[4]);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown WCH Programmer %02x (Ver %d.%d)\n", rbuff[5], rbuff[3], rbuff[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: What in the world is this? It doesn't appear to be needed.
|
||||
wch_link_command( dev, "\x81\x0c\x02\x09\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
|
||||
|
||||
// Note from further debugging:
|
||||
// My capture differs in this case: \x05 instead of \x09 -> But does not seem to be needed
|
||||
//wch_link_command( dev, "\x81\x0c\x02\x05\x01", 5, 0, 0, 0 ); //Reply is: 820c0101
|
||||
|
||||
// This puts the processor on hold to allow the debugger to run.
|
||||
int already_tried_reset = 0;
|
||||
do
|
||||
{
|
||||
wch_link_command( dev, "\x81\x0d\x01\x02", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
|
||||
if (rbuff[0] == 0x81 && rbuff[1] == 0x55 && rbuff[2] == 0x01 ) // && rbuff[3] == 0x01 )
|
||||
{
|
||||
// The following code may try to execute a few times to get the processor to actually reset.
|
||||
// This code could likely be much better.
|
||||
|
||||
fprintf(stderr, "link error, nothing connected to linker (%d = [%02x %02x %02x %02x]). Trying to put processor in hold and retrying.\n", transferred, rbuff[0], rbuff[1], rbuff[2], rbuff[3]);
|
||||
|
||||
// Give up if too long
|
||||
if( already_tried_reset > 10 )
|
||||
return -1;
|
||||
|
||||
wch_link_multicommands( (libusb_device_handle *)dev, 1, 4, "\x81\x0d\x01\x13" ); // Try forcing reset line low.
|
||||
wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\xff", 4, 0, 0, 0); //Exit programming
|
||||
|
||||
if( already_tried_reset > 3 )
|
||||
{
|
||||
MCF.DelayUS( iss, 5000 );
|
||||
wch_link_command( dev, "\x81\x0d\x01\x03", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
|
||||
}
|
||||
else
|
||||
{
|
||||
MCF.DelayUS( iss, 5000 );
|
||||
}
|
||||
|
||||
wch_link_multicommands( (libusb_device_handle *)dev, 1, 4, "\x81\x0d\x01\x14" ); // Release reset line.
|
||||
wch_link_multicommands( (libusb_device_handle *)dev, 3, 4, "\x81\x0b\x01\x01", 4, "\x81\x0d\x01\x02", 4, "\x81\x0d\x01\xff" );
|
||||
already_tried_reset++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while( 1 );
|
||||
|
||||
if(rbuff[3] == 0x08 || rbuff[3] > 0x09) {
|
||||
fprintf( stderr, "Chip Type unknown. Aborting...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum RiscVChip chip = (enum RiscVChip)rbuff[3];
|
||||
printChipInfo(chip);
|
||||
|
||||
int result = checkChip(chip);
|
||||
if( result == 1 ) // Using blob write
|
||||
{
|
||||
fprintf( stderr, "Using binary blob write for operation.\n" );
|
||||
MCF.WriteBinaryBlob = LEWriteBinaryBlob;
|
||||
|
||||
iss->sector_size = 256;
|
||||
|
||||
wch_link_command( dev, "\x81\x0d\x01\x03", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Ignored, 820d050900300500
|
||||
} else if( result < 0 ) {
|
||||
fprintf( stderr, "Chip type not supported. Aborting...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
iss->target_chip_type = chip;
|
||||
|
||||
// For some reason, if we don't do this sometimes the programmer starts in a hosey mode.
|
||||
MCF.WriteReg32( d, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
||||
MCF.WriteReg32( d, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
||||
MCF.WriteReg32( d, DMCONTROL, 0x80000001 ); // No, really make sure.
|
||||
MCF.WriteReg32( d, DMABSTRACTCS, 0x00000700 ); // Ignore any pending errors.
|
||||
MCF.WriteReg32( d, DMABSTRACTAUTO, 0 );
|
||||
MCF.WriteReg32( d, DMCOMMAND, 0x00221000 ); // Read x0 (Null command) with nopostexec (to fix v307 read issues)
|
||||
|
||||
int r = 0;
|
||||
|
||||
r |= MCF.WaitForDoneOp( d, 0 );
|
||||
if( r )
|
||||
{
|
||||
fprintf( stderr, "Fault on setup\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Setup success\n" );
|
||||
}
|
||||
|
||||
// This puts the processor on hold to allow the debugger to run.
|
||||
// Recommended to switch to 05 from 09 by Alexander M
|
||||
// wch_link_command( dev, "\x81\x11\x01\x09", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
|
||||
wch_link_command( dev, "\x81\x11\x01\x05", 4, (int*)&transferred, rbuff, 1024 ); // Reply: Chip ID + Other data (see below)
|
||||
|
||||
if( transferred != 20 )
|
||||
{
|
||||
fprintf( stderr, "Error: could not get part status\n" );
|
||||
return -1;
|
||||
}
|
||||
const uint32_t flskb = ((uint32_t)rbuff[2]<<8) | rbuff[3];
|
||||
fprintf( stderr, "Flash Storage: %d kB\n", flskb ); // 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] );
|
||||
|
||||
// Check for read protection
|
||||
wch_link_command( dev, "\x81\x06\x01\x01", 4, (int*)&transferred, rbuff, 1024 );
|
||||
if(transferred != 4) {
|
||||
fprintf(stderr, "Error: could not get read protection status\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(rbuff[3] == 0x01) {
|
||||
fprintf(stderr, "Read protection: enabled\n");
|
||||
} else {
|
||||
fprintf(stderr, "Read protection: disabled\n");
|
||||
}
|
||||
|
||||
iss->flash_size = flskb * 0x400u;
|
||||
if (chip == CHIP_CH32V20x) { // diferent version chip
|
||||
if (flskb > 32u) iss->ram_size = 20u * 0x400u; // 64K
|
||||
else iss->ram_size = 10u * 0x400u; // 32K
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LEControl3v3( void * d, int bOn )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
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\x0a", 4, 0, 0, 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LEControl5v( void * d, int bOn )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
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 * d )
|
||||
{
|
||||
printf( "Sending unbrick\n" );
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\x0f\x09", 5, 0, 0, 0 );
|
||||
printf( "Done unbrick\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int LEConfigureNRSTAsGPIO( void * d, int one_if_yes_gpio )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
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" );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LEConfigureReadProtection( void * d, int one_if_yes_protect )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
if( one_if_yes_protect )
|
||||
{
|
||||
wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x03\xf7\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" );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LEExit( void * d )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
wch_link_command( (libusb_device_handle *)dev, "\x81\x0d\x01\xff", 4, 0, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * TryInit_WCHLinkE()
|
||||
{
|
||||
libusb_device_handle * wch_linke_devh;
|
||||
wch_linke_devh = wch_link_base_setup(0);
|
||||
if( !wch_linke_devh ) return 0;
|
||||
|
||||
struct LinkEProgrammerStruct * ret = malloc( sizeof( struct LinkEProgrammerStruct ) );
|
||||
memset( ret, 0, sizeof( *ret ) );
|
||||
ret->devh = wch_linke_devh;
|
||||
ret->lasthaltmode = 0;
|
||||
|
||||
MCF.ReadReg32 = LEReadReg32;
|
||||
MCF.WriteReg32 = LEWriteReg32;
|
||||
MCF.FlushLLCommands = LEFlushLLCommands;
|
||||
|
||||
MCF.SetupInterface = LESetupInterface;
|
||||
MCF.Control3v3 = LEControl3v3;
|
||||
MCF.Control5v = LEControl5v;
|
||||
MCF.Unbrick = LEUnbrick;
|
||||
MCF.ConfigureNRSTAsGPIO = LEConfigureNRSTAsGPIO;
|
||||
MCF.ConfigureReadProtection = LEConfigureReadProtection;
|
||||
|
||||
MCF.Exit = LEExit;
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
// Flash Bootloader for V20x and V30x series MCUs
|
||||
|
||||
const uint8_t * bootloader_v1 = (const uint8_t*)
|
||||
"\x93\x77\x15\x00\x41\x11\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\x38\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\x1F\x07\x10" \
|
||||
"\x98\x4B\x6D\x9B\x98\xCB\x93\x77\x45\x00\xA9\xCB\x93\x07\xF6\x07" \
|
||||
"\x9D\x83\x2E\xC0\x2D\x68\x81\x76\x3E\xC4\xB7\x08\x02\x00\xB7\x27" \
|
||||
"\x02\x40\x37\x33\x00\x40\x13\x08\xA8\xAA\xFD\x16\x98\x4B\x33\x67" \
|
||||
"\x17\x01\x98\xCB\x02\x47\xD8\xCB\x98\x4B\x13\x67\x07\x04\x98\xCB" \
|
||||
"\xD8\x47\x05\x8B\x71\xEF\x98\x4B\x75\x8F\x98\xCB\x02\x47\x13\x07" \
|
||||
"\x07\x08\x3A\xC0\x22\x47\x7D\x17\x3A\xC4\x69\xFB\x93\x77\x85\x00" \
|
||||
"\xED\xC3\x93\x07\xF6\x07\x2E\xC0\x9D\x83\x37\x27\x02\x40\x3E\xC4" \
|
||||
"\x1C\x4B\xC1\x66\x37\x08\x08\x00\xD5\x8F\x1C\xCB\xA1\x48\x37\x17" \
|
||||
"\x00\x20\xB7\x27\x02\x40\x37\x03\x04\x00\x94\x4B\xB3\xE6\x06\x01" \
|
||||
"\x94\xCB\xD4\x47\x85\x8A\xF5\xFE\x82\x46\x3A\x8E\x36\xC2\x46\xC6" \
|
||||
"\x92\x46\x83\x2E\x07\x00\x41\x07\x23\xA0\xD6\x01\x92\x46\x83\x2E" \
|
||||
"\x47\xFF\x23\xA2\xD6\x01\x92\x46\x83\x2E\x87\xFF\x23\xA4\xD6\x01" \
|
||||
"\x92\x46\x03\x2E\xCE\x00\x23\xA6\xC6\x01\x94\x4B\xB3\xE6\x66\x00" \
|
||||
"\x94\xCB\xD4\x47\x85\x8A\xF5\xFE\x92\x46\x3A\x8E\xC1\x06\x36\xC2" \
|
||||
"\xB2\x46\xFD\x16\x36\xC6\xCD\xFE\x82\x46\xD4\xCB\x94\x4B\x93\xE6" \
|
||||
"\x06\x04\x94\xCB\xD4\x47\x85\x8A\xF5\xFE\xD4\x47\xD1\x8A\x85\xC6" \
|
||||
"\xD8\x47\xB7\x06\xF3\xFF\xFD\x16\x13\x67\x47\x01\xD8\xC7\x98\x4B" \
|
||||
"\x21\x45\x75\x8F\x98\xCB\x41\x01\x02\x90\x23\x20\xD8\x00\xE9\xBD" \
|
||||
"\x23\x20\x03\x01\x31\xBF\x82\x46\x93\x86\x06\x08\x36\xC0\xA2\x46" \
|
||||
"\xFD\x16\x36\xC4\xB9\xFA\x98\x4B\xB7\x06\xF3\xFF\xFD\x16\x75\x8F" \
|
||||
"\x98\xCB\x41\x89\x15\xC9\x2E\xC0\x0D\x06\x02\xC4\x09\x82\x32\xC6" \
|
||||
"\xB7\x17\x00\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\x71\xBF\x41\x45\x61\xBF" \
|
||||
"\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\xff\xff";
|
||||
|
||||
const uint8_t * bootloader_v2 = (const uint8_t*)
|
||||
"\x93\x77\x15\x00\x41\x11\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\x95\xc7\xb7\x27\x02\x40\x98\x4b" \
|
||||
"\xad\x66\x37\x38\x00\x40\x13\x67\x47\x00\x98\xcb\x98\x4b\x93\x86" \
|
||||
"\xa6\xaa\x13\x67\x07\x04\x98\xcb\xd8\x47\x05\x8b\x61\xeb\x98\x4b" \
|
||||
"\x6d\x9b\x98\xcb\x93\x77\x45\x00\xa9\xcb\x93\x07\xf6\x0f\xa1\x83" \
|
||||
"\x2e\xc0\x2d\x68\x81\x76\x3e\xc4\xb7\x08\x02\x00\xb7\x27\x02\x40" \
|
||||
"\x37\x33\x00\x40\x13\x08\xa8\xaa\xfd\x16\x98\x4b\x33\x67\x17\x01" \
|
||||
"\x98\xcb\x02\x47\xd8\xcb\x98\x4b\x13\x67\x07\x04\x98\xcb\xd8\x47" \
|
||||
"\x05\x8b\x41\xeb\x98\x4b\x75\x8f\x98\xcb\x02\x47\x13\x07\x07\x10" \
|
||||
"\x3a\xc0\x22\x47\x7d\x17\x3a\xc4\x69\xfb\x93\x77\x85\x00\xd5\xcb" \
|
||||
"\x93\x07\xf6\x0f\x2e\xc0\xa1\x83\x3e\xc4\x37\x27\x02\x40\x1c\x4b" \
|
||||
"\xc1\x66\x41\x68\xd5\x8f\x1c\xcb\xb7\x16\x00\x20\xb7\x27\x02\x40" \
|
||||
"\x93\x08\x00\x04\x37\x03\x20\x00\x98\x4b\x33\x67\x07\x01\x98\xcb" \
|
||||
"\xd8\x47\x05\x8b\x75\xff\x02\x47\x3a\xc2\x46\xc6\x32\x47\x0d\xef" \
|
||||
"\x98\x4b\x33\x67\x67\x00\x98\xcb\xd8\x47\x05\x8b\x75\xff\xd8\x47" \
|
||||
"\x41\x8b\x39\xc3\xd8\x47\xc1\x76\xfd\x16\x13\x67\x07\x01\xd8\xc7" \
|
||||
"\x98\x4b\x21\x45\x75\x8f\x98\xcb\x41\x01\x02\x90\x23\x20\xd8\x00" \
|
||||
"\x25\xb7\x23\x20\x03\x01\xa5\xb7\x12\x47\x13\x8e\x46\x00\x94\x42" \
|
||||
"\x14\xc3\x12\x47\x11\x07\x3a\xc2\x32\x47\x7d\x17\x3a\xc6\xd8\x47" \
|
||||
"\x09\x8b\x75\xff\xf2\x86\x5d\xb7\x02\x47\x13\x07\x07\x10\x3a\xc0" \
|
||||
"\x22\x47\x7d\x17\x3a\xc4\x49\xf3\x98\x4b\xc1\x76\xfd\x16\x75\x8f" \
|
||||
"\x98\xcb\x41\x89\x15\xc9\x2e\xc0\x0d\x06\x02\xc4\x09\x82\x32\xc6" \
|
||||
"\xb7\x17\x00\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\xbd\xbf\x41\x45\xad\xbf" \
|
||||
"\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\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\xff\xff\xff\xff";
|
||||
|
||||
int bootloader_len = 512;
|
||||
#endif
|
||||
|
||||
static const uint8_t * GetFlashLoader( enum RiscVChip chip )
|
||||
{
|
||||
switch(chip) {
|
||||
case CHIP_CH32V10x:
|
||||
return bootloader_v1;
|
||||
case CHIP_CH32V20x:
|
||||
case CHIP_CH32V30x:
|
||||
default:
|
||||
return bootloader_v2;
|
||||
}
|
||||
}
|
||||
|
||||
static int InternalLinkEHaltMode( void * d, int mode )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
if( mode == ((struct LinkEProgrammerStruct*)d)->lasthaltmode )
|
||||
return 0;
|
||||
((struct LinkEProgrammerStruct*)d)->lasthaltmode = mode;
|
||||
|
||||
if( mode == 0 )
|
||||
{
|
||||
printf( "Holding in reset\n" );
|
||||
// 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 if( mode == 1 )
|
||||
{
|
||||
// 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" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return -999;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int LEReadBinaryBlob( void * d, uint32_t offset, uint32_t amount, uint8_t * readbuff )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
|
||||
InternalLinkEHaltMode( d, 0 );
|
||||
|
||||
int i;
|
||||
int status;
|
||||
uint8_t rbuff[1024];
|
||||
int transferred = 0;
|
||||
int readbuffplace = 0;
|
||||
|
||||
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 = ((uint32_t*)readbuff)[i];
|
||||
((uint32_t*)readbuff)[i] = (r>>24) | ((r & 0xff0000) >> 8) | ((r & 0xff00)<<8) | (( r & 0xff )<<24);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int LEWriteBinaryBlob( void * d, uint32_t address_to_write, uint32_t len, uint8_t * blob )
|
||||
{
|
||||
libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh;
|
||||
struct InternalState * iss = (struct InternalState*)(((struct LinkEProgrammerStruct*)d)->internal);
|
||||
|
||||
InternalLinkEHaltMode( d, 0 );
|
||||
|
||||
int i;
|
||||
int status;
|
||||
uint8_t rbuff[1024];
|
||||
int transferred;
|
||||
|
||||
int padlen = ((len-1) & (~(iss->sector_size-1))) + iss->sector_size;
|
||||
|
||||
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,
|
||||
// Address to write
|
||||
(uint8_t)(address_to_write >> 24), (uint8_t)(address_to_write >> 16),
|
||||
(uint8_t)(address_to_write >> 8), (uint8_t)(address_to_write & 0xff),
|
||||
// Length to write
|
||||
(uint8_t)(len >> 24), (uint8_t)(len >> 16),
|
||||
(uint8_t)(len >> 8), (uint8_t)(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 );
|
||||
|
||||
const uint8_t *bootloader = GetFlashLoader(iss->target_chip_type);
|
||||
|
||||
int pplace = 0;
|
||||
for( pplace = 0; pplace < bootloader_len; pplace += iss->sector_size )
|
||||
{
|
||||
WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, (uint8_t*)(bootloader+pplace), iss->sector_size, &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 responses 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 += iss->sector_size )
|
||||
{
|
||||
if( pplace + iss->sector_size > len )
|
||||
{
|
||||
uint8_t paddeddata[iss->sector_size];
|
||||
int gap = pplace + iss->sector_size - len;
|
||||
int okcopy = len - pplace;
|
||||
memcpy( paddeddata, blob + pplace, okcopy );
|
||||
memset( paddeddata + okcopy, 0xff, gap );
|
||||
WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, paddeddata, iss->sector_size, &transferred, WCHTIMEOUT ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
WCHCHECK( libusb_bulk_transfer( (libusb_device_handle *)dev, 0x02, blob+pplace, iss->sector_size, &transferred, WCHTIMEOUT ) );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
173
minichlink/serial_dev.c
Normal file
173
minichlink/serial_dev.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include "serial_dev.h"
|
||||
|
||||
int serial_dev_create(serial_dev_t *dev, const char* port, unsigned baud) {
|
||||
if (!dev)
|
||||
return -1;
|
||||
dev->port = port;
|
||||
dev->baud = baud;
|
||||
#ifdef IS_WINDOWS
|
||||
dev->handle = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dev->fd = -1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial_dev_open(serial_dev_t *dev) {
|
||||
fprintf(stderr, "Opening serial port %s at %u baud.\n", dev->port, dev->baud);
|
||||
#ifdef IS_WINDOWS
|
||||
// Windows quirk: port = "COM10" is invalid, has to be encoded as "\\.\COM10".
|
||||
// This also works for COM below 9. So, let's give the user the ability to use
|
||||
// any "COMx" string and just prepend the "\\.\".
|
||||
char winPortName[64];
|
||||
if(dev->port[0] != '\\') {
|
||||
snprintf(winPortName, sizeof(winPortName), "\\\\.\\%s", dev->port);
|
||||
} else {
|
||||
// copy verbatim if string already starts with a '\'
|
||||
snprintf(winPortName, sizeof(winPortName), "%s", dev->port);
|
||||
}
|
||||
dev->handle = CreateFileA(winPortName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0);
|
||||
if (dev->handle == INVALID_HANDLE_VALUE) {
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
fprintf(stderr, "Serial port %s not found.\n", dev->port);
|
||||
// weird: without this, errno = 0 (no error).
|
||||
_set_errno(ERROR_FILE_NOT_FOUND);
|
||||
return -1; // Device not found
|
||||
}
|
||||
// Error while opening the device
|
||||
return -1;
|
||||
}
|
||||
DCB dcbSerialParams;
|
||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||
if (!GetCommState(dev->handle, &dcbSerialParams)) {
|
||||
return -1;
|
||||
}
|
||||
// set baud and 8N1 serial formatting
|
||||
dcbSerialParams.BaudRate = dev->baud;
|
||||
dcbSerialParams.ByteSize = 8;
|
||||
dcbSerialParams.StopBits = ONESTOPBIT;
|
||||
dcbSerialParams.Parity = NOPARITY;
|
||||
// write back
|
||||
if (!SetCommState(dev->handle, &dcbSerialParams)){
|
||||
return -1;
|
||||
}
|
||||
// Set the timeout parameters to "no timeout" (blocking).
|
||||
// see https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts
|
||||
COMMTIMEOUTS timeouts;
|
||||
timeouts.ReadIntervalTimeout = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = MAXDWORD;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
timeouts.WriteTotalTimeoutConstant = MAXDWORD;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
// Write the parameters
|
||||
if (!SetCommTimeouts(dev->handle, &timeouts)) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
struct termios attr;
|
||||
if ((dev->fd = open(dev->port, O_RDWR | O_NOCTTY)) == -1) {
|
||||
perror("open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcgetattr(dev->fd, &attr) == -1) {
|
||||
perror("tcgetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfmakeraw(&attr);
|
||||
cfsetspeed(&attr, dev->baud);
|
||||
|
||||
if (tcsetattr(dev->fd, TCSANOW, &attr) == -1) {
|
||||
perror("tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
// all okay if we get here
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial_dev_write(serial_dev_t *dev, const void* data, size_t len) {
|
||||
#ifdef IS_WINDOWS
|
||||
DWORD dwBytesWritten;
|
||||
if (!WriteFile(dev->handle, data, len, &dwBytesWritten,NULL)) {
|
||||
return -1;
|
||||
}
|
||||
return (int) dwBytesWritten;
|
||||
#else
|
||||
return write(dev->fd, data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int serial_dev_read(serial_dev_t *dev, void* data, size_t len) {
|
||||
#ifdef IS_WINDOWS
|
||||
DWORD dwBytesRead = 0;
|
||||
if (!ReadFile(dev->handle, data, len, &dwBytesRead, NULL)) {
|
||||
return -1;
|
||||
}
|
||||
return (int) dwBytesRead;
|
||||
#else
|
||||
return read(dev->fd, data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int serial_dev_do_dtr_reset(serial_dev_t *dev) {
|
||||
#ifdef IS_WINDOWS
|
||||
// EscapeCommFunction returns 0 on fail
|
||||
if(EscapeCommFunction(dev->handle, SETDTR) == 0) {
|
||||
return -1;
|
||||
}
|
||||
if(EscapeCommFunction(dev->handle, CLRDTR) == 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int argp = TIOCM_DTR;
|
||||
// Arduino DTR reset.
|
||||
if (ioctl(dev->fd, TIOCMBIC, &argp) == -1) {
|
||||
perror("ioctl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcdrain(dev->fd) == -1) {
|
||||
perror("tcdrain");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(dev->fd, TIOCMBIS, &argp) == -1) {
|
||||
perror("ioctl");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial_dev_flush_rx(serial_dev_t *dev) {
|
||||
#ifdef IS_WINDOWS
|
||||
// PurgeComm returns 0 on fail
|
||||
if (PurgeComm(dev->handle, PURGE_RXCLEAR) == 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if (tcflush(dev->fd, TCIFLUSH) == -1) {
|
||||
perror("tcflush");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial_dev_close(serial_dev_t *dev) {
|
||||
#ifdef IS_WINDOWS
|
||||
if(!CloseHandle(dev->handle)) {
|
||||
return -1;
|
||||
}
|
||||
dev->handle = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
int ret = 0;
|
||||
if((ret = close(dev->fd)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
dev->fd = -1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
48
minichlink/serial_dev.h
Normal file
48
minichlink/serial_dev.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef _SERIAL_DEV_H
|
||||
#define _SERIAL_DEV_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define IS_WINDOWS
|
||||
#define DEFAULT_SERIAL_NAME "\\\\.\\COM3"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#define IS_POSIX
|
||||
#define DEFAULT_SERIAL_NAME "/dev/ttyACM0"
|
||||
#endif
|
||||
/* these are available on all platforms */
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
const char* port;
|
||||
unsigned baud;
|
||||
#ifdef IS_WINDOWS
|
||||
HANDLE handle;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
} serial_dev_t;
|
||||
|
||||
/* returns 0 if OK */
|
||||
int serial_dev_create(serial_dev_t *dev, const char* port, unsigned baud);
|
||||
/* returns 0 if OK */
|
||||
int serial_dev_open(serial_dev_t *dev);
|
||||
/* returns -1 on write error */
|
||||
int serial_dev_write(serial_dev_t *dev, const void* data, size_t len);
|
||||
/* returns -1 on read error */
|
||||
int serial_dev_read(serial_dev_t *dev, void* data, size_t len);
|
||||
/* returns -1 on reset error */
|
||||
int serial_dev_do_dtr_reset(serial_dev_t *dev);
|
||||
/* returns -1 on flush error */
|
||||
int serial_dev_flush_rx(serial_dev_t *dev);
|
||||
/* returns -1 on close error */
|
||||
int serial_dev_close(serial_dev_t *dev);
|
||||
|
||||
#endif
|
162
minichlink/terminalhelp.h
Normal file
162
minichlink/terminalhelp.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
// terminalhelp from mini-rv32ima.
|
||||
#ifndef _TERMINALHELP_H
|
||||
#define _TERMINALHELP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Provides the following:
|
||||
static void CaptureKeyboardInput() __attribute__((used));
|
||||
static void ResetKeyboardInput() __attribute__((used));
|
||||
static uint64_t GetTimeMicroseconds() __attribute__((used));
|
||||
static int ReadKBByte() __attribute__((used));
|
||||
static int IsKBHit() __attribute__((used));
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
#define strtoll _strtoi64
|
||||
|
||||
static void CaptureKeyboardInput()
|
||||
{
|
||||
system(""); // Poorly documented tick: Enable VT100 Windows mode.
|
||||
}
|
||||
|
||||
static void ResetKeyboardInput()
|
||||
{
|
||||
}
|
||||
|
||||
static uint64_t GetTimeMicroseconds()
|
||||
{
|
||||
static LARGE_INTEGER lpf;
|
||||
LARGE_INTEGER li;
|
||||
|
||||
if( !lpf.QuadPart )
|
||||
QueryPerformanceFrequency( &lpf );
|
||||
|
||||
QueryPerformanceCounter( &li );
|
||||
return ((uint64_t)li.QuadPart * 1000000LL) / (uint64_t)lpf.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
static int IsKBHit()
|
||||
{
|
||||
return _kbhit();
|
||||
}
|
||||
|
||||
static int ReadKBByte()
|
||||
{
|
||||
// This code is kind of tricky, but used to convert windows arrow keys
|
||||
// to VT100 arrow keys.
|
||||
static int is_escape_sequence = 0;
|
||||
int r;
|
||||
if( is_escape_sequence == 1 )
|
||||
{
|
||||
is_escape_sequence++;
|
||||
return '[';
|
||||
}
|
||||
|
||||
r = _getch();
|
||||
|
||||
if( is_escape_sequence )
|
||||
{
|
||||
is_escape_sequence = 0;
|
||||
switch( r )
|
||||
{
|
||||
case 'H': return 'A'; // Up
|
||||
case 'P': return 'B'; // Down
|
||||
case 'K': return 'D'; // Left
|
||||
case 'M': return 'C'; // Right
|
||||
case 'G': return 'H'; // Home
|
||||
case 'O': return 'F'; // End
|
||||
default: return r; // Unknown code.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( r )
|
||||
{
|
||||
case 13: return 10; //cr->lf
|
||||
case 224: is_escape_sequence = 1; return 27; // Escape arrow keys
|
||||
default: return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#undef BS0
|
||||
#undef BS1
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static void CtrlC()
|
||||
{
|
||||
fprintf( stderr, "Minichlink Closing\n" );
|
||||
ResetKeyboardInput();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
// Override keyboard, so we can capture all keyboard input for the VM.
|
||||
static void CaptureKeyboardInput()
|
||||
{
|
||||
// Hook exit, because we want to re-enable keyboard.
|
||||
atexit(ResetKeyboardInput);
|
||||
signal(SIGINT, CtrlC);
|
||||
signal(SIGPIPE, CtrlC);
|
||||
|
||||
struct termios term;
|
||||
tcgetattr(0, &term);
|
||||
term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
|
||||
tcsetattr(0, TCSANOW, &term);
|
||||
}
|
||||
|
||||
static void ResetKeyboardInput()
|
||||
{
|
||||
// Re-enable echo, etc. on keyboard.
|
||||
struct termios term;
|
||||
tcgetattr(0, &term);
|
||||
term.c_lflag |= ICANON | ECHO;
|
||||
tcsetattr(0, TCSANOW, &term);
|
||||
}
|
||||
|
||||
static uint64_t GetTimeMicroseconds()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday( &tv, 0 );
|
||||
return tv.tv_usec + ((uint64_t)(tv.tv_sec)) * 1000000LL;
|
||||
}
|
||||
|
||||
static int is_eofd;
|
||||
|
||||
static int ReadKBByte()
|
||||
{
|
||||
if( is_eofd ) return 0xffffffff;
|
||||
char rxchar = 0;
|
||||
int rread = read(fileno(stdin), (char*)&rxchar, 1);
|
||||
|
||||
if( rread > 0 ) // Tricky: getchar can't be used with arrow keys.
|
||||
return rxchar;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int IsKBHit()
|
||||
{
|
||||
if( is_eofd ) return -1;
|
||||
int byteswaiting;
|
||||
ioctl(0, FIONREAD, &byteswaiting);
|
||||
if( !byteswaiting && write( fileno(stdin), 0, 0 ) != 0 ) { is_eofd = 1; return -1; } // Is end-of-file for
|
||||
return !!byteswaiting;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Add table
Reference in a new issue