diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddb029a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +gitignore.txt +.gitignore +*.o +*.1 +*~ +wch-isp +test.sh +mingw32 diff --git a/50-wchisp.rules b/50-wchisp.rules index 9e88112..5b9da50 100644 --- a/50-wchisp.rules +++ b/50-wchisp.rules @@ -1,2 +1,2 @@ # WCH ISP bootloader -SUBSYSTEMS=="usb", ATTRS{idVendor}=="4348", ATTRS{idProduct}=="55e0", GROUP="uucp", MODE="0666" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="4348", ATTRS{idProduct}=="55e0", GROUP="uucp", MODE="0660" diff --git a/Makefile b/Makefile index 9c3e6b4..a95518b 100644 --- a/Makefile +++ b/Makefile @@ -1,58 +1,70 @@ # SPDX-License-Identifier: GPL-2.0-only -VERSION = 0.3.0 +VERSION = 0.1.1 +NAME := wch-isp # Install paths PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man -UDEVPREFIX = /etc/udev +UDEVPREFIX = $(PREFIX)/lib/udev +#make CROSS_COMPILE=i686-w64-mingw32- INCS="-Imingw32/include -Imingw32/include/libusb-1.0" LIBS="-Lmingw32/lib mingw32/bin/libusb-1.0.dll -lyaml" ifneq ($(CROSS_COMPILE),) -CC = $(CROSS_COMPILE)cc +CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld endif +#CFLAGS += -DPREFIX=\"$(PREFIX)\" -DNAME=\"$(NAME)\" -g PKG_CONFIG = pkg-config +ifneq ($(OPTIONS),small) # include and libs -INCS = `$(PKG_CONFIG) --cflags libusb-1.0` -LIBS = `$(PKG_CONFIG) --libs libusb-1.0` +INCS += `$(PKG_CONFIG) --cflags libusb-1.0` `$(PKG_CONFIG) --cflags yaml-0.1` +LIBS += `$(PKG_CONFIG) --libs libusb-1.0` `$(PKG_CONFIG) --libs yaml-0.1` +else +# include and libs +INCS += -DBUILD_SMALL +LIBS += +endif # Flags WCHISP_CPPFLAGS = -DVERSION=\"$(VERSION)\" $(CPPFLAGS) WCHISP_CFLAGS = -Wall -O2 $(INCS) $(CFLAGS) WCHISP_LDFLAGS = $(LIBS) $(LDFLAGS) -SRC = wch-isp.c +SRC = main.c wch_if_usb.c wch_if_uart.c wch_yaml_parse.c +#SRC = $(NAME).c HDR = arg.h devices.h OBJ = $(SRC:.c=.o) -BIN = wch-isp -MAN = wch-isp.1 -DISTFILES = $(SRC) $(HDR) $(MAN) 50-wchisp.rules Makefile +BIN = $(NAME) +MAN = $(NAME).1 +DISTFILES = $(SRC) $(HDR) *.man 50-wchisp.rules Makefile + -all: $(BIN) +all: $(BIN) $(MAN) -$(OBJ): arg.h devices.h +%.1: %.man + sed "s/VERSION/$(VERSION)/g" < $< > $@ + +small: + make OPTIONS="small" all $(BIN): $(OBJ) $(CC) -o $@ $^ $(WCHISP_LDFLAGS) .c.o: $(CC) $(WCHISP_CFLAGS) $(WCHISP_CPPFLAGS) -c $< -install: - mkdir -p $(DESTDIR)$(PREFIX)/bin - cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin - chmod 755 $(DESTDIR)$(PREFIX)/bin/$(BIN) - mkdir -p $(DESTDIR)$(MANPREFIX)/man1 - sed "s/VERSION/$(VERSION)/g" < $(MAN) > $(DESTDIR)$(MANPREFIX)/man1/$(MAN) - chmod 644 $(DESTDIR)$(MANPREFIX)/man1/$(MAN) - -install-rules: - mkdir -p $(DESTDIR)$(UDEVPREFIX)/rules.d - cp -f 50-wchisp.rules $(DESTDIR)$(UDEVPREFIX)/rules.d +install: all + install -D $(BIN) $(DESTDIR)$(PREFIX)/bin/${BIN} + install -m644 -D $(MAN) $(DESTDIR)$(MANPREFIX)/man1/$(MAN) + mkdir -p $(DESTDIR)$(PREFIX)/share/$(NAME) + cp -a devices $(DESTDIR)$(PREFIX)/share/$(NAME)/ + install -D 50-wchisp.rules $(DESTDIR)$(UDEVPREFIX)/rules.d/50-wchisp.rules uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) rm -f $(DESTDIR)$(MANPREFIX)/man1/$(MAN) + rm -rf $(DESTDIR)$(PREFIX)/share/$(NAME) + rm -f $(DESTDIR)$(UDEVPREFIX)/rules.d/50-wchisp.rules dist: mkdir -p $(BIN)-$(VERSION) @@ -62,6 +74,25 @@ dist: rm -rf $(BIN)-$(VERSION) clean: - rm -f $(OBJ) $(BIN) + rm -f $(OBJ) $(BIN) $(MAN) + +test: + @if [ $(PORT)"" = "" ] ; then \ + echo "examples: " ;\ + echo "make PORT=/dev/ttyUSB0 test" ;\ + echo "./"$(BIN)" --port=USB info" ;\ + echo "./"$(BIN)" --port='//./COM3' --reset=RTS --boot0=DTR info" ;\ + else \ + stty -F $(PORT) 300 ;\ + stty -F $(PORT) 50 ;\ + bash -c "echo 'RBU' > $(PORT)" ;\ + bash -c "echo 'rBU' > $(PORT)" ;\ + sleep 1 ;\ + ./$(BIN) --port=$(PORT) info ;\ + stty -F $(PORT) 50 ;\ + bash -c "echo 'RbU' > $(PORT)" ;\ + sleep 1 ;\ + bash -c "echo 'rbuz' > $(PORT)" ;\ + fi -.PHONY: all install install-rules uninstall dist clean +.PHONY: all install uninstall dist clean test diff --git a/README.md b/README.md index 72f9d43..20b3431 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,92 @@ wch-isp ======= -wch-isp is an utility to write firmware into the flash of WCH microcontrollers, over USB. +wch-isp is an utility to write firmware into the flash of WCH microcontrollers, over USB or COM-port. This utility started as a rewrite in C of the rust tool [wchisp](https://github.com/ch32-rs/wchisp). ``` -usage: wch-isp [-VDnpr] [-d ] COMMAND [ARG ...] - wch-isp [-VDnpr] [-d ] [flash|write|verify|reset] FILE - wch-isp [-VDnpr] [-d ] [erase|config|remove-wp] - wch-isp [-VDnpr] list - -options: - -d Select the usb device that matches the uid - -n No verify after writing to flash, done by default - -p Print a progress-bar during command operation - -r Reset after command completed - -D Print raw isp command (for debug) - -V Print version and exit +usage: ./wch-isp [OPTIONS] COMMAND [ARG...] + OPTIONS: + -v Print version and exit + -h, --help Show this help and exit + -n No verify after writing + -p Show progress bar + -d Debug mode, show raw commands + -r Reset after command completed + -b Do not read database\n"); + -f Ignore if firmware size more than cached flash size (program memory) + -F Ignore if firmware size more than total flash size (program memory + const data memory + --port=USB Specify port as USB (default) + --port=/dev/ttyUSB0 9600 Specify port as COM-port '/dev/ttyUSB0' with speed 9600 baud + --port='//./COM3' Specify port as COM-port '//./COM3' + --device=DEV Test if connected device is DEV and exit if they differ + --uid=AA-BB-CC-DD-EE-FF-GG-HH Specify device UID + --reset=PIN Use PIN as RESET + --boot0=PIN Use PIN as Boot0 + 'PIN' may be 'RTS', 'DTR', 'nRTS' or 'nDTR' + --address=0x08000000 Write or verify data from specified address + --database-path=/home/user/wch-isp/devices Search device info in specified path + + COMMAND: + write FILE write file (.hex or .bin) + verify FILE verify file (.hex ot .bin) + erase erase all memory + list show connected devices + unlock remove write protection + info show device info: bootloader version, option bytes, flash size etc + optionbytes 'CMD' change optionbytes + example: ./wch-isp optionbytes 'RDPR=0xA5, DATA0 = 0x42' + optionshow 'CMD' show changes after apply CMD to optionbytes; Do not write ``` This utility has been tested on: - - CH32V103 - - CH569W + - CH32V307RCT6 + - CH32V303CBT6 + - CH32V203G8R6 ## Examples List detected device in bootloader mode: + ```sh -$ wch-isp list -0: BTVER v2.7 UID 8d-ff-ba-e4-c2-84-09-69 [0x1069] CH569 -1: BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 +./wch-isp list +found 0x19 0x3B ( CH32V203G8R6 ), bt ver.0206 uid = [ CD-AB-1D-36-51-BC-3B-9E ] +found 0x17 0x71 ( CH32V307RCT6 ), bt ver.0209 uid = [ 87-80-CB-26-3B-38-8D-DF ] ``` -Flash the `firmware.bin` file, `-p` enable the progress bar. -``` -$ wch-isp -p flash firmware.bin -BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 -[####################################################] write 35392/35392 -[####################################################] verify 35392/35392 -flash done +Flash the `firmware.bin` file via USB, `-p` enable the progress. + +```sh +$ ./wch-isp --port=USB -p write firmware.bin +Erase 1 sectors (1024 bytes) +Write: 100.0 % Write 792 bytes: DONE +Verify: 100.0 % Verify 792 bytes: DONE ``` -Erase the device's flash, select the device by it's uid (option `-d`). +Verify the `firmware.hex` file via COM-port (reset connected to RTS, Boot0 connected to DTR): + +```sh +$ ./wch-isp --port=/dev/ttyUSB0 --reset=RTS --boot0=DTR verify firmware.hex +Verify 792 bytes: DONE + ``` -$ wch-isp -d f2-3e-88-26-3b-38-b5-9d erase -BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 -erase done + +Unlock read-protection and write 0x42 to DATA0 field in optionbytes: + +```sh +$ ./wch-isp --device=CH32V203G8R6 optionbytes 'RDPR=0xA5 DATA0 = 0x42' +Option bytes write: + 0xC03F5AA5 + 0xBA45BD42 + 0xFFFFFFFF +Done + ``` ## Dependency -wch-isp depends on libusb 1.0 or above. +wch-isp depends on **libusb-1.0** and **yaml-0.1** or above. ## How to build @@ -72,6 +107,16 @@ Default udev rules are provided and can be installed with this command: make install-rules ``` +### Cross-compilling using mingw32 + +Download and unarchive **mingw-w64-i686-libusb** and **mingw-w64-i686-libyaml** into special directory, for example, ```lib/mingw32```. Then execute makefile: + +``` +make CROSS_COMPILE=i686-w64-mingw32- INCS="-Imingw32/include -Imingw32/include/libusb-1.0" LIBS="-Lmingw32/lib mingw32/bin/libusb-1.0.dll -lyaml" +``` + +Comilled binary will reqire **libusb-1.0.dll**, **libyaml-0-2.dll** (from ```lib/mingw32/bin```) and **libgcc_s_dw2-1.dll** (in my system: ```/usr/lib/gcc/i686-w64-mingw32/10-win32/libgcc_s_dw2-1.dll```) + ### Windows using MSYS2 On Windows the build is done using MSYS2 and mingw64, you can install this from https://www.msys2.org @@ -89,3 +134,7 @@ Then the `wch-isp.exe` binary can be run like so: ``` PATH="$PATH:/mingw64/bin" ./wch-isp.exe ``` + +## TODO: + +- Test compilling on Windows (Jules Maselbas probably tested it, but I (COKPOWEHEU) haven't yet) diff --git a/arg.h b/arg.h deleted file mode 100644 index 028db0b..0000000 --- a/arg.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copy me if you can. */ - -#ifndef ARG_H -#define ARG_H - -extern char *argv0; - -#define ARGBEGIN {char *_arg, **_argp, **_args; \ - for (argv0 = *argv++, argc--, _args = _argp = argv; \ - (_arg = *_argp) != NULL; /* while != NULL */ \ - *_argp ? _argp++ : 0) /* inc only if _argp != NULL */ \ - if (*_arg == '-' && _arg[1] == '-' && _arg[2] == '\0') \ - for (argc--, _argp++; /* skip the '--' arg */ \ - (_arg = *_argp) != NULL; \ - *_argp ? _argp++ : 0) \ - *(_args++) = _arg; /* copy all args */ \ - else if (*_arg == '-' && _arg[1] != '-' && _arg[1] != '\0') \ - for (argc--, _arg++; *_arg; *_arg ? _arg++ : 0) \ - switch (*_arg) - -#define ARGLONG else if (*_arg == '-' && _arg[1] == '-' && _arg[2] != '\0') - -#define ARGEND else *(_args++) = _arg; /* else copy the argument */ } - -#define ARGC() *_arg -#define ARGF() ((_arg[1])? (*_arg = 0, _arg + 1) :\ - (_argp[1])? (argc--, _argp++, _argp[0]) :\ - NULL) -#define EARGF(x) ((_arg[1])? (*_arg = 0, _arg + 1) :\ - (_argp[1])? (argc--, _argp++, _argp[0]) :\ - ((x), abort(), NULL)) - -#define ARGLC() (_arg + 2) -#define ARGLF() (_argp ? (_argp++, *_argp) : NULL) -#define EARGLF(x) (_argp ? (_argp++, *_argp) : ((x), abort(), NULL)) - -#endif /* ARG_H */ diff --git a/devices.h b/devices.h deleted file mode 100644 index 4ef8003..0000000 --- a/devices.h +++ /dev/null @@ -1,96 +0,0 @@ -#define SZ_1K (1024) -#define SZ_5K ( 5 * SZ_1K) -#define SZ_8K ( 8 * SZ_1K) -#define SZ_10K (10 * SZ_1K) -#define SZ_14K (14 * SZ_1K) -#define SZ_16K (16 * SZ_1K) -#define SZ_28K (28 * SZ_1K) -#define SZ_32K (32 * SZ_1K) -#define SZ_60K (60 * SZ_1K) -#define SZ_64K (64 * SZ_1K) -#define SZ_128K (128 * SZ_1K) -#define SZ_192K (192 * SZ_1K) -#define SZ_224K (224 * SZ_1K) -#define SZ_448K (448 * SZ_1K) -#define SZ_480K (480 * SZ_1K) - -/* record for a device */ -struct db_dev { - u8 id; - const char *name; - u32 flash_size; - u32 eeprom_size; -}; - -/* record for a device family */ -struct db { - u8 type; - u32 flash_sector_size; - const char *name; - int (*show_conf)(struct isp_dev *dev, size_t len, u8 *cfg); - const struct db_dev *devs; -}; - -const struct db devices[] = { - { .type = 0x10, .flash_sector_size = 256, - .name = "CH56x", - .show_conf = ch56x_show_conf, - .devs = (const struct db_dev[]){ - { 0x63, "CH563", SZ_224K, SZ_28K, }, - { 0x42, "CH563", SZ_224K, SZ_28K, }, - { 0x43, "CH563", SZ_224K, SZ_28K, }, - { 0x44, "CH563", SZ_224K, SZ_28K, }, - { 0x45, "CH563", SZ_224K, SZ_28K, }, - { 0x65, "CH565", SZ_448K, SZ_32K, }, - { 0x66, "CH566", SZ_64K, SZ_32K, }, - { 0x67, "CH567", SZ_192K, SZ_32K, }, - { 0x68, "CH568", SZ_192K, SZ_32K, }, - { 0x69, "CH569", SZ_448K, SZ_32K, }, - { /* sentinel */ } } - }, - { .type = 0x11, .flash_sector_size = 1024, - .name = "CH55x", - .devs = (const struct db_dev[]){ - { 0x51, "CH551", SZ_10K, 128 }, - { 0x52, "CH552", SZ_14K, 128 }, - { 0x53, "CH554", SZ_14K, 128 }, - { 0x55, "CH555", SZ_60K, SZ_1K }, - { 0x56, "CH556", SZ_60K, SZ_1K }, - { 0x57, "CH557", SZ_60K, SZ_1K }, - { 0x58, "CH558", SZ_32K, SZ_5K }, - { 0x59, "CH559", SZ_60K, SZ_1K }, - { /* sentinel */ } } - }, - { .type = 0x14, .flash_sector_size = 1024, - .name = "CH32F103", - .devs = (const struct db_dev[]){ - { 0x3f, "CH32F103x6x6", SZ_32K }, - { 0x33, "CH32F103x8x6", SZ_64K }, - { /* sentinel */ } } - }, - { .type = 0x15, .flash_sector_size = 1024, - .name = "CH32V103", - .devs = (const struct db_dev[]){ - { 0x3f, "CH32V103x6x6", SZ_32K }, - { 0x33, "CH32V103x8x6", SZ_64K }, - { /* sentinel */ } } - }, - { .type = 0x19, .flash_sector_size = 1024, - .name = "CH32V20x", - .devs = (const struct db_dev[]){ - { 0x80, "CH32V208WBU6", SZ_480K }, - { 0x81, "CH32V208RBT6", SZ_128K }, - { 0x82, "CH32V208CBU6", SZ_128K }, - { 0x83, "CH32V208CBU6", SZ_128K }, - { 0x30, "CH32V203C8U6", SZ_64K }, - { 0x31, "CH32V203C8T6", SZ_64K }, - { 0x32, "CH32V203K8T6", SZ_64K }, - { 0x33, "CH32V203C6T6", SZ_32K }, - { 0x34, "CH32V203RBT6", SZ_128K }, - { 0x35, "CH32V203K6T6", SZ_32K }, - { 0x35, "CH32V203G6U6", SZ_32K }, - { 0x35, "CH32V203F6P6", SZ_32K }, - { 0x37, "CH32V203F6P6", SZ_32K }, - { /* sentinel */ } } - }, -}; diff --git a/devices/0x15-CH32V103.yaml b/devices/0x15-CH32V103.yaml new file mode 100644 index 0000000..73787cf --- /dev/null +++ b/devices/0x15-CH32V103.yaml @@ -0,0 +1,71 @@ +--- +name: CH32V103 Series +mcu_type: 5 +device_type: 0x15 +support_net: false +support_usb: true +support_serial: true +description: CH32V103 (RISC-V3A) Series +config_registers: + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0x00FF5AA5 + fields: + # byte 0 + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + # byte 2 + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4K sections are protected +variants: + - name: CH32V103C6T6 + chip_id: 0x32 + flash_size: 32K + - name: CH32V103(C8T6|C8U6|R8T6) + chip_id: 0x33 + alt_chip_ids: ["ALL"] + flash_size: 64K + - name: CH32V103C8T6 + chip_id: 0x3F + alt_chip_ids: ["ALL"] + flash_size: 64k diff --git a/devices/0x16-CH58x.yaml b/devices/0x16-CH58x.yaml new file mode 100644 index 0000000..5c27c40 --- /dev/null +++ b/devices/0x16-CH58x.yaml @@ -0,0 +1,103 @@ +--- +name: CH58x Series +mcu_type: 6 +device_type: 0x16 +support_usb: true +support_serial: true +support_net: false +description: CH58x (RISC-V4A BLE 5.3) Series +flash_total: 480k +config_registers: + - offset: 0x00 + name: RESERVED + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: WPROTECT + reset: 0xFFFFFFFF + type: u32 + fields: + - bit_range: [0, 0] + name: NO_KEY_SERIAL_DOWNLOAD + description: Turn on No-key serial port download + explaination: + 1: Enable + 0: Disable + - bit_range: [1, 1] + name: DOWNLOAD_CFG + explaination: + 1: PB22(Default set) + 0: PB11 + # TODO: parse write protect address + - offset: 0x08 + name: USER_CFG + description: User non-volatile configuration + # CFG_DEBUG_EN=1、CFG_RESET_EN=0、CFG_ROM_READ=1 + # Enable 2-wire debug + # reset: 0xd73f0f4d + # See-also: #26 + reset: 0xd50fff4f # WeActStudio default + type: u32 + fields: + - bit_range: [2, 0] + name: RESERVED + explaination: + 0b101: Default + _: Changed + - bit_range: [3, 3] + name: CFG_RESET_EN + description: "RST# external manual reset input pin enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [4, 4] + name: CFG_DEBUG_EN + description: "Two-wire simulation debug interface SWD enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [5, 5] + name: RESERVED + - bit_range: [6, 6] + name: CFG_BOOT_EN + description: "Bootloader enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [7, 7] + name: CFG_ROM_READ + description: "Code and data protection mode in FlashROM" + explaination: + 0: Disable the programmer to read out, and keep the program secret + 1: Read enable + - bit_range: [27, 8] + name: RESERVED + explaination: + 0xFFF0F: Default + _: Changed + - bit_range: [31, 28] + name: VALID_SIG + description: "Configuration information valid flag, fixed value" + explaination: + 0b0100: Valid + _: Error + +variants: + - name: CH581 + chip_id: 0x81 + flash_size: 192k + eeprom_size: 32k + eeprom_start_addr: 0x00070000 + + - name: CH582 + chip_id: 0x82 + flash_size: 448k + eeprom_size: 32k + eeprom_start_addr: 0x00070000 + + - name: CH583 + chip_id: 0x83 + flash_size: 448k + eeprom_size: 32k + eeprom_start_addr: 0x00070000 diff --git a/devices/0x17-CH32V30x.yaml b/devices/0x17-CH32V30x.yaml new file mode 100644 index 0000000..8c63856 --- /dev/null +++ b/devices/0x17-CH32V30x.yaml @@ -0,0 +1,95 @@ +--- +name: CH32V30x Series +mcu_type: 7 +device_type: 0x17 +support_net: false +support_usb: true +support_serial: true +description: CH32V30x (RISC-V4F) Series +flash_total: 480k +config_registers: + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0x00FF5AA5 + fields: + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + # byte 2, [0:0] + 16 + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + # [1:1] + 16 + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + # [2:2] + 16 + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + # [7:6] + 16 + - bit_range: [23, 22] + name: SRAM_CODE_MODE + description: SRAM Code Mode + explaination: + 0b00: CODE-192kB + RAM-128kB + 0b01: CODE-224kB + RAM-96kB + 0b10: CODE-256kB + RAM-64kB + 0b11: CODE-288kB + RAM-32kB + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4k sections are protected +variants: + - name: CH32V305RBT6 + chip_id: 0x50 + flash_size: 128k + - name: CH32V307VCT6 + chip_id: 0x70 + flash_size: 256k + - name: CH32V307RCT6 + chip_id: 0x71 + flash_size: 256k + - name: CH32V307WCU6 + chip_id: 0x73 + flash_size: 256k + - name: CH32V303VCT6 + chip_id: 0x30 + flash_size: 256k + - name: CH32V303RCT6 + chip_id: 0x31 + flash_size: 256k + - name: CH32V303RBT6 + chip_id: 0x32 + flash_size: 128k + - name: CH32V303CBT6 + chip_id: 0x33 + flash_size: 128k diff --git a/devices/0x19-CH32V20x.yaml b/devices/0x19-CH32V20x.yaml new file mode 100644 index 0000000..9364246 --- /dev/null +++ b/devices/0x19-CH32V20x.yaml @@ -0,0 +1,159 @@ +--- +name: CH32V20x Series +mcu_type: 9 +device_type: 0x19 +support_net: false +support_usb: true +support_serial: true +description: CH32V20x (RISC-V4B/V4C) Series +flash_total: 224k +config_registers: + # Ref: section "32.6 User option bytes" of RM manual + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0x00FF5AA5 + type: u32 + fields: + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + # [1:1] + 16 + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + # [2:2] + 16 + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + # [7:6] + 16 + - bit_range: [23, 22] + name: SRAM_CODE_MODE + description: SRAM Code Mode + explaination: + 0b00: CODE-192KB + RAM-128KB / CODE-128KB + RAM-64KB depending on the chip + 0b01: CODE-224KB + RAM-96KB / CODE-144KB + RAM-48KB depending on the chip + 0b10: CODE-256KB + RAM-64KB / CODE-160KB + RAM-32KB depending on the chip + 0b11: CODE-288KB + RAM-32KB / CODE-160KB + RAM-32KB depending on the chip +config_registers_static: + # Ref: section "32.6 User option bytes" of RM manual + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0xC03F5AA5 + type: u32 + fields: + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4K sections are protected + +# chip_id 0x8X for V4B, chip_id 0x3X for V4C +variants: + - name: CH32V208WBU6 + chip_id: 0x80 + flash_size: 128k + flash_total: 480k + - name: CH32V208RBT6 + chip_id: 0x81 + flash_size: 128k + flash_total: 480k + - name: CH32V208CBU6 + chip_id: 0x82 + flash_size: 128k + flash_total: 480k + - name: CH32V208GBU6 + chip_id: 0x83 + flash_size: 128k + flash_total: 480k + + - name: CH32V203C8U6 + chip_id: 0x30 + flash_size: 64k + config_registers: *config_registers_static + - name: CH32V203C8T6 + chip_id: 0x31 + flash_size: 64k + config_registers: *config_registers_static + - name: CH32V203K8T6 + chip_id: 0x32 + flash_size: 64k + config_registers: *config_registers_static + - name: CH32V203C6T6 + chip_id: 0x33 + flash_size: 32k + config_registers: *config_registers_static + - name: CH32V203RBT6 + chip_id: 0x34 + flash_size: 128k + - name: CH32V203K6T6 + chip_id: 0x35 + flash_size: 32k + config_registers: *config_registers_static + - name: CH32V203G6U6 + chip_id: 0x36 + flash_size: 32k + config_registers: *config_registers_static + - name: CH32V203F6P6 + chip_id: 0x37 + flash_size: 32k + config_registers: *config_registers_static + - name: CH32V203G8R6 + chip_id: 0x3B + flash_size: 64k + config_registers: *config_registers_static + # FIXME: missing 0x38 diff --git a/devices/0x22-CH59x.yaml b/devices/0x22-CH59x.yaml new file mode 100644 index 0000000..e04f628 --- /dev/null +++ b/devices/0x22-CH59x.yaml @@ -0,0 +1,94 @@ +--- +name: CH59x Series +mcu_type: 0x12 +device_type: 0x22 +support_usb: true +support_serial: true +support_net: false +flash_total: 480k +description: CH59x (RISC-V4C BLE 5.4) Series +config_registers: + - offset: 0x00 + name: RESERVED + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: WPROTECT + reset: 0xFFFFFFFF + type: u32 + fields: + - bit_range: [0, 0] + name: NO_KEY_SERIAL_DOWNLOAD + description: Turn on No-key serial port download + explaination: + 1: Enable + 0: Disable + - bit_range: [1, 1] + name: DOWNLOAD_CFG + explaination: + 1: PB22(Default set) + 0: PB11 + - offset: 0x08 + name: USER_CFG + description: User config register + reset: 0x4FFF0FD5 + type: u32 + fields: + - bit_range: [2, 0] + name: RESERVED + explaination: + 0b101: Default + _: Error + - bit_range: [3, 3] + name: CFG_RESET_EN + description: "RST# external manual reset input pin enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [4, 4] + name: CFG_DEBUG_EN + description: "Two-wire simulation debug interface SWD enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [5, 5] + name: RESERVED + explaination: + 0: Default + _: Error + - bit_range: [6, 6] + name: CFG_BOOT_EN + description: "Bootloader enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [7, 7] + name: CFG_ROM_READ + description: "Code and data protection mode in FlashROM" + explaination: + 0: Disable the programmer to read out, and keep the program secret + 1: Read enable + - bit_range: [27, 8] + name: RESERVED + explaination: + 0xFF0F: Default + _: Error + - bit_range: [31, 28] + name: VALID_SIG + description: "Configuration information valid flag, fixed value" + explaination: + 0b0100: Valid + _: Error + +variants: + - name: CH591 + chip_id: 0x91 + flash_size: 192k + eeprom_size: 32k + + - name: CH592 + chip_id: 0x92 + flash_size: 448k + eeprom_size: 32k + diff --git a/devices/todo/0x10-CH56x.yaml b/devices/todo/0x10-CH56x.yaml new file mode 100644 index 0000000..7305149 --- /dev/null +++ b/devices/todo/0x10-CH56x.yaml @@ -0,0 +1,144 @@ +--- +name: CH56x Series +mcu_type: 0 +device_type: 0x10 +support_serial: true +description: CH56x Series, RISC-V3A (CH569/CH565), ARM9 like (CH563/CH561), RISC (CH566/CH567/CH568) NDS32? (CH568) +# Section 2.2.1 On-chip non-volatile memory map +config_registers_ch569: &config_registers_ch_565_ch569 + - offset: 0x00 + name: UNKNOWN0 + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: UNKNOWN1 + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x08 + name: NV_INFO + description: Non-volatile information + reset: 0x8FFFF2E5 + type: u32 + fields: + - bit_range: [31,30] + name: USER_MEM + description: System RAMX/ROM capacity redefine configuration. + explaination: + 0b00: RAMX 32KB + ROM 96KB + 0b01: RAMX 64KB + ROM 64KB + _: RAMX 96KB + ROM 32KB + - bit_range: [29,29] + name: LOCKUP_RST_EN + description: Core LOCKUP reset system enable + explaination: + 1: Reset + 0: NotReset + - bit_range: [28,28] + name: RESERVED1 + explaination: + 0: Reserved + _: Error + - bit_range: [27,12] + name: RESERVED2 + explaination: + 0xffff: Reserved + _: Error + - bit_range: [11,10] + name: RESERVED3 + explaination: + 0b00: Reserved + _: Error + - bit_range: [9,8] + name: RESERVED4 + explaination: + 0b10: Reserved + _: Error + - bit_range: [7,7] + name: CODE_READ_EN + description: External programmer read FLASH enable + explaination: + 1: Enable + 0: Disable + - bit_range: [6,6] + name: BOOT_EN + description: Bootloader function enable + explaination: + 1: Enable + 0: Disable + - bit_range: [5,5] + name: DEBUG_EN + description: Debug interface enable + explaination: + 1: Enable + 0: Disable + - bit_range: [4,4] + name: RESET_EN + description: External reset enable + explaination: + 1: Enable via PB15 + 0: Disable, PB15 is used as GPIO + - bit_range: [3,0] + name: RESERVED5 + explaination: + 0b0101: Reserved + _: Error +variants: + # use 0x46 to probe + - name: CH561 + chip_id: 0x61 + alt_chip_ids: [0x46] + flash_size: 64K + eeprom_size: 28K + support_usb: false + support_net: true + + # use 0x42 to probe + - name: CH563 + chip_id: 0x63 + # 0x45 for CH563_E? + alt_chip_ids: [0x42, 0x43, 0x44, 0x45] + flash_size: 224K + eeprom_size: 28K + support_usb: true + support_net: true + + - name: CH565 + chip_id: 0x65 + flash_size: 448K + eeprom_size: 32K + support_usb: true + support_net: false + eeprom_start_addr: 0 + config_registers: *config_registers_ch_565_ch569 + + - name: CH566 + chip_id: 0x66 + flash_size: 64K + eeprom_size: 32K + support_usb: true + support_net: false + eeprom_start_addr: 0 + + - name: CH567 + chip_id: 0x67 + flash_size: 192K + eeprom_size: 32K + support_usb: true + support_net: false + + - name: CH568 + chip_id: 0x68 + flash_size: 192K + eeprom_size: 32K + support_usb: true + support_net: false + + - name: CH569 + chip_id: 0x69 + flash_size: 448K + eeprom_size: 32K + support_usb: true + support_net: false + config_registers: *config_registers_ch_565_ch569 diff --git a/devices/todo/0x11-CH55x.yaml b/devices/todo/0x11-CH55x.yaml new file mode 100644 index 0000000..9278f67 --- /dev/null +++ b/devices/todo/0x11-CH55x.yaml @@ -0,0 +1,125 @@ +--- +name: CH55x Series +mcu_type: 1 +device_type: 0x11 +support_usb: true +support_serial: true +support_net: false +description: CH55x (E8051) Series +config_registers: + - offset: 0x00 + name: REVERSED + description: Reversed 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: WPROTECT + reset: 0xFFFFFFFF + type: u32 + fields: + - bit_range: [0, 0] + name: NO_KEY_SERIAL_DOWNLOAD + description: Turn on No-key serial port download + explaination: + 1: Enable + 0: Disable + - bit_range: [1, 1] + name: DOWNLOAD_CFG + explaination: + 1: P4.6 / P15 / P3.6(Default set) + 0: P5.1 / P51 / P1.5 + - offset: 0x08 + name: GLOBAL_CFG + reset: 0xFFFF4EFF + type: u32 + fields: + # Configuration Information, sec 6.2 + - bit_range: [15, 15] + name: CODE_PROTECT + explaination: + 0: Forbid code & data protection + 1: Readable + - bit_range: [14, 14] + name: NO_BOOT_LOAD + explaination: + 0: Boot from 0x0000 Application + 1: Boot from 0xf400 Bootloader + - bit_range: [13, 13] + name: EN_LONG_RESET + explaination: + 0: Short reset + 1: Wide reset, add 87ms reset time + - bit_range: [12, 12] + name: XT_OSC_STRONG + explaination: + 0: Standard + 1: Enhanced + - bit_range: [11, 11] + name: EN_P5.7_RESET + explaination: + 0: Forbid + 1: Enable reset + - bit_range: [10, 10] + name: EN_P0_PULLUP + explaination: + 0: Forbid + 1: Enable + - bit_range: [9, 8] + name: RESERVED + explaination: + 0b10: Default + _: Error + - bit_range: [7, 0] + name: RESERVED + explaination: + 0b11111111: Default + _: Error +variants: + - name: CH551 + chip_id: 0x51 + flash_size: 10K + eeprom_size: 128 + eeprom_start_addr: 0xC000 + + - name: CH552 + chip_id: 0x52 + # FIXME: 16K or 14K + flash_size: 14K + eeprom_size: 128 + eeprom_start_addr: 0xC000 + + - name: CH554 + chip_id: 0x54 + flash_size: 14K + eeprom_size: 128 + eeprom_start_addr: 0xC000 + + - name: CH555 + chip_id: 0x55 + flash_size: 61440 + eeprom_size: 1K + eeprom_start_addr: 61440 + + - name: CH556 + chip_id: 0x56 + flash_size: 61440 + eeprom_size: 1K + eeprom_start_addr: 61440 + + - name: CH557 + chip_id: 0x57 + flash_size: 61440 + eeprom_size: 1024 + eeprom_start_addr: 61440 + + - name: CH558 + chip_id: 0x58 + flash_size: 32768 + eeprom_size: 5120 + eeprom_start_addr: 0xE000 + + - name: CH559 + chip_id: 0x59 + flash_size: 61440 + eeprom_size: 1024 + eeprom_start_addr: 61440 diff --git a/devices/todo/0x12-CH54x.yaml b/devices/todo/0x12-CH54x.yaml new file mode 100644 index 0000000..5ce0874 --- /dev/null +++ b/devices/todo/0x12-CH54x.yaml @@ -0,0 +1,92 @@ +--- +name: CH54x Series +mcu_type: 2 +device_type: 0x12 +support_usb: true +support_serial: true +support_net: false +# CH540-CH542 is not supported yet +description: CH54x (E8051) Series +config_registers: &config_registers_ch548_ch549 + - offset: 0x00 + name: REG0 + reset: 0x00000000 + - offset: 0x04 + name: REG1 + reset: 0x00000000 + fields: + - bit_range: [7, 0] + name: REG1_0 + # section 6.2 + - offset: 0x08 + name: REG2 + reset: 0x0000D200 + fields: + - bit_range: [2, 0] + name: LV_RST_VOL + explaination: + 0b000: 2.4V + 0b001: 2.4V + 0b010: 2.7V + 0b011: 3.0V + 0b100: 3.6V + 0b101: 4.0V + 0b110: 4.3V + 0b111: 4.6V + - bit_range: [8, 8] + name: MUST_0 + - bit_range: [9, 9] + name: MUST_1 + - bit_range: [12, 12] + name: En_P5.7_RESET + - bit_range: [13, 13] + name: En_Long_Reset + - bit_range: [14, 14] + name: No_Boot_Load + - bit_range: [15, 15] + name: Code_Protect +variants: + - name: CH543 + chip_id: 67 + flash_size: 14336 + eeprom_size: 1024 + eeprom_start_addr: 14336 + support_serial: false + + - name: CH544 + flash_size: 61440 + eeprom_size: 1024 + chip_id: 68 + eeprom_start_addr: 61440 + + - name: CH545 + flash_size: 61440 + eeprom_size: 1024 + chip_id: 69 + eeprom_start_addr: 61440 + + - name: CH546 + flash_size: 32768 + eeprom_size: 1024 + chip_id: 70 + eeprom_start_addr: 61440 + + - name: CH547 + flash_size: 61440 + eeprom_size: 1024 + chip_id: 71 + eeprom_start_addr: 61440 + + - name: CH548 + flash_size: 32768 + eeprom_size: 1024 + chip_id: 72 + eeprom_start_addr: 61440 + config_registers: *config_registers_ch548_ch549 + + - name: CH549 + flash_size: 61440 + eeprom_size: 1024 + chip_id: 73 + eeprom_start_addr: 61440 + config_registers: *config_registers_ch548_ch549 diff --git a/devices/todo/0x13-CH57x.yaml b/devices/todo/0x13-CH57x.yaml new file mode 100644 index 0000000..4ea7c08 --- /dev/null +++ b/devices/todo/0x13-CH57x.yaml @@ -0,0 +1,199 @@ +--- +name: CH57x Series +mcu_type: 3 +device_type: 0x13 +support_usb: true +support_serial: true +support_net: false +# CH571/CH573: RISC-V3A +# Others: Cortext-M0 +description: CH57x (Cortex-M0/RISC-V3A BLE 4.2) Series +# The following applies to CH573. It seems the same as CH58x. +config_registers_ch571_ch573: &config_registers_ch571_ch573 + - offset: 0x00 + name: RESERVED + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: WPROTECT + reset: 0xFFFFFFFF + type: u32 + fields: + - bit_range: [0, 0] + name: NO_KEY_SERIAL_DOWNLOAD + description: Turn on No-key serial port download + explaination: + 1: Enable + 0: Disable + - bit_range: [1, 1] + name: DOWNLOAD_CFG + explaination: + 1: PB22(Default set) + 0: PB11 + # Table 2.3 in Chip Datasheet + - offset: 0x08 + name: USER_CFG + description: User config register + # reset: 0x4FFF0F4D + # CFG_DEBUG_EN=1、CFG_RESET_EN=0、CFG_ROM_READ=1 + # enable 2-wire debug + reset: 0x4FFF0FD5 + type: u32 + fields: + - bit_range: [2, 0] + name: RESERVED + explaination: + 0b101: Default + _: Error + - bit_range: [3, 3] + name: CFG_RESET_EN + description: "RST# external manual reset input pin enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [4, 4] + name: CFG_DEBUG_EN + description: "Two-wire simulation debug interface SWD enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [5, 5] + name: RESERVED + explaination: + 0: Default + _: Error + - bit_range: [6, 6] + name: CFG_BOOT_EN + description: "Bootloader enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [7, 7] + name: CFG_ROM_READ + description: "Code and data protection mode in FlashROM" + explaination: + 0: Disable the programmer to read out, and keep the program secret + 1: Read enable + - bit_range: [27, 8] + name: RESERVED + explaination: + 0xFFF0F: Default + _: Error + - bit_range: [31, 28] + name: VALID_SIG + description: "Configuration information valid flag, fixed value" + explaination: + 0b0100: Valid + _: Error + +config_registers_ch579: &config_registers_ch579 + - offset: 0x00 + name: RESERVED + description: Reserved 32-bit word + reset: 0xFFFFFFFF + type: u32 + - offset: 0x04 + name: WPROTECT + reset: 0xFFFFFFFF + type: u32 + fields: + - bit_range: [0, 0] + name: NO_KEY_SERIAL_DOWNLOAD + description: Turn on No-key serial port download + explaination: + 1: Enable + 0: Disable + - bit_range: [1, 1] + name: DOWNLOAD_CFG + explaination: + 1: PB22(Default set) + 0: PB11 + - offset: 0x08 + name: USER_CFG + description: User config register + reset: 0x50FFFF48 + type: u32 + fields: + - bit_range: [2, 0] + name: RESERVED + explaination: + 0b000: Default + _: Error + - bit_range: [3, 3] + name: CFG_RESET_EN + description: "RST# external manual reset input pin enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [4, 4] + name: CFG_DEBUG_EN + description: "Two-wire simulation debug interface SWD enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [5, 5] + name: RESERVED + explaination: + 0: Default + _: Error + - bit_range: [6, 6] + name: CFG_BOOT_EN + description: "Bootloader enable" + explaination: + 0: Disable + 1: Enable + - bit_range: [7, 7] + name: CFG_ROM_READ + description: "Code and data protection mode in FlashROM" + explaination: + 0: Disable the programmer to read out, and keep the program secret + 1: Read enable + - bit_range: [27, 8] + name: RESERVED + explaination: + 0xFFFF: Default + _: Error + - bit_range: [31, 28] + name: VALID_SIG + description: "Configuration information valid flag, fixed value" + explaination: + 0b0101: Valid + _: Error + +variants: + # Boot pin for CH571F: PB22(Default) PB11 + # Boot pin for CH571D: PB7(Default) PB11 + # CH571K: No boot pin + - name: CH571 + chip_id: 0x71 + flash_size: 196608 + eeprom_size: 32768 + eeprom_start_addr: 196608 + config_registers: *config_registers_ch571_ch573 + + - name: CH573 + chip_id: 0x73 + flash_size: 458752 + eeprom_size: 32768 + eeprom_start_addr: 458752 + config_registers: *config_registers_ch571_ch573 + + - name: CH577 + chip_id: 0x77 + flash_size: 131072 + eeprom_size: 2048 + eeprom_start_addr: 131072 + + - name: CH578 + chip_id: 0x58 + flash_size: 163840 + eeprom_size: 2048 + eeprom_start_addr: 163840 + + - name: CH579 + chip_id: 0x79 + flash_size: 256000 + eeprom_size: 2048 + eeprom_start_addr: 256000 + config_registers: *config_registers_ch579 diff --git a/devices/todo/0x14-CH32F103.yaml b/devices/todo/0x14-CH32F103.yaml new file mode 100644 index 0000000..0524379 --- /dev/null +++ b/devices/todo/0x14-CH32F103.yaml @@ -0,0 +1,66 @@ +--- +name: CH32F103 Series +mcu_type: 4 +device_type: 0x14 +support_net: false +support_usb: true +support_serial: true +description: CH32F103 (Cortex-M3) Series +config_registers: + - offset: 0x00 + name: RDPR_USER + reset: 0x00FF5AA5 + fields: + # byte 0 + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected + explaination: + 0xa5: Unprotected + _: Protected + # byte 2 + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4K sections are protected +variants: + - name: CH32F103C6T6 + chip_id: 0x32 + flash_size: 32K + - name: CH32F103(C8T6|C8U6|R8T6) + chip_id: 0x33 + alt_chip_ids: ["ALL"] + flash_size: 64K diff --git a/devices/todo/0x15-CH32V103.yaml b/devices/todo/0x15-CH32V103.yaml new file mode 100644 index 0000000..cae08f8 --- /dev/null +++ b/devices/todo/0x15-CH32V103.yaml @@ -0,0 +1,67 @@ +--- +name: CH32V103 Series +mcu_type: 5 +device_type: 0x15 +support_net: false +support_usb: true +support_serial: true +description: CH32V103 (RISC-V3A) Series +config_registers: + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0x00FF5AA5 + fields: + # byte 0 + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + # byte 2 + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4K sections are protected +variants: + - name: CH32V103C6T6 + chip_id: 0x32 + flash_size: 32K + - name: CH32V103(C8T6|C8U6|R8T6) + chip_id: 0x33 + alt_chip_ids: ["ALL"] + flash_size: 64K diff --git a/devices/todo/0x18-CH32F20x.yaml b/devices/todo/0x18-CH32F20x.yaml new file mode 100644 index 0000000..7437681 --- /dev/null +++ b/devices/todo/0x18-CH32F20x.yaml @@ -0,0 +1,32 @@ +--- +name: CH32F20x D8/D8C/D8W Series +mcu_type: 8 +device_type: 0x18 +support_net: false +support_usb: true +support_serial: true +# Specific D8, D8C, D8W +description: CH32F20x High density general-purpose(F203), Connectivity(F205), Interconnectivity(F207), Wireless(F208) +variants: + - name: CH32F205RBT6 + chip_id: 0x50 + flash_size: 128K + - name: CH32F207VCT6 + chip_id: 0x70 + flash_size: 256K + - name: CH32F208WBU6 + chip_id: 0x80 + flash_size: 128K + - name: CH32F208RBT6 + chip_id: 0x81 + flash_size: 128K + - name: CH32F203VCT6 + chip_id: 0x30 + flash_size: 256K + - name: CH32F203RCT6 + chip_id: 0x31 + flash_size: 256K + - name: CH32F203CBT6 + chip_id: 0x33 + flash_size: 128K + diff --git a/devices/todo/0x20-CH32F20x-Compact.yaml b/devices/todo/0x20-CH32F20x-Compact.yaml new file mode 100644 index 0000000..a11dd54 --- /dev/null +++ b/devices/todo/0x20-CH32F20x-Compact.yaml @@ -0,0 +1,24 @@ +--- +name: CH32F20x D6 Series +# NOTE: mcu_type is hex-encoded, not "10" in base-10 +mcu_type: 0x10 +device_type: 0x20 +support_net: false +support_usb: true +support_serial: true +description: CH32F20x Low-and-medium-density general-purpose Cortex-M3 (Specific D6) +variants: + - name: CH32F203C8U6 + chip_id: 0x30 + flash_size: 64K + - name: CH32F203C8T6 + chip_id: 0x31 + flash_size: 64K + # FIXME: CH32F203K8T6 is not listed in config file, this is a guess + - name: CH32F203K8T6 + chip_id: 0x32 + flash_size: 64K + - name: CH32F203C6T6 + chip_id: 0x33 + flash_size: 32K + diff --git a/devices/todo/0x21-CH32V00x.yaml b/devices/todo/0x21-CH32V00x.yaml new file mode 100644 index 0000000..8067621 --- /dev/null +++ b/devices/todo/0x21-CH32V00x.yaml @@ -0,0 +1,15 @@ +--- +name: CH32V00x Series +mcu_type: 0x11 +device_type: 0x21 +support_net: false +support_usb: false +support_serial: true +description: CH32V00x (RISC-V2A) Series +config_registers: +variants: + - name: CH32V003*4*6 + chip_id: 51 + alt_chip_ids: [50, 49, 48] + flash_size: 16K + diff --git a/devices/todo/0x23-CH32X03x.yaml b/devices/todo/0x23-CH32X03x.yaml new file mode 100644 index 0000000..9b41d96 --- /dev/null +++ b/devices/todo/0x23-CH32X03x.yaml @@ -0,0 +1,91 @@ +--- +name: CH32X03x Series +# NOTE: mcu_type is hex-encoded, not "10" in base-10 +mcu_type: 0x13 +device_type: 0x23 +support_net: false +support_usb: true +support_serial: true +description: CH32X03x RISC-V4C Series +# RM Section 20.5 +config_registers: + - offset: 0x00 + name: RDPR_USER + description: RDPR, nRDPR, USER, nUSER + reset: 0xFFFF5AA5 + fields: + - bit_range: [7, 0] + name: RDPR + description: Read Protection. 0xA5 for unprotected, otherwise read-protected(ignoring WRP) + explaination: + 0xa5: Unprotected + _: Protected + # byte 2, [0:0] + 16 + - bit_range: [16, 16] + name: IWDG_SW + description: Independent watchdog (IWDG) hardware enable + explaination: + 1: IWDG enabled by the software, and disabled by hardware + 0: IWDG enabled by the software (decided along with the LSI clock) + # [1:1] + 16 + - bit_range: [17, 17] + name: STOP_RST + description: System reset control under the stop mode + explaination: + 1: Disable + 0: Enable + # [2:2] + 16 + - bit_range: [18, 18] + name: STANDBY_RST + description: System reset control under the standby mode, STANDY_RST + explaination: + 1: Disable, entering standby-mode without RST + 0: Enable + # [4:3] + 16 + - bit_range: [20, 19] + name: RST_MOD + description: Reset mode + explaination: + 0b00: Enable RST alternative function + 0b11: Disable RST alternative function, use PA21/PC3/PB7 as GPIO + _: Error + - offset: 0x04 + name: DATA + description: Customizable 2 byte data, DATA0, nDATA0, DATA1, nDATA1 + reset: 0xFF00FF00 + type: u32 + fields: + - bit_range: [7, 0] + name: DATA0 + - bit_range: [23, 16] + name: DATA1 + - offset: 0x08 + name: WRP + # Each bit represents 4K bytes (16 pages) to store the write protection status + description: Flash memory write protection status + type: u32 + reset: 0xFFFFFFFF + explaination: + 0xFFFFFFFF: Unprotected + _: Some 4K sections are protected +variants: + - name: CH32X035R8T6 + chip_id: 80 + flash_size: 64K + - name: CH32X035C8T6 + chip_id: 81 + flash_size: 64K + - name: CH32X035F8U6 + chip_id: 94 + flash_size: 64K + - name: CH32X035G8U6 + chip_id: 86 + flash_size: 64K + - name: CH32X035G8R6 + chip_id: 91 + flash_size: 64K + + - name: CH32X035F7P6 + chip_id: 87 + flash_size: 49152 + diff --git a/devices/todo/0x24-CH643.yaml b/devices/todo/0x24-CH643.yaml new file mode 100644 index 0000000..cffa154 --- /dev/null +++ b/devices/todo/0x24-CH643.yaml @@ -0,0 +1,20 @@ +--- +name: CH643 Series +# NOTE: mcu_type is hex-encoded, not "10" in base-10 +mcu_type: 0x14 +device_type: 0x24 +support_net: false +support_usb: true +support_serial: true +description: CH643 RISC-V4C RGB Display Driver Series +variants: + # NOTE: These chips share the same flash size, so we can use a single variant + - name: CH643 + # 48 - CH643W + # 49 - CH643Q + # 51 - CH643L + # 52 - CH643U + chip_id: 48 + alt_chip_ids: ["ALL"] + flash_size: 63488 + diff --git a/devices/todo/0x25-CH32L103.yaml b/devices/todo/0x25-CH32L103.yaml new file mode 100644 index 0000000..d708938 --- /dev/null +++ b/devices/todo/0x25-CH32L103.yaml @@ -0,0 +1,31 @@ +--- +name: CH32L103 Series +mcu_type: 0x15 +device_type: 0x25 +support_net: false +support_usb: true +support_serial: true +description: CH32L103 (RISC-V4C) Series +config_registers: +variants: + - name: CH32L103C8U6 + chip_id: 48 + flash_size: 64K + - name: CH32L103C8T6 + chip_id: 49 + flash_size: 64K + - name: CH32L103F8P6 + chip_id: 58 + flash_size: 64K + - name: CH32L103G8R6 + chip_id: 59 + flash_size: 64K + - name: CH32L103K8U6 + chip_id: 50 + flash_size: 64K + - name: CH32L103F8U6 + chip_id: 61 + flash_size: 64K + - name: CH32L103F7P6 + chip_id: 55 + flash_size: 48K diff --git a/devices/todo/SCHEMA.yaml b/devices/todo/SCHEMA.yaml new file mode 100644 index 0000000..97717d1 --- /dev/null +++ b/devices/todo/SCHEMA.yaml @@ -0,0 +1,31 @@ +--- +name: +mcu_type: +device_type: # normally this is mcy_type + 0x10 +support_usb: +support_serial: +support_net: +description: +config_registers: + # registers are parsed in LE mode + - offset: + name: + description: + reset: # a u32 value, used to reset chip config, like unprotect + fields: + - bit_range: [7, 0] # inclusive range, [MSB, LSB] + name: + description: + explaination: + 0xa5: Unprotected # an explaination to field value + _: Protected # matches all remaining cases +variants: + - name: + chip_id: 0x30 + alt_chip_ids: ["ALL"] # special fix to probe all chip variants + flash_size: 64K # 0x10000, 64KiB, 64KB, 64K + eeprom_size: 32K + eeprom_start_addr: 0x0 + support_usb: true # config can overwrite faimily config + support_serial: true + support_net: false diff --git a/main.c b/main.c new file mode 100644 index 0000000..80d7ecd --- /dev/null +++ b/main.c @@ -0,0 +1,967 @@ +#include +#include +#include +#include +#include +#include "wch_if.h" +#include "wch_yaml_parse.h" + +#ifndef NAME + #define NAME "wch-isp" +#endif +#ifdef PREFIX + #define DATABASE_PATH PREFIX "/share/" NAME "/devices" +#else + #define DATABASE_PATH "devices" +#endif + +#define __DATE_D__ (((__DATE__[4]==' '?'0':__DATE__[4])-'0')*10 + (__DATE__[5]-'0')) +#define __DATE_M__ (((__DATE__[1]=='a')&&(__DATE__[2]=='n'))?1: \ + (__DATE__[2]=='b')?2: \ + ((__DATE__[1]=='a')&&(__DATE__[2]=='r'))?3: \ + ((__DATE__[1]=='p')&&(__DATE__[2]=='r'))?4: \ + (__DATE__[2]=='y')?5: \ + ((__DATE__[1]=='u')&&(__DATE__[2]=='n'))?6: \ + (__DATE__[2]=='l')?7: \ + (__DATE__[2]=='g')?8: \ + (__DATE__[2]=='p')?9: \ + (__DATE__[2]=='t')?10: \ + (__DATE__[2]=='v')?11: \ + (__DATE__[2]=='c')?12: \ + 0) +#define __DATE_Y__ ((__DATE__[7]-'0')*1000 + (__DATE__[8]-'0')*100 + (__DATE__[9]-'0')*10 + (__DATE__[10]-'0')) + + +/* + * All readable and writable registers. + * - `RDPR`: Read Protection + * - `USER`: User Config Byte (normally in Register Map datasheet) + * - `WPR`: Write Protection Mask, 1=unprotected, 0=protected + * + * | BYTE0 | BYTE1 | BYTE2 | BYTE3 | + * |--------|--------|--------|--------| + * | RDPR | nRDPR | USER | nUSER | + * | DATA0 | nDATA0 | DATA1 | nDATA1 | + * | WPR0 | WPR1 | WPR2 | WPR3 | + */ +#define CFG_MASK_RDPR_USER_DATA_WPR 0b111 +#define CFG_MASK_BTVER 0b1000 // Bootloader version, in the format of `[0x00, major, minor, 0x00]` +#define CFG_MASK_UID 0b10000 // Device Unique ID +#define CFG_MASK_ALL 0b11111 // All mask bits of CFGs + + +#define CMD_IDENTIFY 0xA1 +#define CMD_ISP_END 0xA2 +#define CMD_ISP_KEY 0xA3 +#define CMD_ERASE 0xA4 +#define CMD_PROGRAM 0xA5 +#define CMD_VERIFY 0xA6 +#define CMD_READ_CONFIG 0xA7 +#define CMD_WRITE_CONFIG 0xA8 +#define CMD_DATA_ERASE 0xA9 +#define CMD_DATA_PROGRAM 0xAA +#define CMD_DATA_READ 0xAB +#define CMD_WRITE_OTP 0xC3 +#define CMD_READ_OTP 0xC4 +#define CMD_SET_BAUD 0xC5 + +typedef struct{ + uint8_t id; + uint8_t type; + uint8_t uid[8]; + uint16_t bootloader_ver; + uint8_t xor_key[8]; + uint32_t optionbytes[3]; + wch_if_t port; + char *name; +}isp_dev; +typedef isp_dev* isp_dev_t; + +//read id, type +static char isp_cmd_identify(isp_dev_t dev); +//read (uid | bootloader_ver | user option bytes) by cfgmask +static size_t isp_cmd_read_config(isp_dev_t dev, uint16_t cfgmask, size_t len, uint8_t data[]); +// +static void isp_cmd_write_config(isp_dev_t dev, uint16_t cfgmask, size_t len, uint8_t data[]); +//set ISP_KEY (source fot XOR_KEY) +static void isp_cmd_isp_key(isp_dev_t dev, size_t len, uint8_t *key, uint8_t *sum); +//exit (if 'reason' == 1 then reset) +static void isp_cmd_isp_end(isp_dev_t dev, uint8_t reason); +// +static char isp_cmd_erase(isp_dev_t dev, uint32_t sectors); +// +static size_t isp_cmd_bulk(isp_dev_t dev, uint8_t cmd, uint32_t addr, size_t len, uint8_t *data); + + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +// ISP commands +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +static char isp_cmd_identify(isp_dev_t dev){ + if(dev->port == NULL){fprintf(stderr, "isp_cmd_identify: wch_if is NULL\n"); return 0;} + uint8_t buf[] = "\0\0MCU ISP & WCH.CN"; + dev->port->send(dev->port, CMD_IDENTIFY, sizeof(buf)-1, buf); + char res = dev->port->recv(dev->port, CMD_IDENTIFY, 2, buf); + dev->id = buf[0]; + dev->type = buf[1]; + return res; +} + +static size_t isp_cmd_read_config(isp_dev_t dev, uint16_t cfgmask, size_t len, uint8_t data[]){ + uint8_t buf[60]; + uint16_t retmask; + size_t res; + + buf[0] = (cfgmask >> 0) & 0xFF; + buf[1] = (cfgmask >> 8) & 0xFF; + + dev->port->send(dev->port, CMD_READ_CONFIG, 2, buf); + res = dev->port->recv(dev->port, CMD_READ_CONFIG, sizeof(buf), buf); + + if(res < 2){ + fprintf(stderr, "isp_cmd_read_config fail: not received enough bytes\n"); + return 0; + } + retmask = (uint16_t)buf[1]<<8 | buf[0]; + if(cfgmask != retmask){ + fprintf(stderr, "isp_cmd_read_config fail: received conf does not match\n"); + return 0; + } + if(res-2 < len)len = res-2; + if(data)memcpy(data, &buf[2], len); + return len; +} + +static void isp_cmd_write_config(isp_dev_t dev, uint16_t cfgmask, size_t len, uint8_t data[]){ + uint8_t buf[64]; + buf[0] = (cfgmask >> 0) & 0xFF; + buf[1] = (cfgmask >> 8) & 0xFF; + if(len > dev->port->maxdatasize-3)len = dev->port->maxdatasize-3; + memcpy(&buf[2], data, len); + dev->port->send(dev->port, CMD_WRITE_CONFIG, len+2, buf); + dev->port->recv(dev->port, CMD_WRITE_CONFIG, 2, buf); +} + +static void isp_cmd_isp_key(isp_dev_t dev, size_t len, uint8_t *key, uint8_t *sum){ + uint8_t buf[2]; + uint8_t uid_sum = 0; + if(len < 29){fprintf(stderr, "isp_cmd_isp_key: key must be at least 29 bytes\n"); return;} + + dev->port->send(dev->port, CMD_ISP_KEY, len, key); + dev->port->recv(dev->port, CMD_ISP_KEY, 2, buf); + if(sum)*sum = buf[0]; + + for(int i=0; i<8; i++)uid_sum += dev->uid[i]; + + //XOR key generation algorythm. At least fot bootloader ver 02.09 + dev->xor_key[0] = uid_sum ^ key[len / 7 * 4]; + dev->xor_key[1] = uid_sum ^ key[len / 5 * 1]; + dev->xor_key[2] = uid_sum ^ key[len / 7 * 1]; + dev->xor_key[3] = uid_sum ^ key[len / 7 * 6]; + dev->xor_key[4] = uid_sum ^ key[len / 7 * 3]; + dev->xor_key[5] = uid_sum ^ key[len / 5 * 3]; + dev->xor_key[6] = uid_sum ^ key[len / 7 * 5]; + dev->xor_key[7] = dev->id + dev->xor_key[0]; +} + +static void isp_cmd_isp_end(isp_dev_t dev, uint8_t reason){ + uint8_t buf[2]; + dev->port->send(dev->port, CMD_ISP_END, sizeof(reason), &reason); + dev->port->recv(dev->port, CMD_ISP_END, sizeof(buf), buf); +} + +static char isp_cmd_erase(isp_dev_t dev, uint32_t sectors){ + uint8_t buf[64]; + buf[0] = (sectors >> 0) & 0xff; + buf[1] = (sectors >> 8) & 0xff; + buf[2] = (sectors >> 16) & 0xff; + buf[3] = (sectors >> 24) & 0xff; + + dev->port->send(dev->port, CMD_ERASE, 4, buf); + dev->port->recv(dev->port, CMD_ERASE, 2, buf); + + if(buf[0] != 0 || buf[1] != 0){ + fprintf(stderr, "isp_cmd_erase: failed: %.2X %.2X\n", buf[0], buf[1]); + return 0; + } + return 1; +} + +static size_t isp_cmd_bulk(isp_dev_t dev, uint8_t cmd, uint32_t addr, size_t len, uint8_t data[]){ + uint8_t buf[64]; + if(len > dev->port->maxdatasize-5){ + len = dev->port->maxdatasize-5; + } + buf[0] = (addr >> 0) & 0xff; + buf[1] = (addr >> 8) & 0xff; + buf[2] = (addr >> 16) & 0xff; + buf[3] = (addr >> 24) & 0xff; + buf[4] = 0; //padding (ignored by bootloader) + + for(int i=0; ixor_key[i % 8]; + //TODO: test + size_t align = (len + 7)&~7; + if(len != align){ + for(int i=len; ixor_key[i % 8]; + //printf("align %zu -> %zu\n", len, align); + len = align; + } + + + dev->port->send(dev->port, cmd, len+5, buf); + dev->port->recv(dev->port, cmd, 2, buf); + + if(buf[0] != 0 || buf[1] != 0){ + if(cmd == CMD_VERIFY)fprintf(stderr, "isp_cmd_verify"); + else if(cmd == CMD_PROGRAM)fprintf(stderr, "isp_cmd_program"); + else fprintf(stderr, "isp_cmd_unknown (%.2X)", cmd); + fprintf(stderr, ": failed at address 0x%.8X\n", addr); + return 0; + } + return len; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +// Read file +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// + +size_t flash_align(size_t x){ + //return (x + 63) &~ 63; + return x; +} + +static void file_read_bin(const char name[], size_t *sz, uint8_t **data){ + int res; + size_t len, size; + uint8_t *buf = NULL; + FILE *pf = fopen(name, "rb"); + if(pf == NULL){fprintf(stderr, "file_read_bin, open [%s]: %s\n", name, strerror(errno)); return;} + res = fseek(pf, 0, SEEK_END); + if(res == -1){fprintf(stderr, "file_read_bin, fseek: %s\n", strerror(errno)); return;} + len = ftell(pf); + res = fseek(pf, 0, SEEK_SET); + if(res == -1){fprintf(stderr, "file_read_bin, fseek: %s\n", strerror(errno)); return;} + + size = flash_align(len); + + buf = malloc(len); + if(buf == NULL){fprintf(stderr, "file_read_bin, malloc %d bytes\n", (int)len); return;} + size_t rdbytes = fread(buf, 1, len, pf); + if(rdbytes != len){ + fprintf(stderr, "file_read_bin, read %d bytes (exp. %d)\n", (int)rdbytes, (int)len); + } + fclose(pf); + memset(&buf[rdbytes], 0, (size-rdbytes)); + *sz = size; + *data = buf; +} + +static void file_read_hex(const char name[], size_t *sz, uint8_t **data){ + size_t size = 0; + uint8_t *buf = NULL; + int res; + size_t maxaddr = 0; + int line = 0; + uint8_t linesize; + uint16_t lineaddr; + uint16_t newaddr; + uint8_t linecode; + uint8_t linedata; + uint8_t lrc; + FILE *pf = fopen(name, "rt"); + if(pf == NULL){fprintf(stderr, "file_read_hex, open [%s]: %s\n", name, strerror(errno)); return;} + while(1){ + line++; + res = fscanf(pf, ":%2hhX%4hX%2hhX", &linesize, &lineaddr, &linecode); + if(res != 3)break; + if(linecode != 0)continue; + lrc = linesize; + lrc += (lineaddr >> 0) & 0xFF; + lrc += (lineaddr >> 8) & 0xFF; + newaddr = lineaddr + linesize; + //realloc + if(newaddr > size)size = newaddr; + if(newaddr > maxaddr){ + size_t maxaddr_prev = maxaddr; + uint8_t *buf_prev = buf; + maxaddr = (newaddr + 1023) &~1023; //round up to 1k + buf = realloc(buf_prev, maxaddr); + if(buf == NULL){ + if(buf_prev)free(buf_prev); + *sz = 0; + *data = NULL; + return; + } + memset(&buf[maxaddr_prev], 0, (maxaddr - maxaddr_prev)); + } + //read data + for(int i=0; ibootloader_ver = (uint16_t)buf[1]<<8 | buf[2]; + memcpy(dev->uid, &buf[4], 8); + return 1; +} + +static char dev_read_options(isp_dev_t dev){ + uint8_t buf[12]; + size_t len; + len = isp_cmd_read_config(dev, CFG_MASK_RDPR_USER_DATA_WPR, sizeof(buf), buf); + if(len != 12){fprintf(stderr, "Error reading info: not received enough bytes\n"); return 0;} + //printf("DEBUG optionbytes: "); for(int i=0; i<12; i++)printf("%.2X ", buf[i]); printf("\n"); + + dev->optionbytes[0] = ((uint32_t)buf[0] << 0 ) | ((uint32_t)buf[1] << 8 ) | + ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24); + + dev->optionbytes[1] = ((uint32_t)buf[4] << 0 ) | ((uint32_t)buf[5] << 8 ) | + ((uint32_t)buf[6] << 16) | ((uint32_t)buf[7] << 24); + + dev->optionbytes[2] = ((uint32_t)buf[8] << 0 ) | ((uint32_t)buf[9] << 8 ) | + ((uint32_t)buf[10]<< 16) | ((uint32_t)buf[11]<< 24); + return 1; +} + +void progressbar(char comment[], float per){ + if(per>100)per=100; + printf("\r%s%2.1f %% ", comment, per); + fflush(stdout); +} + +static char do_erase(isp_dev_t dev, size_t size){ + size = (size + 1023) / 1024; + //if(size < 8)size = 8; + printf("Erase %zu sectors (%zu bytes)\n", size, size*1024); + return isp_cmd_erase(dev, size); +} + + +#define PIN_NONE 0 +#define PIN_RTS 1 +#define PIN_DTR 2 +#define PIN_nRTS 3 +#define PIN_nDTR 4 + +#define COMMAND_ERR 15 +#define COMMAND_NONE 0 +#define COMMAND_WRITE 1 +#define COMMAND_VERIFY 2 +#define COMMAND_ERASE 3 +#define COMMAND_UNLOCK 4 +#define COMMAND_INFO 5 +#define COMMAND_OPTION 6 +#define COMMAND_OPTSHOW 7 + +struct{ + unsigned int pin_rst:3; + unsigned int pin_boot:3; + unsigned int cmd:4; + unsigned int noverify:1; + unsigned int progress:1; + unsigned int fin_reset:1; + unsigned int db_ignore:1; + unsigned int ignore_flash_size:1; + unsigned int ignore_flash_total:1; +}run_flags = { + .pin_rst = PIN_NONE, + .pin_boot= PIN_NONE, + .cmd = COMMAND_NONE, + .noverify = 0, + .progress = 0, + .fin_reset = 0, + .db_ignore = 0, + .ignore_flash_size = 0, + .ignore_flash_total = 0, +}; + +static char do_bulk(isp_dev_t dev, uint8_t cmd, uint32_t addr, size_t size, uint8_t data[]){ + char *name; + ssize_t sz = size; + size_t count = 0; + size_t res; + + if(cmd == CMD_VERIFY)name = "Verify: "; + else if(cmd == CMD_PROGRAM)name = "Write: "; + else{fprintf(stderr, "do_bulk.cmd must be either CMD_PROGRAM or CMD_VERIFY\n"); return 0;} + + while(sz>0){ + count = sz; + res = isp_cmd_bulk(dev, cmd, addr, count, data); + if(res == 0)return 0; + addr += res; + data += res; + sz -= res; + if(run_flags.progress)progressbar(name, 100.0 - 100.0*sz/size); + } + if(cmd == CMD_PROGRAM)isp_cmd_bulk(dev, cmd, addr, 0, NULL); + return 1; +} + +void command_info(isp_dev_t dev, wch_info_t *info){ + printf("Device %.2X %.2X, UID=", dev->id, dev->type); + for(int i=0; i<7; i++)printf("%.2X-", dev->uid[i]); + printf("%.2X\n", dev->uid[7]); + printf("Bootloader v.%.4X\n", dev->bootloader_ver); + if(!dev_read_options(dev))return; + if(info){ + wch_info_regs_import(info, dev->optionbytes, sizeof(dev->optionbytes)/sizeof(dev->optionbytes[0])); + wch_info_show(info); + } +} + +void pin_rst_ctl(wch_if_t self, char state){ + switch(run_flags.pin_rst){ + case PIN_RTS: wch_if_uart_rts(self, state); break; + case PIN_DTR: wch_if_uart_dtr(self, state); break; + case PIN_nRTS:wch_if_uart_rts(self, !state); break; + case PIN_nDTR:wch_if_uart_dtr(self, !state); break; + } +} + +void pin_boot_ctl(wch_if_t self, char state){ + switch(run_flags.pin_boot){ + case PIN_RTS: wch_if_uart_rts(self, state); break; + case PIN_DTR: wch_if_uart_dtr(self, state); break; + case PIN_nRTS:wch_if_uart_rts(self, !state); break; + case PIN_nDTR:wch_if_uart_dtr(self, !state); break; + } +} + +static void rtsdtr_release(wch_if_t self){ + if(self->close != wch_if_close_uart)return; + pin_boot_ctl(self, 0); + pin_rst_ctl(self, 0); + usleep(100000); + pin_rst_ctl(self, 1); +} + +static char match_rtsdtr(wch_if_t self){ + if(self->close != wch_if_close_uart)return 1; + pin_boot_ctl(self, 1); + pin_rst_ctl(self, 0); + usleep(100000); + pin_rst_ctl(self, 1); + usleep(100000); + pin_boot_ctl(self, 0); + return 1; +} + +char *database_path = DATABASE_PATH; +char *dev_uid = NULL; + +static char match_dev_list(wch_if_t self){ + isp_dev dev = {.id = 0, .type = 0}; + char *dev_name = NULL; + dev.port = self; + match_rtsdtr(self); + isp_cmd_identify(&dev); + if(dev.id == 0 || dev.type == 0)return 0; + dev_readinfo(&dev); + + wch_info_t *info = NULL; + if(!run_flags.db_ignore){ + info = wch_info_read_dir(database_path, 1, dev.type, dev.id); + if(info == NULL){ + fprintf(stderr, "Could not find the device [0x%.2X 0x%.2X] in databse\n", dev.type ,dev.id); + } + if(info->name)dev_name = info->name; + } + + printf("found 0x%.2X 0x%.2X", dev.type, dev.id); + if(dev_name)printf(" ( %s )", dev_name); + printf(", bt ver.%.4X uid = [ ", dev.bootloader_ver); + + for(int i=0; i<7; i++)printf("%.2X-", dev.uid[i]); + printf("%.2X ]\n", dev.uid[7]); + wch_info_free(&info); + return 0; +} +static char match_dev_uid(wch_if_t self){ + static uint8_t exp_uid[8] = {0}; + if(self == NULL){ + sscanf(dev_uid, "%2hhX-%2hhX-%2hhX-%2hhX-%2hhX-%2hhX-%2hhX-%2hhX", + &exp_uid[0], &exp_uid[1], &exp_uid[2], &exp_uid[3], + &exp_uid[4], &exp_uid[5], &exp_uid[6], &exp_uid[7]); + return 0; + } + isp_dev dev = {.id = 0, .type = 0}; + dev.port = self; + match_rtsdtr(self); + isp_cmd_identify(&dev); + if(dev.id == 0 || dev.type == 0)return 0; + dev_readinfo(&dev); + + return (memcmp(exp_uid, dev.uid, 8) == 0); +} + +char* dbg_decode_cmd(uint8_t cmd){ + static char* cmd_A[] = {"IDENTIFY", "ISP_END", "ISP_KEY", "ERASE", "PROGRAM", "VERIFY", "READ_CONFIG", "WRITE_CONFIG", "DATA_ERASE", "DATA_PROGRAM", "DATA_READ"}; + static char* cmd_C[] = {"WRITE_OTP", "READ_OTP", "SET_BAUD"}; + static char* cmd_err = "Unknown (0x00)"; + if( (cmd >= 0xA1)&&(cmd <= 0xAB) )return cmd_A[cmd - 0xA1]; + if( (cmd >= 0xC3)&&(cmd <= 0xC5) )return cmd_C[cmd - 0xC3]; + sprintf(cmd_err, "Unknown (0x%.2X)", cmd); + return cmd_err; +} + +void debug(wch_if_t interface, char comment[], char dir_send, uint16_t len, uint8_t buf[]){ + uint8_t *data = &buf[interface->dbg_content_start]; + uint16_t dsize; + if(dir_send){ + dsize = data[1] | (uint16_t)data[2]<<8; + }else{ + dsize = data[2] | (uint16_t)data[3]<<8; + } + printf("%s cmd %s (%.2x) len %.4x : ", comment, dbg_decode_cmd(data[0]), data[0], dsize); + dsize = len - interface->dbg_content_start - 4 + dir_send; + for(int i=0; i [OPTIONS] COMMAND [ARG]\n", name); + printf(" OPTIONS:\n"); + printf("\t-v Print version and exit\n"); + printf("\t-h, --help Show this help and exit\n"); + printf("\t--port='//./COM3' \t Specify port as COM-port '//./COM3'\n"); + printf("\t--reset=PIN Use PIN as RESET\n"); + printf("\t--boot0=PIN Use PIN as Boot0\n"); + printf("\t 'PIN' may be 'RTS', 'DTR', 'nRTS' or 'nDTR'\n"); + + printf("\n"); + printf(" COMMAND:\n"); + printf("\twrite FILE write file (.hex or .bin)\n"); + printf("\tverify FILE verify file (.hex ot .bin)\n"); +} + +void init_small(){ + run_flags.db_ignore = 1; +} +#endif + +char parse_baudrate(char *port_name, int idx, int argc, char** argv){ + uint32_t baud = 0; + if(strcasecmp(port_name, "usb")==0)return 0; + if(idx >= argc)return 0; + char *res; + baud = strtol(argv[idx], &res, 10); + if(res[0] == 0){ + port_speed = baud; + return 1; + }else{ + return 0; + } + return 1; +} + +#define StrEq(str, sample) (strncmp(str, sample, sizeof(sample)-1) == 0) +int main(int argc, char **argv){ + if(argc < 2){ help(argv[0]); return 0; } + for(int i=1; iname)!=0){ + printf("Connected device is [ %s ] instead of [ %s ]\n", info->name, dev_name); + run_flags.cmd = COMMAND_ERR; + } + } + + + if(run_flags.cmd == COMMAND_WRITE || run_flags.cmd == COMMAND_VERIFY){ + uint8_t *data = NULL; + size_t size = 0; + char res = 0; + + if(info != NULL){ + wch_regs_t *reg = NULL; + wch_bitfield_t *rdpr = NULL; + char res = dev_read_options(&dev); + if(!res){fprintf(stderr, "Reading option bytes from MCU: failed\n");} + wch_info_regs_import(info, dev.optionbytes, sizeof(dev.optionbytes)/sizeof(dev.optionbytes[0])); + reg = wch_bitfield_byname(info, "RDPR", &rdpr); + if(reg!=NULL && rdpr != NULL){ + uint32_t val = wch_bitfield_val(rdpr, reg->curval); + if(val != 0xA5)printf("\nWARNING: mcu locked by RDPR bits. Try to run 'unlock' command\n\n"); + } + } + + file_read(filename, &size, &data); + //printf("file size = %zu\n", size); + if(!run_flags.db_ignore){ + if(info->errflag || info->flash_size == 0){ + fprintf(stderr, "Error reading databse\n"); + free(data); + data = NULL; + run_flags.cmd = COMMAND_ERR; + } + if(size > info->flash_size){ + if((info->flash_total == 0)||(size > info->flash_total)){ + fprintf(stderr, "Firmware size (%zu kB) more then avaible flash (%zu kB)\n", (size_t)(size+1023)/1024, (size_t)(info->flash_size + 1023)/1024); + if(!run_flags.ignore_flash_size && !run_flags.ignore_flash_total){ + free(data); + data = NULL; + run_flags.cmd = COMMAND_ERR; + } + }else{ + fprintf(stderr, "WARNING: Firmware size (%zu kB) more then cached flash (%zu kB)\n", (size_t)(size+1023)/1024, (size_t)(info->flash_size + 1023)/1024); + fprintf(stderr, " use flag '-f' to ignore\n"); + if(!run_flags.ignore_flash_total){ + free(data); + data = NULL; + run_flags.cmd = COMMAND_ERR; + } + } + } + } + + if(data != NULL){ + uint8_t isp_key[30] = {0}; + isp_cmd_isp_key(&dev, sizeof(isp_key), isp_key, NULL); + + if(run_flags.cmd == COMMAND_WRITE){ + res = do_erase(&dev, size); + if(!res){ + printf("Erase: FAIL\n"); + size = 0; + } + res = do_bulk(&dev, CMD_PROGRAM, writeaddr, size, data); + if(!res){ + printf("Write: FAIL\n"); + }else{ + printf("Write %zu bytes: DONE\n", size); + if(!run_flags.noverify)run_flags.cmd = COMMAND_VERIFY; + } + } + + if(run_flags.cmd == COMMAND_VERIFY){ + res = do_bulk(&dev, CMD_VERIFY, writeaddr, size, data); + if(!res){ + printf("Verify: FAIL\n"); + }else{ + printf("Verify %zu bytes: DONE\n", size); + } + } + free(data); + } + } //COMMAND_WRITE, COMMAND_VERIFY + + if(run_flags.cmd == COMMAND_ERASE){ + if(info == NULL || info->errflag || info->flash_size == 0){ + fprintf(stderr, "Reading database failed but it necessary for correct execution of the command\n"); + }else{ + char res = do_erase(&dev, info->flash_size); + if(!res){ + printf("Erase: FAIL\n"); + } + } + } + + if(run_flags.cmd == COMMAND_INFO)command_info(&dev, info); + + do{ + if(run_flags.cmd == COMMAND_OPTION || run_flags.cmd == COMMAND_OPTSHOW || run_flags.cmd == COMMAND_UNLOCK){ + if(info == NULL || info->errflag){ + fprintf(stderr, "Reading database failed but it necessary for correct execution of the command\n"); + break; + } + //printf("optionstr = [%s]\n", optionstr); + + char res = dev_read_options(&dev); + if(!res){fprintf(stderr, "Reading option bytes from MCU: failed\n"); break;} + wch_info_regs_import(info, dev.optionbytes, sizeof(dev.optionbytes)/sizeof(dev.optionbytes[0])); + + if(run_flags.cmd == COMMAND_UNLOCK){ + optionstr = NULL; + wch_regs_t *reg = NULL; + wch_bitfield_t *bits = NULL; + char res = dev_read_options(&dev); + if(!res){fprintf(stderr, "Reading option bytes from MCU: failed\n"); break;} + wch_info_regs_import(info, dev.optionbytes, sizeof(dev.optionbytes)/sizeof(dev.optionbytes[0])); + //Unlock via RDPR (set to 0xA5) + reg = wch_bitfield_byname(info, "RDPR", &bits); + if(reg!=NULL && bits != NULL){ + uint32_t val = wch_bitfield_val(bits, reg->curval); + if(val == 0xA5){printf("Device is already unlocked; Do nothing\n"); break;} + optionstr = "RDPR=0xA5"; + } + //Unlock via CFG_ROM_READ (set to 1) + reg = wch_bitfield_byname(info, "CFG_ROM_READ", &bits); + if(reg!=NULL && bits != NULL){ + uint32_t val = wch_bitfield_val(bits, reg->curval); + if(val == 1){printf("Device is already unlocked; Do nothing\n"); break;} + optionstr = "CFG_ROM_READ=1"; + } + + if(optionstr == NULL){ + printf("The unlocking method is unknown for this device; Do nothing\n"); + break; + } + } + + res = wch_info_modify(info, optionstr); + if(!res){fprintf(stderr, "Can not apply command '%s' to optionbytes\n", optionstr); break;} + + if(run_flags.cmd == COMMAND_OPTSHOW){ + wch_info_show(info); + }else{ + uint32_t regs[3]; + uint8_t data[12]; + wch_info_regs_export(info, regs, sizeof(regs)/sizeof(regs[0])); + data[0] = (regs[0] >> 0) & 0xFF; data[1] = (regs[0] >> 8) & 0xFF; + data[2] = (regs[0] >>16) & 0xFF; data[3] = (regs[0] >>24) & 0xFF; + data[4] = (regs[1] >> 0) & 0xFF; data[5] = (regs[1] >> 8) & 0xFF; + data[6] = (regs[1] >>16) & 0xFF; data[7] = (regs[1] >>24) & 0xFF; + data[8] = (regs[2] >> 0) & 0xFF; data[9] = (regs[2] >> 8) & 0xFF; + data[10]= (regs[2] >>16) & 0xFF; data[11]= (regs[2] >>24) & 0xFF; + //printf("DEBUG result : "); for(int i=0; i<12; i++)printf("%.2X ", data[i]); printf("\n"); + isp_cmd_write_config(&dev, CFG_MASK_RDPR_USER_DATA_WPR, sizeof(data), data); + printf("Option bytes write:\n 0x%.8X\n 0x%.8X\n 0x%.8X\nDone\n", regs[0], regs[1], regs[2]); + } + } + }while(0); + + if(run_flags.fin_reset)isp_cmd_isp_end(&dev, 1); + + wch_info_free(&info); + + rtsdtr_release(dev.port); + wch_if_close(&dev.port); +} diff --git a/wch-isp.1 b/wch-isp.1 deleted file mode 100644 index 65cd28d..0000000 --- a/wch-isp.1 +++ /dev/null @@ -1,118 +0,0 @@ -.TH WCH-ISP 1 wch-isp\-VERSION -.SH NAME -wch-isp \- firmware programmer for WCH microcontrollers -.SH SYNOPSIS -.B wch-isp -.RI [ OPTIONS ] -.I COMMAND -.RI [ ARG ...] -.P -.B wch-isp -.RB [ \-VDnpr ] -.RB [ \-d -.BR ] -.RB [ flash | write | verify | reset ] -.I FILE -.P -.B wch-isp -.RB [ \-VDnpr ] -.RB [ \-d -.BR ] -.RB [ erase | config | remove-wp ] -.P -.B wch-isp -.RB [ \-VDnpr ] -.RB [ \-d -.BR ] -.B list -.SH DESCRIPTION -.B wch-isp -is an utility to write firmware into the flash of WCH microcontrollers, over USB. -By default the flash content is verified after writing. -.SH OPTIONS -.TP -.B \-d -Select the USB device that matches the uid. -.TP -.B \-n -Do not verify flash content after writing, verification is done by default. -.TP -.B \-p -Prints a progress bar during command operation. -.TP -.B \-r -Reset the microcontroller after the command is completed. -.TP -.B \-D -Enable debug information, print raw isp commands sent over USB. -.TP -.B \-V -Prints version information to stdout, then exits. -.SH COMMANDS -.TP -.B list -List the currently detected compatible USB devices. -.TP -.BI write " FILE" -Write -.I FILE -into flash, this will automatically erase sufficient flash sectors before writing to it. -.TP -.BI flash " FILE" -Write -.I FILE -into flash, same as write. -.TP -.BI verify " FILE" -Verify -.I FILE -content against the flash content. -.TP -.B reset -Reset the microcontroller and jump to code flash. -.TP -.B erase -Erase all the flash content. -.TP -.B config -Print a memory dump of the chip configuration. -.TP -.B remove-wp -Disable flash write protection (modifies the chip configuration). -.SH EXAMPLES -.PP -List detected devices: -.PP -.in +4n -.EX -.RB "$ " "wch-isp list" -0: BTVER v2.7 UID 8d-ff-ba-e4-c2-84-09-69 [0x1069] CH569 -1: BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 -.EE -.in -.PP -Flash the -.B firmware.bin -file, -.B \-p -enable the progress bar. -.PP -.in +4n -.EX -.RB "$ " "wch-isp -p flash firmware.bin" -BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 -[####################################################] write 35392/35392 -[####################################################] verify 35392/35392 -flash done -.EE -.in -.PP -Erase the device's flash, select the device by it's uid (option -.BR \-d ). -.PP -.in +4n -.EX -.RB "$ " "wch-isp -d f2-3e-88-26-3b-38-b5-9d erase" -BTVER v2.5 UID f2-3e-88-26-3b-38-b5-9d [0x1980] CH32V208WBU6 -erase done - diff --git a/wch-isp.c b/wch-isp.c deleted file mode 100644 index 025b99f..0000000 --- a/wch-isp.c +++ /dev/null @@ -1,961 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* SPDX-FileCopyrightText: 2022 Jules Maselbas */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arg.h" - -#define __noreturn __attribute__((noreturn)) -#define __unused __attribute__((unused)) -#define __printf __attribute__((format(printf,1,2))) - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; - -#define BIT(x) (1UL << (x)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define LEN(a) (sizeof(a) / sizeof(*a)) -#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) - -struct isp_dev; -static int ch56x_show_conf(struct isp_dev *dev, size_t len, u8 *cfg); -#include "devices.h" - -#define MAX_PACKET_SIZE 64 -#define SECTOR_SIZE 1024 - -/* - * All readable and writable registers. - * - `RDPR`: Read Protection - * - `USER`: User Config Byte (normally in Register Map datasheet) - * - `WPR`: Write Protection Mask, 1=unprotected, 0=protected - * - * | BYTE0 | BYTE1 | BYTE2 | BYTE3 | - * |--------|--------|--------|--------| - * | RDPR | nRDPR | USER | nUSER | - * | DATA0 | nDATA0 | DATA1 | nDATA1 | - * | WPR0 | WPR1 | WPR2 | WPR3 | - */ -#define CFG_MASK_RDPR_USER_DATA_WPR 0x07 -#define CFG_MASK_BTVER 0x08 /* Bootloader version, in the format of `[0x00, major, minor, 0x00]` */ -#define CFG_MASK_UID 0x10 /* Device Unique ID */ -#define CFG_MASK_ALL 0x1f /* All mask bits of CFGs */ - -#define CMD_IDENTIFY 0xa1 -#define CMD_ISP_END 0xa2 -#define CMD_ISP_KEY 0xa3 -#define CMD_ERASE 0xa4 -#define CMD_PROGRAM 0xa5 -#define CMD_VERIFY 0xa6 -#define CMD_READ_CONFIG 0xa7 -#define CMD_WRITE_CONFIG 0xa8 -#define CMD_DATA_ERASE 0xa9 -#define CMD_DATA_PROGRAM 0xaa -#define CMD_DATA_READ 0xab -#define CMD_WRITE_OTP 0xc3 -#define CMD_READ_OTP 0xc4 -#define CMD_SET_BAUD 0xc5 - -#define ISP_VID 0x4348 -#define ISP_PID 0x55e0 -#define ISP_EP_OUT (2 | LIBUSB_ENDPOINT_OUT) -#define ISP_EP_IN (2 | LIBUSB_ENDPOINT_IN) - -struct isp_dev { - u8 id; - u8 type; - u8 uid[8]; - char uid_str[3 * 8]; - u16 btver; - u8 xor_key[8]; - libusb_device_handle *usb_dev; - unsigned int kernel; - /* info filled from db */ - const struct db *db; /* device family */ - const char *name; - u32 flash_size; - u32 eeprom_size; - u32 flash_sector_size; -}; -static struct isp_dev *dev_list; -static size_t dev_count; - -static libusb_context *usb; -static u8 isp_key[30]; /* all zero key */ - -static int dbg_enable; -static int do_progress; -static int do_reset; -static int do_verify = 1; -static const char *do_match; - -__noreturn static void die(const char *errstr, ...) __printf; -__noreturn static void version(void); -__noreturn static void usage(int help); -static void *xcalloc(size_t nmemb, size_t size); - -static void usb_init(void); -static void usb_fini(void); -static void usb_open(struct isp_dev *dev, libusb_device *usb_dev); -static void usb_close(struct isp_dev *dev); - -static void dbg_isp_cmd(const char *dir, u8 cmd, u16 len, const u8 *data); -static void isp_init(struct isp_dev *dev); -static void isp_key_init(struct isp_dev *dev); -static void isp_fini(struct isp_dev *dev); -static size_t isp_send_cmd(struct isp_dev *dev, u8 cmd, u16 len, u8 *data); -static size_t isp_recv_cmd(struct isp_dev *dev, u8 cmd, u16 len, u8 *data); - -static void -die(const char *errstr, ...) -{ - va_list ap; - - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - - exit(1); -} - -static void -dbg_isp_cmd(const char *dir, u8 cmd, u16 len, const u8 *data) -{ - u16 i; - - if (!dbg_enable) - return; - - fprintf(stderr, "isp %s cmd %.2x len %.4x : ", dir, cmd, len); - for (i = 0; i < len; i++) - fprintf(stderr, "%.2x", data[i]); - fprintf(stderr, "\n"); -} - -static void * -xcalloc(size_t nmemb, size_t size) -{ - void *p = calloc(nmemb, size); - if (p == NULL) - die("calloc: %s\n", strerror(errno)); - return p; -} - -static int -streq(const char *s1, const char *s2) -{ - return strcmp(s1, s2) == 0; -} - -static size_t -isp_send_cmd(struct isp_dev *dev, u8 cmd, u16 len, u8 *data) -{ - u8 buf[64]; - int ret, got = 0; - - if ((size_t)(len + 3) > sizeof(buf)) - die("isp_send_cmd: invalid argument, length %d\n", len); - - buf[0] = cmd; - /* length is sent in little endian... but it doesn't really matter - * as the usb maxpacket size is 64, thus len should never be greater - * than 61 (64 minus the 3 bytes header). */ - buf[1] = (len >> 0) & 0xff; - buf[2] = (len >> 8) & 0xff; - if (len != 0) - memcpy(&buf[3], data, len); - - dbg_isp_cmd("send", cmd, len, data); - - ret = libusb_bulk_transfer(dev->usb_dev, ISP_EP_OUT, buf, len + 3, &got, 10000); - if (ret) - die("isp_send_cmd: %s\n", libusb_strerror(ret)); - return got; -} - -static size_t -isp_recv_cmd(struct isp_dev *dev, u8 cmd, u16 len, u8 *data) -{ - u8 buf[64]; - int ret, got = 0; - u16 hdrlen; - - if ((size_t)(len + 4) > sizeof(buf)) - die("isp_recv_cmd: invalid argument, length %d\n", len); - - ret = libusb_bulk_transfer(dev->usb_dev, ISP_EP_IN, buf, len + 4, &got, 10000); - if (ret) - die("isp_recv_cmd: %s\n", libusb_strerror(ret)); - - if (got < 4) - die("isp_recv_cmd: not enough data recv\n"); - if (buf[0] != cmd) - die("isp_recv_cmd: got wrong command %#.x (exp %#.x)\n", buf[0], cmd); - if (buf[1]) - die("isp_recv_cmd: cmd error %#.x\n", buf[1]); - - got -= 4; - hdrlen = buf[2] | (buf[3] << 8); - if (hdrlen != got) - die("isp_recv_cmd: length mismatch, got %#.x (hdr %#.x)\n", got, hdrlen); - len = MIN(len, got); - - if (data != NULL) - memcpy(data, buf + 4, len); - - dbg_isp_cmd("recv", cmd, len, data); - - return got; -} - -static void -isp_cmd_identify(struct isp_dev *dev, u8 *dev_id, u8 *dev_type) -{ - u8 buf[] = "\0\0MCU ISP & WCH.CN"; - u8 ids[2]; - - buf[0] = 0; // dev_id - buf[1] = 0; // dev_type - /* do not send the terminating nul byte, hence sizeof(buf) - 1 */ - isp_send_cmd(dev, CMD_IDENTIFY, sizeof(buf) - 1, buf); - isp_recv_cmd(dev, CMD_IDENTIFY, sizeof(ids), ids); - - if (dev_id) - *dev_id = ids[0]; - if (dev_type) - *dev_type = ids[1]; -} - -static void -isp_cmd_isp_key(struct isp_dev *dev, size_t len, u8 *key, u8 *sum) -{ - u8 rsp[2]; - - isp_send_cmd(dev, CMD_ISP_KEY, len, key); - isp_recv_cmd(dev, CMD_ISP_KEY, sizeof(rsp), rsp); - if (sum) - *sum = rsp[0]; -} - -static void -isp_cmd_isp_end(struct isp_dev *dev, u8 reason) -{ - u8 buf[2]; - - isp_send_cmd(dev, CMD_ISP_END, sizeof(reason), &reason); - isp_recv_cmd(dev, CMD_ISP_END, sizeof(buf), buf); -} - -static void -isp_cmd_erase(struct isp_dev *dev, u32 sectors) -{ - u8 sec[4]; - u8 rsp[2]; - - sec[0] = (sectors >> 0) & 0xff; - sec[1] = (sectors >> 8) & 0xff; - sec[2] = (sectors >> 16) & 0xff; - sec[3] = (sectors >> 24) & 0xff; - - isp_send_cmd(dev, CMD_ERASE, sizeof(sec), sec); - isp_recv_cmd(dev, CMD_ERASE, 2, rsp); - - if (rsp[0] != 0 || rsp[1] != 0) - die("Fail to erase, error: %.2x %.2x\n", rsp[0], rsp[1]); -} - -static size_t -isp_cmd_program(struct isp_dev *dev, uint32_t addr, size_t len, const u8 *data, const u8 key[8]) -{ - u8 unk[61]; - u8 rsp[2]; - size_t i; - - unk[0] = (addr >> 0) & 0xff; - unk[1] = (addr >> 8) & 0xff; - unk[2] = (addr >> 16) & 0xff; - unk[3] = (addr >> 24) & 0xff; - unk[4] = 0; /* carefully choosen random number */ - - len = MIN(sizeof(unk) - 5, len); - for (i = 0; i < len; i++) - unk[5 + i] = data[i] ^ key[i % 8]; - - isp_send_cmd(dev, CMD_PROGRAM, len + 5, unk); - isp_recv_cmd(dev, CMD_PROGRAM, sizeof(rsp), rsp); - - if (rsp[0] != 0 || rsp[1] != 0) - die("Fail to program chunk @ %#x error: %.2x %.2x\n", addr, rsp[0], rsp[1]); - - return len; -} - -static size_t -isp_cmd_verify(struct isp_dev *dev, uint32_t addr, size_t len, const u8 *data, const u8 key[8]) -{ - u8 unk[61]; - u8 rsp[2]; - size_t i; - - unk[0] = (addr >> 0) & 0xff; - unk[1] = (addr >> 8) & 0xff; - unk[2] = (addr >> 16) & 0xff; - unk[3] = (addr >> 24) & 0xff; - unk[4] = 0; /* carefully choosen random number */ - - len = MIN(sizeof(unk) - 5, len); - for (i = 0; i < len; i++) - unk[5 + i] = data[i] ^ key[i % 8]; - - isp_send_cmd(dev, CMD_VERIFY, len + 5, unk); - isp_recv_cmd(dev, CMD_VERIFY, sizeof(rsp), rsp); - - if (rsp[0] != 0 || rsp[1] != 0) - die("Fail to verify chunk @ %#x error: %.2x %.2x\n", addr, rsp[0], rsp[1]); - - return len; -} - -static size_t -isp_cmd_read_conf(struct isp_dev *dev, u16 cfgmask, size_t len, u8 *cfg) -{ - u8 buf[60]; - u8 req[2]; - u16 mask; - size_t got; - - req[0] = (cfgmask >> 0) & 0xff; - req[1] = (cfgmask >> 8) & 0xff; - - isp_send_cmd(dev, CMD_READ_CONFIG, sizeof(req), req); - got = isp_recv_cmd(dev, CMD_READ_CONFIG, sizeof(buf), buf); - if (got < 2) - die("read conf fail: not received enough bytes\n"); - mask = buf[0] | (buf[1] << 8); - if (cfgmask != mask) - die("read conf fail: received conf does not match\n"); - len = MIN(got - 2, len); - memcpy(cfg, &buf[2], len); - - return len; -} - -static void -isp_cmd_write_conf(struct isp_dev *dev, u16 cfgmask, size_t len, u8 *cfg) -{ - u8 req[60]; - u8 rsp[2]; - - req[0] = (cfgmask >> 0) & 0xff; - req[1] = (cfgmask >> 8) & 0xff; - - len = MIN(sizeof(req) - 2, len); - memcpy(&req[2], cfg, len); - - isp_send_cmd(dev, CMD_WRITE_CONFIG, len + 2, req); - isp_recv_cmd(dev, CMD_WRITE_CONFIG, sizeof(rsp), rsp); -} - -static u16 -read_btver(struct isp_dev *dev) -{ - u8 buf[4]; - size_t len; - - /* format: [0x00, major, minor, 0x00] */ - len = isp_cmd_read_conf(dev, CFG_MASK_BTVER, sizeof(buf), buf); - if (len != 4) - return 0xffff; - - return (buf[1] << 8) | buf[2]; -} - -static void -usb_init(void) -{ - struct libusb_device_descriptor desc; - ssize_t i, count; - libusb_device **list; - int err; - - err = libusb_init(&usb); - if (err) - die("libusb_init: %s\n", libusb_strerror(err)); - - count = libusb_get_device_list(usb, &list); - if (count < 0) - die("Fail to get a list of USB devices: %s\n", strerror(errno)); - - dev_list = xcalloc(count, sizeof(*dev_list)); - for (i = 0; i < count; i++) { - err = libusb_get_device_descriptor(list[i], &desc); - if (err < 0) - continue; - if (desc.idVendor == ISP_VID && desc.idProduct == ISP_PID) - usb_open(&dev_list[dev_count++], libusb_ref_device(list[i])); - } - - libusb_free_device_list(list, 1); -} - -static void -usb_open(struct isp_dev *dev, libusb_device *usb_dev) -{ - int err; - - err = libusb_open(usb_dev, &dev->usb_dev); - if (err < 0) - die("libusb_open: %s\n", libusb_strerror(err)); - - err = libusb_kernel_driver_active(dev->usb_dev, 0); - if (err == LIBUSB_ERROR_NOT_SUPPORTED) - err = 0; - if (err < 0) - die("libusb_kernel_driver_active: %s\n", libusb_strerror(err)); - dev->kernel = err; - if (dev->kernel == 1) - if (libusb_detach_kernel_driver(dev->usb_dev, 0)) - die("Couldn't detach kernel driver!\n"); - - err = libusb_claim_interface(dev->usb_dev, 0); - if (err) - die("libusb_claim_interface: %s\n", libusb_strerror(err)); -} - -static void -usb_close(struct isp_dev *dev) -{ - int err = 0; - - if (dev->usb_dev) - err = libusb_release_interface(dev->usb_dev, 0); - if (err) - fprintf(stderr, "libusb_release_interface: %s\n", libusb_strerror(err)); - if (dev->kernel == 1) - libusb_attach_kernel_driver(dev->usb_dev, 0); - - if (dev->usb_dev) - libusb_close(dev->usb_dev); -} - -static void -usb_fini(void) -{ - libusb_device *dev; - - while (dev_count > 0) { - dev_count--; - dev = libusb_get_device(dev_list[dev_count].usb_dev); - usb_close(&dev_list[dev_count]); - libusb_unref_device(dev); - } - if (dev_list) - free(dev_list); - - if (usb) - libusb_exit(usb); -} - -static size_t -db_flash_size(struct isp_dev *dev) -{ - return dev->flash_size; -} - -static size_t -db_flash_sector_size(struct isp_dev *dev) -{ - return dev->flash_sector_size; -} - -static void -isp_init_from_db(struct isp_dev *dev) -{ - const struct db *db = NULL; - const struct db_dev *db_dev = NULL; - size_t i; - - dev->flash_sector_size = SECTOR_SIZE; - dev->name = "unknown"; - dev->flash_size = 0xffff; - dev->eeprom_size = 0; - - for (i = 0; i < LEN(devices); i++) { - if (devices[i].type == dev->type) { - dev->db = db = &devices[i]; - break; - } - } - if (db) { - for (db_dev = db->devs; db_dev->id != 0; db_dev++) { - if (db_dev->id == dev->id) - break; - } - if (db_dev->id == 0) - db_dev = NULL; - } - - if (db) { - dev->flash_sector_size = db->flash_sector_size; - } - if (db_dev) { - dev->name = db_dev->name; - dev->flash_size = db_dev->flash_size; - dev->eeprom_size = db_dev->eeprom_size; - } -} - -static void -isp_init(struct isp_dev *dev) -{ - size_t i; - - /* get the device type and id */ - isp_cmd_identify(dev, &dev->id, &dev->type); - /* match the detected device */ - isp_init_from_db(dev); - /* get the bootloader version */ - dev->btver = read_btver(dev); - - /* get the device uid */ - isp_cmd_read_conf(dev, CFG_MASK_UID, sizeof(dev->uid), dev->uid); - - for (i = 0; i < sizeof(dev->uid); i++) { - snprintf(dev->uid_str + 3 * i, sizeof(dev->uid_str) - 3 * i, - "%.2x-", dev->uid[i]); - } -} - -static void -isp_key_init(struct isp_dev *dev) -{ - size_t i; - u8 sum; - u8 rsp; - - /* initialize xor_key */ - for (sum = 0, i = 0; i < sizeof(dev->uid); i++) - sum += dev->uid[i]; - memset(dev->xor_key, sum, sizeof(dev->xor_key)); - dev->xor_key[7] = dev->xor_key[0] + dev->id; - - /* send the isp key */ - isp_cmd_isp_key(dev, sizeof(isp_key), isp_key, &rsp); - - /* The bootloader send back a checksum of xor_key. This response is - * to make sure that we are in sync. */ - for (sum = 0, i = 0; i < sizeof(dev->xor_key); i++) - sum += dev->xor_key[i]; - - /* Workaround for CH56x family which reply with 0 - * (only tested on CH569) */ - if (dev->type == 0x10) - sum = 0; - - if (rsp != sum) - die("failed set isp key, wrong reply, got %x (exp %x)\n", rsp, sum); -} - -static void -progress_bar(const char *act, size_t current, size_t total) -{ - static char f[] = "####################################################"; - static char e[] = " "; - int l = sizeof(f) - 1; - int n = total > 0 ? (l * current) / total : (size_t)l; - - if (!do_progress) - return; - printf("\r[%.*s%.*s] %s %zu/%zu", n, f, l - n, e, act, current, total); - if (current == total) - puts(""); - fflush(stdout); -} - -static void -isp_flash(struct isp_dev *dev, size_t size, u8 *data) -{ - size_t sector_size = db_flash_sector_size(dev); - u32 nr_sectors = ALIGN(size, sector_size) / sector_size; - size_t off = 0; - size_t rem = size; - size_t len; - - isp_cmd_erase(dev, nr_sectors); - - while (off < size) { - progress_bar("write", off, size); - - len = isp_cmd_program(dev, off, rem, data + off, dev->xor_key); - off += len; - rem -= len; - } - isp_cmd_program(dev, off, 0, NULL, dev->xor_key); - progress_bar("write", size, size); -} - -static void -isp_verify(struct isp_dev *dev, size_t size, u8 *data) -{ - size_t off = 0; - size_t rem = size; - size_t len; - - while (off < size) { - progress_bar("verify", off, size); - - len = isp_cmd_verify(dev, off, rem, data + off, dev->xor_key); - off += len; - rem -= len; - } - progress_bar("verify", size, size); -} - -static void -isp_fini(struct isp_dev *dev) -{ - if (do_reset) - isp_cmd_isp_end(dev, 1); -} - -static void -file_read_all(const char *name, size_t *size_p, void **bin_p) -{ - FILE *f; - size_t len, size; - void *bin; - int ret; - - f = fopen(name, "rb"); - if (f == NULL) - die("%s: %s\n", name, strerror(errno)); - - ret = fseek(f, 0, SEEK_END); - if (ret == -1) - die("fseek: %s\n", strerror(errno)); - - len = ftell(f); - ret = fseek(f, 0, SEEK_SET); - if (ret == -1) - die("fseek: %s\n", strerror(errno)); - - /* binary image needs to be aligned to a 64 bytes boundary */ - size = ALIGN(len, 64); - bin = calloc(1, size); - if (bin == NULL) - die("calloc: %s\n", strerror(errno)); - - if (len > 0) { - ret = fread(bin, len, 1, f); - if (ret != 1) - die("fread: %s\n", strerror(errno)); - } - - fclose(f); - - *size_p = size; - *bin_p = bin; -} - -static void -cmd_write_flash(struct isp_dev *dev, int argc, char **argv) -{ - const char *name; - size_t size; - void *bin; - - if (argc < 2) - die("%s: missing file\n", argv[0]); - name = argv[1]; - - file_read_all(name, &size, &bin); - if (size > db_flash_size(dev)) - die("%s: file too big, flash size is %zd\n", name, db_flash_size(dev)); - - isp_flash(dev, size, bin); - if (do_verify) - isp_verify(dev, size, bin); - - free(bin); -} - -static void -cmd_verify_flash(struct isp_dev *dev, int argc, char **argv) -{ - const char *name; - size_t size; - void *bin; - - if (argc < 2) - die("%s: missing file\n", argv[0]); - name = argv[1]; - - file_read_all(name, &size, &bin); - if (size > db_flash_size(dev)) - die("%s: file too big, flash size is %zd\n", name, db_flash_size(dev)); - - isp_verify(dev, size, bin); - - free(bin); -} - -/** - * fmtb formating function to print a binary number to a char buf - * b the output buffer - * n the buffer size - * p the "precision", how many 'bit' will be printed - * v the value to be printed - */ -static char * -fmtb(char *b, size_t n, int p, u32 v) -{ - char *s = b + n; - - if ((size_t)p > 32) p = 32; - if ((size_t)p > n) p = n; - - *--s = '\0'; - for (; b < s && v ; v >>= 1, p--) - *--s = (v & 1) ? '1' : '0'; - while (b < s && p-- > 0) - *--s = '0'; - return s; -} - -static int -ch56x_show_conf(struct isp_dev *dev, size_t len, u8 *cfg) -{ - char buf[4]; - u32 nv; - - if (!(dev->type == 0x10 && dev->id == 0x69)) { - fprintf(stderr, "FIXME: config is only supported on CH569\n"); - return 1; - } - - if (len < 12) - die("config: invalid length\n"); - - nv = (cfg[8] << 0) | (cfg[9] << 8) | (cfg[10] << 16) | (cfg[11] << 24); - printf("[4] RESET_EN %d: %s\n", !!(nv & BIT(4)), - (nv & BIT(4)) ? "enabled" : "disabled"); - printf("[5] DEBUG_EN %d: %s\n", !!(nv & BIT(5)), - (nv & BIT(5)) ? "enabled" : "disabled"); - printf("[6] BOOT_EN %d: %s\n", !!(nv & BIT(6)), - (nv & BIT(6)) ? "enabled" : "disabled"); - printf("[7] CODE_READ_EN %d: %s\n", !!(nv & BIT(7)), - (nv & BIT(7)) ? "enabled" : "disabled"); - printf("[29] LOCKUP_RST_EN %d: %s\n", !!(nv & BIT(29)), - (nv & BIT(29)) ? "enabled" : "disabled"); - printf("[31:30] USER_MEM 0b%s: %s\n", - fmtb(buf, sizeof(buf), 2, (nv >> 30) & 0b11), - ((nv >> 30) & 0b11) == 0 ? "RAMX 32KB + ROM 96KB" : - ((nv >> 30) & 0b11) == 1 ? "RAMX 64KB + ROM 64KB" : - "RAMX 96KB + ROM 32KB"); - - return 0; -} - -static void -cmd_config_show(struct isp_dev *dev, __unused int argc, __unused char **argv) -{ - u8 cfg[16]; - size_t len, i; - int ret; - - len = isp_cmd_read_conf(dev, 0x7, sizeof(cfg), cfg); - - if (dev->db && dev->db->show_conf) { - ret = dev->db->show_conf(dev, len, cfg); - if (ret == 0) - return; - } - - for (i = 0; i < len; i++) - printf("%.2x%c", cfg[i], ((i % 4) == 3) ? '\n' : ' '); -} - -static void -cmd_remove_wp(struct isp_dev *dev, __unused int argc, __unused char **argv) -{ - u8 cfg[16]; - size_t len; - - len = isp_cmd_read_conf(dev, 0x7, sizeof(cfg), cfg); - if (cfg[0] == 0xa5) { - printf("write protection already off\n"); - } else { - cfg[0] = 0xa5; - isp_cmd_write_conf(dev, 0x7, len, cfg); - printf("write protection disabled\n"); - } -} - -static void -cmd_erase_all(struct isp_dev *dev, __unused int argc, __unused char **argv) -{ - size_t size = db_flash_size(dev); - size_t sector_size = db_flash_sector_size(dev); - u32 nr_sectors = size / sector_size; - isp_cmd_erase(dev, nr_sectors); -} - -static void -cmd_reset(struct isp_dev *dev, __unused int argc, __unused char **argv) -{ - isp_cmd_isp_end(dev, 1); -} - -static struct isp_dev * -dev_by_uid(const char *uid) -{ - size_t i; - - for (i = 0; i < dev_count; i++) - if (streq(uid, dev_list[i].uid_str)) - return &dev_list[i]; - - return NULL; -} - -char *argv0; - -static void -usage(int help) -{ - printf("usage: %s [-VDnpr] [-d ] COMMAND [ARG ...]\n", argv0); - printf(" %s [-VDnpr] [-d ] [flash|write|verify|reset] FILE\n", argv0); - printf(" %s [-VDnpr] [-d ] [erase|config|remove-wp]\n", argv0); - printf(" %s [-VDnpr] list\n", argv0); - if (!help) - exit(1); - - printf("options:\n"); - printf(" -d Select the usb device that matches the uid\n"); - printf(" -n No verify after writing to flash, done by default\n"); - printf(" -p Print a progress-bar during command operation\n"); - printf(" -r Reset after command completed\n"); - printf(" -D Print raw isp command (for debug)\n"); - printf(" -V Print version and exit\n"); - - exit(0); -} - -static void -version(void) -{ - printf("%s %s\n", argv0, VERSION); - exit(0); -} - -static const struct { - const char *name; - void (*func)(struct isp_dev *dev, int argc, char **argv); -} cmds[] = { - { "flash", cmd_write_flash }, - { "write", cmd_write_flash }, - { "verify", cmd_verify_flash }, - { "erase", cmd_erase_all }, - { "reset", cmd_reset }, - { "config", cmd_config_show }, - { "remove-wp", cmd_remove_wp }, -}; - -int -main(int argc, char **argv) -{ - struct isp_dev *dev; - size_t i; - - argv0 = argv[0]; - - ARGBEGIN { - case 'p': - do_progress = 1; - break; - case 'r': - do_reset = 1; - break; - case 'v': - do_verify = 1; - break; - case 'n': - do_verify = 0; - break; - case 'd': - do_match = EARGF(usage(0)); - break; - case 'D': - dbg_enable = 1; - break; - case 'V': - version(); - case 'h': - usage(1); - default: - usage(0); - } ARGLONG { - fprintf(stderr, "unknown option '%s'\n", ARGLC()); - usage(0); - } ARGEND; - - usb_init(); - - if (argc < 1) - die("missing command\n"); - - if (dev_count == 0) - die("no device detected\n"); - - for (i = 0; i < dev_count; i++) - isp_init(&dev_list[i]); - - if (streq(argv[0], "list")) { - for (i = 0; i < dev_count; i++) { - dev = &dev_list[i]; - printf("%zd: BTVER v%d.%d UID %s [0x%.2x%.2x] %s\n", i, - dev->btver >> 8, dev->btver & 0xff, - dev->uid_str, dev->type, dev->id, - dev->name); - } - goto out; - } - - /* by default select the first device */ - dev = &dev_list[0]; - if (do_match) { - dev = dev_by_uid(do_match); - if (!dev) - die("no device match for '%s'\n", do_match); - } - - printf("BTVER v%d.%d UID %s [0x%.2x%.2x] %s\n", - dev->btver >> 8, dev->btver & 0xff, - dev->uid_str, dev->type, dev->id, - dev->name); - isp_key_init(dev); - - for (i = 0; i < LEN(cmds); i++) { - if (streq(argv[0], cmds[i].name)) { - cmds[i].func(dev, argc, argv); - printf("%s done\n", cmds[i].name); - break; - } - } - if (i == LEN(cmds)) - die("%s: invalid command\n", argv[0]); - - isp_fini(dev); -out: - usb_fini(); - - return 0; -} diff --git a/wch-isp.man b/wch-isp.man new file mode 100644 index 0000000..7afd792 --- /dev/null +++ b/wch-isp.man @@ -0,0 +1,148 @@ +.TH WCH-ISP 1 wch-isp\-VERSION +.SH NAME +wch-isp \- firmware programmer for WCH microcontrollers +.SH SYNOPSIS +.B wch-isp +.RI [ OPTIONS ] +.I COMMAND +.RI [ ARG ...] +.SH DESCRIPTION +.B wch-isp +is an utility to write firmware into the flash of WCH microcontrollers, over USB or COM-port. +By default the flash content is verified after writing. +.SH OPTIONS +.TP +.B \-n +Do not verify flash content after writing, verification is done by default. +.TP +.B \-p +Prints a progress during command operation. +.TP +.B \-r +Reset the microcontroller after the command is completed. +.TP +.B \-b +Do not read database. +.TP +.B \-f +Ignore if firmware size more than cached flash size (program memory). +.TP +.B \-F +Ignore if firmware size more than total flash size (program memory + const data memory). +.TP +.B \-d +Enable debug information, print raw isp commands sent over USB or COM-port. +.TP +.B \-v +Prints version information to stdout, then exits. +.TP +.B \-h, --help +Prints help message to stdout, then exits. +.TP +.BI \--port= "port [baudrate]" +Specify \fIport\fR to connect the device. It may be \fBUSB\fR or \fB/dev/ttyUSB0\fR or \fB//./COM2\fR or something else. +.TP +.BI \--device= dev +Test if connected device is \fIdev\fR and exit if they differ. \fIdev\fR may be read by \fBinfo\fR option. +.RB Example:\ --device=CH32V203G8R6 +.TP +.BI \--uid= uid +Select the microcontroller that matches the \fIuid\fR. +.TP +.BI \--reset= number +Specify COM-port control pin \fInumber\fR as RESET signal. May be +.BR RTS ,\ DTR ,\ nRTS\ or\ nDTR . +.TP +.BI \--boot0= number +Specify COM-port control pin \fInumber\fR as Boot0 signal. May be +.BR RTS ,\ DTR ,\ nRTS\ or\ nDTR . +.TP +.BI \--address= addr +Start writing from +.IR addr . +.TP +.BI \--database-path= directory +Search device info in specified +.IR directory . +.SH COMMANDS +.TP +.B list +List the currently detected compatible devices. +.TP +.BI write " FILE" +Write +.I FILE +into flash, this will automatically erase sufficient flash sectors before writing to it. +.TP +.BI verify " FILE" +Verify +.I FILE +content against the flash content. +.TP +.B erase +Erase all the flash content (not implemented yet). +.TP +.B unlock +Disable flash write protection (modifies the chip configuration) (not implemented yet). +.TP +.B info +Print a device information and memory dump of the chip configuration. +.TP +.BI optionbytes " 'CMD'" +Change optionbytes. + Example: +.B ./wch-isp optionbytes 'RDPR=0xA5, DATA0 = 0x42' +.TP +.BI optionshow " 'CMD'" +Show changes after applying \fICMD\fR to optionbytes; do not write. +.SH EXAMPLES +.PP +List detected devices: +.PP +.in +4n +.EX +.RB "$ " "wch-isp list" +found 0x19 0x3B ( CH32V203G8R6 ), bt ver.0206 uid = [ CD-AB-1D-36-51-BC-3B-9E ] +found 0x17 0x71 ( CH32V307RCT6 ), bt ver.0209 uid = [ 87-80-CB-26-3B-38-8D-DF ] +.EE +.in +.PP +Flash the +.B firmware.bin +file via USB, +.B \-p +enable the progress bar. +.PP +.in +4n +.EX +.RB "$ " "./wch-isp --port=USB -p write firmware.bin" +Erase 1 sectors (1024 bytes) +Write: 100.0 % Write 792 bytes: DONE +Verify: 100.0 % Verify 792 bytes: DONE +.EE +.in +.PP +Verify the +.B firmware.bin +via COM-port (reset connected to RTS, Boot0 connected to DTR): +.PP +.in +4n +.EX +.RB "$ " "./wch-isp --port=/dev/ttyUSB0 --reset=RTS --boot0=DTR verify firmware.hex +Verify 792 bytes: DONE + +.EE +.in +.PP +Unlock read-protection and write 0x42 to DATA0 field in optionbytes: +.PP +.in +4n +.EX +.RB "$ " "./wch-isp --port=/dev/tty_CH32_PROG_0 9600 optionbytes 'RDPR=0xA5 DATA0 = 0x42'" +Option bytes write: + 0x609F5AA5 + 0xFF00FF42 + 0xFFFFFFFF +Done + + diff --git a/wch_if.h b/wch_if.h new file mode 100644 index 0000000..077fdec --- /dev/null +++ b/wch_if.h @@ -0,0 +1,32 @@ +#ifndef __WCH_INTERFACE_H__ +#define __WCH_INTERFACE_H__ +#include +#include + +struct wch_if; +typedef struct wch_if* wch_if_t; + +typedef void (*wch_if_debug)(wch_if_t interface, char comment[], char dir_send, uint16_t len, uint8_t buf[]); + +struct wch_if{ + uint16_t maxdatasize; + size_t (*send)(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]); + size_t (*recv)(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]); + void (*close)(wch_if_t *interface); + wch_if_debug debug; + size_t dbg_content_start; + void *intern; +}; + +typedef char (*wch_if_match)(wch_if_t); + +wch_if_t wch_if_open_usb( wch_if_match match_func, wch_if_debug debug_func ); +wch_if_t wch_if_open_uart(char portname[], uint32_t baudrate, wch_if_match match_func, wch_if_debug debug_func); +void wch_if_close_usb(wch_if_t *interface); +void wch_if_close_uart(wch_if_t *interface); +inline void wch_if_close( wch_if_t *interface ){if(*interface != NULL)(*interface)->close(interface);} + +void wch_if_uart_rts(wch_if_t interface, char state); +void wch_if_uart_dtr(wch_if_t interface, char state); + +#endif \ No newline at end of file diff --git a/wch_if_uart.c b/wch_if_uart.c new file mode 100644 index 0000000..e94e0b7 --- /dev/null +++ b/wch_if_uart.c @@ -0,0 +1,450 @@ +#include +#include +#include +#include "wch_if.h" + +#define UART_TIMEOUT_ms 1000 + +struct wch_if_uart; +typedef struct wch_if_uart* wch_if_uart_t; + +wch_if_uart_t wch_if_uart_open(char name[], unsigned int baudrate); +int wch_if_uart_close(wch_if_uart_t tty); +ssize_t wch_if_uart_write(wch_if_uart_t tty, void *buf, size_t count); +ssize_t wch_if_uart_read(wch_if_uart_t tty, void *buf, size_t count); +int wch_if_uart_timeout(wch_if_uart_t tty, ssize_t time_ms); + +static void uart_if_flush(wch_if_uart_t tty); + +static size_t uart_if_send(wch_if_t interf, uint8_t cmd, uint16_t len, uint8_t data[]); +static size_t uart_if_recv(wch_if_t interf, uint8_t cmd, uint16_t len, uint8_t data[]); +#define TTY ( (wch_if_uart_t)(interf->intern) ) + +#define CMD_SET_BAUD 0xC5 + +wch_if_t wch_if_open_uart(char portname[], uint32_t baudrate, wch_if_match match_func, wch_if_debug debug_func ){ + if(portname == NULL){fprintf(stderr, "portname = NULL\n"); return NULL;} +#warning if portname == NULL add function to list all avaible COM-ports and try to connect + + wch_if_t port = (wch_if_t)malloc(sizeof(struct wch_if)); + if(port == NULL)return NULL; + + wch_if_uart_t tty = wch_if_uart_open(portname, 115200); + if(tty == NULL){fprintf(stderr, "Can not open [%s]\n", portname); return NULL;} + wch_if_uart_timeout(tty, UART_TIMEOUT_ms); + uart_if_flush(tty); + + port->maxdatasize = 61; + port->send = uart_if_send; + port->recv = uart_if_recv; + port->close = wch_if_close_uart; + port->debug = debug_func; + port->dbg_content_start = 2; + port->intern = tty; + + if(match_func){ + if(!match_func(port))wch_if_close_uart(&port); + } + + if(baudrate != 0 && baudrate != 115200){ + uint8_t buf[4]; + buf[0] = (baudrate >> 0) & 0xFF; + buf[1] = (baudrate >> 8) & 0xFF; + buf[2] = (baudrate >>16) & 0xFF; + buf[3] = (baudrate >>24) & 0xFF; + uart_if_send(port, CMD_SET_BAUD, 4, buf); + uart_if_recv(port, CMD_SET_BAUD, 2, buf); + wch_if_uart_close(tty); + tty = wch_if_uart_open(portname, baudrate); + wch_if_uart_timeout(tty, UART_TIMEOUT_ms); + uart_if_flush(tty); + port->intern = tty; + } + + return port; +} + +static size_t uart_if_send(wch_if_t interf, uint8_t cmd, uint16_t len, uint8_t data[]){ + uint8_t buf[67]; + if(len > (sizeof(buf)-6)){ + fprintf(stderr, "uart_if_send: invalid argument, length %d\n", len); + return 0; + } + buf[0] = 0x57; + buf[1] = 0xAB; + buf[2] = cmd; + buf[3] = (len >> 0) & 0xFF; + buf[4] = (len >> 8) & 0xFF; + if(len > 0)memcpy(&buf[5], data, len); + uint8_t sum = 0; + for(int i=2; i<(len+5); i++)sum += buf[i]; + buf[len+5] = sum; + + if( interf->debug )interf->debug(interf, "uart_if_send", 1, len+6, buf); + + int res = wch_if_uart_write(TTY, buf, len+6); + if(res != (len+6)){fprintf(stderr, "uart_if_send error\n"); return 0;} + return res; +} + +// received buffer format: +// [0] = 0x55 +// [1] = 0xAA +// [2] = cmd +// [3] = 0 +// [4:5] = len +// [6 .. len+5] = data +// [len+6] = checksum +static size_t uart_if_recv(wch_if_t interf, uint8_t cmd, uint16_t len, uint8_t data[]){ + uint8_t buf[67]; + int res; + uint16_t datalen; + uint8_t sum = 0; + res = wch_if_uart_read(TTY, buf, 6); + if(res != 6){fprintf(stderr, "uart_if_recv: timeout\n"); return 0;} + datalen = (uint16_t)buf[5]<<8 | buf[4]; + if(datalen > (sizeof(buf)-7)){ + fprintf(stderr, "uart_if_recv: got wrong 'len' %i (more than %i)\n", datalen, (int)(sizeof(buf)-7)); + return 0; + } + + res = wch_if_uart_read(TTY, &buf[6], datalen+1); + if(res != (datalen+1)){fprintf(stderr, "uart_if_recv: timeout\n"); return 0;} + + if(buf[0] != 0x55 || buf[1] != 0xAA)printf("uart_if_recv: preamble: fail\n"); + + if(buf[2] != cmd){ + fprintf(stderr, "uart_if_recv: got wrong 'cmd' %.2X (exp %.2X)\n", buf[2], cmd); + return 0; + } + + for(int i=2; i<(datalen+6); i++)sum+=buf[i]; + if(buf[datalen+6] != sum)printf("uart_if_recv: checksum error: %.2X (exp. %.2X)\n", buf[datalen+6], sum); + + //if( interf->debug )interf->debug(interf, "uart_if_recv", 0, len+7, buf); + if( interf->debug )interf->debug(interf, "uart_if_recv", 0, res+6, buf); + + if(len > datalen)len = datalen; + if(data != NULL)memcpy(data, &buf[6], len); + return len; +} + +void wch_if_close_uart(wch_if_t *interf){ + wch_if_uart_t tty = ( (wch_if_uart_t)((*interf)->intern) ); + wch_if_uart_close(tty); + + free(*interf); + *interf = NULL; +} + +static void uart_if_flush(wch_if_uart_t tty){ + char ch; + ssize_t res; + wch_if_uart_timeout(tty, 100); + do{ + res = wch_if_uart_read(tty, &ch, 1); + }while(res > 0); + wch_if_uart_timeout(tty, UART_TIMEOUT_ms); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// +// TTY implementation +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(__APPLE__) || defined(__MACH__) +#include +#include +#include +#include +#include +#include +#include +#include + +struct wch_if_uart{ + int fd; + struct termios ttyset, oldset; +}; + +//I know it looks like a crutch, but I can’t guarantee that B9600 == 9600 +static speed_t speed_convert(unsigned int baudrate){ + switch(baudrate){ + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; +#ifdef B460800 + case 460800: return B460800; +#endif +#ifdef B500000 + case 500000: return B500000; +#endif +#ifdef B576000 + case 576000: return B576000; +#endif +#ifdef B921600 + case 921600: return B921600; +#endif +#ifdef B1000000 + case 1000000: return B1000000; +#endif +#ifdef B1152000 + case 1152000: return B1152000; +#endif +#ifdef B1500000 + case 1500000: return B1500000; +#endif +#ifdef B2000000 + case 2000000: return B2000000; +#endif +#ifdef B2500000 + case 2500000: return B2500000; +#endif +#ifdef B3000000 + case 3000000: return B3000000; +#endif +#ifdef B3500000 + case 3500000: return B3500000; +#endif +#ifdef B4000000 + case 4000000: return B4000000; +#endif + default: return B0; + } +} + +wch_if_uart_t wch_if_uart_open(char name[], unsigned int baudrate){ + speed_t speed = speed_convert(baudrate); + wch_if_uart_t res = (struct wch_if_uart*)malloc(sizeof(struct wch_if_uart)); + if(res == NULL)return res; + res->fd = open(name, O_RDWR | O_NOCTTY); + if(res->fd == -1){ + free(res); + return NULL; + } + tcgetattr( res->fd, &(res->ttyset) ); + memcpy( &(res->oldset), &(res->ttyset), sizeof(struct termios)); + res->ttyset.c_cflag = speed | CS8 | CLOCAL | CREAD; + res->ttyset.c_iflag = IGNPAR; + res->ttyset.c_oflag = 0; + res->ttyset.c_lflag = 0; + res->ttyset.c_cc[VMIN] = 0; + res->ttyset.c_cc[VTIME]= 0; + tcflush(res->fd, TCIFLUSH); + tcsetattr(res->fd, TCSANOW, &(res->ttyset) ); + return res; +} + +int wch_if_uart_close(wch_if_uart_t tty){ + if(tty == NULL)return -1; + tcsetattr( tty->fd, TCSANOW, &(tty->oldset) ); + close( tty->fd ); + free(tty); + return 0; +} + +ssize_t wch_if_uart_write(wch_if_uart_t tty, void *buf, size_t count){ + if(tty == NULL)return -1; + return write( tty->fd, buf, count ); +} +#if 0 +// nonblocking read must read at least 1 byte. If connection fails the timeout will be infinite +ssize_t wch_if_uart_read(wch_if_uart_t tty, void *buf, size_t count){ + if(tty == NULL)return -1; + if((count > 1) && (tty->ttyset.c_cc[VTIME]>0)){ + tty->ttyset.c_cc[VMIN]= count; + tcsetattr( tty->fd, TCSANOW, &(tty->ttyset) ); + } + return read( tty->fd, buf, count ); +} +#endif +ssize_t wch_if_uart_read(wch_if_uart_t tty, void *buf, size_t count){ + if(tty == NULL)return -1; + size_t i = 0; + for(;ifd, &((char*)buf)[i], 1); + if(res <= 0)break; + } + return i; +} + +int wch_if_uart_timeout(wch_if_uart_t tty, ssize_t time_ms){ + if(tty == NULL)return -1; + time_ms /= 100; + tty->ttyset.c_cc[VTIME]= time_ms; + if(time_ms == 0)tty->ttyset.c_cc[VMIN] = 0; + tcsetattr( tty->fd, TCSANOW, &(tty->ttyset) ); + return 0; +} + +void wch_if_uart_rts(wch_if_t interf, char state){ + int flags; + state = !state; + ioctl(TTY->fd, TIOCMGET, &flags); + flags = (flags &~TIOCM_RTS) | (state*TIOCM_RTS); + ioctl(TTY->fd, TIOCMSET, &flags); + uart_if_flush(TTY); +} + +void wch_if_uart_dtr(wch_if_t interf, char state){ + int flags; + state = !state; + ioctl(TTY->fd, TIOCMGET, &flags); + flags = (flags &~TIOCM_DTR) | (state*TIOCM_DTR); + ioctl(TTY->fd, TIOCMSET, &flags); + uart_if_flush(TTY); +} + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#include +#include +#ifndef PHYSICAL_ADDRESS //bug workaround "/usr/share/mingw-w64/include/ntddser.h:368:9: error: unknown type name ‘PHYSICAL_ADDRESS’ " +typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; +#endif +#include + + +struct wch_if_uart{ + HANDLE handle; +}; + +wch_if_uart_t wch_if_uart_open(char name[], unsigned int baudrate){ + wch_if_uart_t res = (struct wch_if_uart*)malloc(sizeof(struct wch_if_uart)); + if(res == NULL)return res; + res->handle = CreateFileA(name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(res->handle == INVALID_HANDLE_VALUE){ + free(res); + return NULL; + } + if(res->handle == (HANDLE)-1){ + free(res); + return NULL; + } + + SetCommMask(res->handle, EV_RXCHAR); //какие сигналы отслеживать (только RX) + SetupComm(res->handle, 1000, 1000); //размер буферов на прием и передачу + + COMMTIMEOUTS timeout; + timeout.ReadIntervalTimeout = 0; + timeout.ReadTotalTimeoutMultiplier = 1; + timeout.ReadTotalTimeoutConstant = 0; + timeout.WriteTotalTimeoutMultiplier = 1; + timeout.WriteTotalTimeoutConstant = 0; + + if( !SetCommTimeouts(res->handle, &timeout) ){ + CloseHandle(res->handle); + free(res); + return NULL; + } + + DCB dcb; + + memset(&dcb,0,sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + GetCommState(res->handle, &dcb); + + dcb.BaudRate = (DWORD)baudrate; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + dcb.fAbortOnError = FALSE; + dcb.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE; + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.XonChar = 0; + dcb.XoffChar = (unsigned char)0xFF; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.XonLim = 128; + dcb.XoffLim = 128; + + if( !SetCommState(res->handle, &dcb) ){ + CloseHandle(res->handle); + free(res); + return NULL; + } + return res; +} + +int wch_if_uart_close(wch_if_uart_t tty){ + if(tty == NULL)return -1; + CloseHandle(tty->handle); + free(tty); + return 0; +} + +ssize_t wch_if_uart_write(wch_if_uart_t tty, void *buf, size_t count){ + if(tty == NULL)return -1; + DWORD cnt; + if( !WriteFile(tty->handle, buf, (DWORD)count, &cnt, NULL))return -1; + return cnt; +} + +ssize_t wch_if_uart_read(wch_if_uart_t tty, void *buf, size_t count){ + if(tty == NULL)return -1; + DWORD cnt; + if( !ReadFile(tty->handle, buf, (DWORD)count, &cnt, NULL))return -1; + return cnt; +} + +int wch_if_uart_timeout(wch_if_uart_t tty, ssize_t time_ms){ + if(tty == NULL)return -1; + COMMTIMEOUTS timeout; + GetCommTimeouts(tty->handle, &timeout); + timeout.ReadIntervalTimeout = time_ms; + timeout.ReadTotalTimeoutMultiplier = 1; + timeout.ReadTotalTimeoutConstant = time_ms; + timeout.WriteTotalTimeoutMultiplier = 1; + timeout.WriteTotalTimeoutConstant = time_ms; + if( !SetCommTimeouts(tty->handle, &timeout) ){ + return -1; + } + return 0; +} + +void wch_if_uart_rts(wch_if_t interf, char state){ + DWORD code; + if(state)code = IOCTL_SERIAL_CLR_RTS; else code = IOCTL_SERIAL_SET_RTS; + DeviceIoControl(TTY->handle, code, NULL, 0, NULL, 0, NULL, NULL); + uart_if_flush(TTY); +} + +void wch_if_uart_dtr(wch_if_t interf, char state){ + DWORD code; + if(state)code = IOCTL_SERIAL_CLR_DTR; else code = IOCTL_SERIAL_SET_DTR; + DeviceIoControl(TTY->handle, code, NULL, 0, NULL, 0, NULL, NULL); + uart_if_flush(TTY); +} + +#else + #error "Unsupported platform" +#endif diff --git a/wch_if_usb.c b/wch_if_usb.c new file mode 100644 index 0000000..bba711d --- /dev/null +++ b/wch_if_usb.c @@ -0,0 +1,186 @@ +#ifdef BUILD_SMALL + + #warning USB disabled + #include "wch_if.h" + wch_if_t wch_if_open_usb( wch_if_match match_func, wch_if_debug debug_func ){return NULL;} + void wch_if_close_usb(wch_if_t *interface){} + +#else + +#include +#include +#include +#include +#include "wch_if.h" + +#define ISP_VID 0x4348 +#define ISP_PID 0x55e0 +#define ISP_EP_OUT 0x02 +#define ISP_EP_IN 0x82 + +typedef struct{ + libusb_context *usb; + libusb_device_handle *dev; + unsigned int kernel; +}usb_if_t; + +static size_t usb_if_send(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]); +static size_t usb_if_recv(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]); +#define SELF ( (usb_if_t*)(interface->intern) ) + +static char wch_if_match_default(wch_if_t self){return 1;} + + +wch_if_t wch_if_open_usb( wch_if_match match_func, wch_if_debug debug_func ){ + if(match_func == NULL)match_func = wch_if_match_default; + + wch_if_t port = (wch_if_t)malloc(sizeof(struct wch_if)); + if(port == NULL)return NULL; + + usb_if_t *usb = (usb_if_t*)malloc(sizeof(usb_if_t)); + if(usb == NULL){free(port); return NULL;} + + usb->usb = NULL; + usb->dev = NULL; + usb->kernel = 0; + port->maxdatasize = 64-3; + port->send = usb_if_send; + port->recv = usb_if_recv; + port->close = wch_if_close_usb; + port->debug = debug_func; + port->dbg_content_start = 0; + port->intern = usb; + + int res; + libusb_device **list; + libusb_device *cur = NULL; + struct libusb_device_descriptor desc; + + res = libusb_init(&usb->usb); + if(res){fprintf(stderr, "Usb init: fail\n"); return NULL;} + + ssize_t dev_count = libusb_get_device_list(usb->usb, &list); + if(dev_count < 0){fprintf(stderr, "Usb get device list: fail\n"); return NULL;} + + for(int i=0; ikernel = 0; + res = libusb_get_device_descriptor(list[i], &desc); + if(res < 0)continue; + + if(desc.idVendor != ISP_VID || desc.idProduct != ISP_PID)continue; + + cur = libusb_ref_device(list[i]); + + res = libusb_open(cur, &usb->dev); + if(res < 0){ + fprintf(stderr, "libusb_open: %s\n", libusb_strerror(res)); + break; + } + + res = libusb_kernel_driver_active(usb->dev, 0); + if(res == LIBUSB_ERROR_NOT_SUPPORTED)res = 0; + if(res < 0){fprintf(stderr, "libusb_kernel_driver_active: %s\n", libusb_strerror(res)); continue;} + usb->kernel = res; + if(usb->kernel == 1){ + if(libusb_detach_kernel_driver(usb->dev, 0)){ + fprintf(stderr, "Couldn't detach kernel driver!\n"); + break; + } + } + res = libusb_claim_interface(usb->dev, 0); + if(res){fprintf(stderr, "libusb_claim_interface: %s\n", libusb_strerror(res)); break;} + + res = match_func(port); + if(res){ + libusb_free_device_list(list, 1); + return port; + } + + res = libusb_release_interface(usb->dev, 0); + if(res){fprintf(stderr, "libusb_release_interface: %s\n", libusb_strerror(res));} + if(usb->kernel == 1)libusb_attach_kernel_driver(usb->dev, 0); + libusb_close(usb->dev); + libusb_unref_device(cur); + usb->dev = NULL; + } + libusb_free_device_list(list, 1); + wch_if_close_usb(&port); + return NULL; +} + +static size_t usb_if_send(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]){ + uint8_t buf[64]; + int count, res; + if((size_t)(len + 3) > sizeof(buf)){ + fprintf(stderr, "usb_if_send: invalid argument, length %d\n", len); + return 0; + } + buf[0] = cmd; + buf[1] = (len >> 0) & 0xFF; + buf[2] = (len >> 8) & 0xFF; + if(len > 0)memcpy(&buf[3], data, len); + + if( interface->debug )interface->debug(interface, "usb_if_send", 1, len+3, buf); + + res = libusb_bulk_transfer( SELF->dev, ISP_EP_OUT, buf, len + 3, &count, 10000); + if(res){fprintf(stderr, "usb_if_send: %s\n", libusb_strerror(res)); return 0;} + return count; +} + +static size_t usb_if_recv(wch_if_t interface, uint8_t cmd, uint16_t len, uint8_t data[]){ + uint8_t buf[64]; + int res, count; + uint16_t datalen; + if((size_t)(len + 4) > sizeof(buf)){ + fprintf(stderr, "usb_if_recv: invalid argument, length %d\n", len); + return 0; + } + res = libusb_bulk_transfer(SELF->dev, ISP_EP_IN, buf, len + 4, &count, 10000); + if(res){fprintf(stderr, "usb_if_send: %s\n", libusb_strerror(res)); return 0;} + if(count < 4){fprintf(stderr, "usb_if_recv: not enough data recv\n"); return 0;} + if(buf[0] != cmd){ + fprintf(stderr, "usb_if_recv: got wrong 'cmd' %.2X (exp %.2X)\n", buf[0], cmd); + return 0; + } + if(buf[1]){fprintf(stderr, "usb_if_recv: cmd error %.2X\n", buf[1]); return 0;} + count -= 4; + datalen = buf[2] | ((uint16_t)buf[3] << 8); + if(datalen != count){ + fprintf(stderr, "usb_if_recv: length mismatch, %d ('len'=%d)\n", count, datalen); + return 0; + } + if(count < len)len = count; + + if(data != NULL)memcpy(data, &buf[4], len); + + if( interface->debug )interface->debug(interface, "usb_if_recv", 0, count+4, buf); + + return count; +} + +void wch_if_close_usb(wch_if_t *interface){ + usb_if_t *usb = (usb_if_t*)((*interface)->intern); + int res = 0; + libusb_device *dev = NULL; + if(usb->dev)dev = libusb_get_device(usb->dev); + //printf("dev = %p\n", dev); + + if(usb->dev)res = libusb_release_interface(usb->dev, 0); + if(res){fprintf(stderr, "libusb_release_interface: %s\n", libusb_strerror(res));} + //printf("usb_dev = %p\n", usb->dev); + + if(usb->kernel == 1)libusb_attach_kernel_driver(usb->dev, 0); + + if(usb->dev)libusb_close(usb->dev); + //printf("close\n"); + + if(dev)libusb_unref_device(dev); + //printf("unref\n"); + + if (usb)libusb_exit(usb->usb); + + free(usb); + free(*interface); + *interface = NULL; +} +#endif \ No newline at end of file diff --git a/wch_yaml_parse.c b/wch_yaml_parse.c new file mode 100644 index 0000000..546a9cb --- /dev/null +++ b/wch_yaml_parse.c @@ -0,0 +1,786 @@ +#ifndef BUILD_SMALL +#include +#include +#include +#include +#include +#include "wch_yaml_parse.h" + +#ifdef _MSC_VER //not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw + #define strcasecmp _stricmp +#endif + +//debug +#define INDENT " " +#define STRVAL(x) ((x) ? (char*)(x) : "") + +static void indent(int level){ + int i; + for(i = 0; i < level; i++)printf("%s", INDENT); +} + +#define YAMLT_UNKNOWN 0 +#define YAMLT_SCALAR 1 +#define YAMLT_STRUCT 2 +#define YAMLT_ARRAY 3 +#define YAMLT_REF 4 + +//debug +__attribute__((unused)) static char *names[] = { + "unknown", + "scalar", + "struct", + "array", + "ref", +}; + +struct yaml_var; +typedef struct yaml_var yvar_t; +struct yaml_var{ + yvar_t *parent; + char *name; + char type; + union{ + char *value; + char *ref; + yvar_t *arr; + }; + size_t count; + size_t arr_size; +}; + +__attribute__((unused)) static void yvar_show(yvar_t *cur, int level, int depth){ + if(cur->type == YAMLT_SCALAR){ + printf("%s\n", cur->value); + }else if(cur->type == YAMLT_REF){ + printf("-> %s\n", cur->ref); + }else if(cur->type == YAMLT_ARRAY){ + if(depth == 0){printf("[...]\n"); return;} + printf("[\n"); + for(int i=0; icount; i++){ + indent(level); + yvar_show(&cur->arr[i], level+1, depth-1); + } + indent(level-1); printf("]\n"); + }else if(cur->type == YAMLT_STRUCT){ + if(depth == 0){printf("{...}\n"); return;} + printf("{\n"); + for(int i=0; icount; i++){ + indent(level); + printf("%s: ", cur->arr[i].name); + yvar_show(&cur->arr[i], level+1, depth-1); + } + indent(level-1); printf("}\n"); + } +} + +static yvar_t* yvar_find(yvar_t *root, char *fieldname, char *value){ + if(root == NULL)return NULL; + if(root->type != YAMLT_STRUCT && root->type != YAMLT_ARRAY)return NULL; + for(int i=0; icount; i++){ + yvar_t *cur = &root->arr[i]; + if(cur->name && (strcasecmp(cur->name, fieldname)==0)){ + if(value == NULL)return cur; + if((cur->type == YAMLT_SCALAR || cur->type == YAMLT_REF) && strcasecmp(cur->value, value)==0)return cur; + } + if(cur->type == YAMLT_STRUCT || cur->type == YAMLT_ARRAY){ + yvar_t *res = yvar_find(cur, fieldname, value); + if(res)return res; + } + } + return NULL; +} + +static yvar_t* yvar_byname(yvar_t *root, char name[]){ + if(root == NULL)return NULL; + if(root->type != YAMLT_STRUCT && root->type != YAMLT_ARRAY)return NULL; + for(int i=0; icount; i++){ + yvar_t *cur = &root->arr[i]; + if(cur->name && (strcasecmp(cur->name, name)==0))return cur; + } + return NULL; +} + +static void yvar_free(yvar_t *cur){ + for(int i=0; icount; i++){ + yvar_free(&cur->arr[i]); + } + if(cur->arr)free(cur->arr); + if(cur->name)free(cur->name); + cur->arr = NULL; + cur->count = 0; + cur->type = YAMLT_UNKNOWN; + cur->name = NULL; +} + +static void yvar_init(yvar_t *cur){ + cur->name = NULL; + cur->arr = NULL; + cur->type = YAMLT_UNKNOWN; + cur->count = 0; + cur->arr_size = 0; +} + +static char yvar_reaisze(yvar_t *cur){ + if((cur->count+1) < cur->arr_size)return 1; + size_t newsize = cur->arr_size + 8; + yvar_t *temp = realloc(cur->arr, sizeof(yvar_t)*newsize); + if(temp == NULL){ + fprintf(stderr, "yvar_reaisze: can not allocate memory\n"); + return 0; + } + cur->arr = temp; + cur->arr_size = newsize; + for(int i=0; icount; i++){ + temp = &(cur->arr[i]); + temp->parent = cur; + if(temp->type != YAMLT_STRUCT && temp->type != YAMLT_ARRAY)continue; + for(int j=0; jcount; j++){ + temp->arr[j].parent = temp; + } + } + return 1; +} + +static char yvar_parse(yvar_t *cur, yaml_parser_t *parser){ + yaml_event_t event; + yaml_event_type_t event_type; + + cur->arr_size = 0; + cur->count = 0; + cur->arr = NULL; + if(!yvar_reaisze(cur))return 0; + cur->count = 1; + yvar_t *var = &cur->arr[0]; + yvar_init(var); + var->parent = cur; + + do{ + if(!yaml_parser_parse(parser, &event))break; + event_type = event.type; + + switch(event_type){ + case YAML_MAPPING_START_EVENT: + var->type = YAMLT_STRUCT; + if(!yvar_parse(var, parser))return 0; + if(!yvar_reaisze(cur))return 0; + cur->count++; + var = &cur->arr[cur->count-1]; + yvar_init(var); + var->parent = cur; + break; + case YAML_MAPPING_END_EVENT: + yaml_event_delete(&event); + cur->count--; + return 1; + case YAML_SEQUENCE_START_EVENT: + var->type = YAMLT_ARRAY; + if(!yvar_parse(var, parser))return 0; + if(!yvar_reaisze(cur))return 0; + cur->count++; + var = &cur->arr[cur->count-1]; + yvar_init(var); + var->parent = cur; + break; + case YAML_SEQUENCE_END_EVENT: + yaml_event_delete(&event); + cur->count--; + return 1; + case YAML_SCALAR_EVENT: + if((var->name == NULL) && (cur->type != YAMLT_ARRAY)){ + var->name = strdup((char*)event.data.scalar.value); + }else{ + var->type = YAMLT_SCALAR; + var->value = strdup((char*)event.data.scalar.value); + if(!yvar_reaisze(cur))return 0; + cur->count++; + var = &cur->arr[cur->count-1]; + yvar_init(var); + var->parent = cur; + } + break; + case YAML_ALIAS_EVENT: + var->type = YAMLT_REF; + var->ref = strdup((char*)event.data.alias.anchor); + if(!yvar_reaisze(cur))return 0; + cur->count++; + var = &cur->arr[cur->count-1]; + yvar_init(var); + var->parent = cur; + break; + case YAML_NO_EVENT: case YAML_STREAM_START_EVENT: case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_START_EVENT: case YAML_DOCUMENT_END_EVENT: + break; + } + + yaml_event_delete(&event); + }while (event_type != YAML_STREAM_END_EVENT); + if(cur->type == YAMLT_ARRAY || cur->type == YAMLT_STRUCT)cur->count--; + return 1; +} + +static yvar_t* yvar_load(char filename[]){ + FILE *pf = fopen(filename, "rt"); + yvar_t *var = (yvar_t*)malloc(sizeof(yvar_t)); + yvar_init(var); + var->type = YAMLT_STRUCT; + + yaml_parser_t parser; + + yaml_parser_initialize(&parser); + yaml_parser_set_input_file(&parser, pf); + + char res = yvar_parse(var, &parser); + if(!res){ + yaml_parser_delete(&parser); + yvar_free(var); + free(var); + return NULL; + } + + yaml_parser_delete(&parser); + return var; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// wch_info +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + + +static uint32_t read_bitfield(char *str){ + if(str[0] < '0' || str[0] > '9')return 0xFFFFFFFF; + if(str[0] == '0'){ + if(str[1] == 'b'){ + return strtoull(str+2, NULL, 2); + }else if(str[1] == 'x'){ + return strtoull(str+2, NULL, 16); + } + } + return strtoull(str, NULL, 10); +} + +static uint32_t read_units(wch_info_t *info, char *field, char *str){ + uint32_t res = 0; + char *uns; + res = strtoull(str, &uns, 10); + if(uns[0] == 'k'){ + res *= 1024; + }else if(uns[0] == 'M'){ + res *= 1024*1024; + }else if(uns[0] != 0){ + info->errflag = 1; + fprintf(stderr, "Warning: unknown units [%c] in '%s' field\n", uns[0], field); + } + return res; +} + +wch_info_t* wch_info_read_file(char filename[], uint8_t type, uint8_t id){ + char buf[100]; + yvar_t *root = yvar_load(filename); + if(root == NULL){fprintf(stderr, "Error opening [%s]\n", filename); return NULL;} + + sprintf(buf, "0x%.2X", type); + yvar_t *com = yvar_find(root, "device_type", buf); + if(com == NULL){yvar_free(root); free(root); return NULL;} + + sprintf(buf, "0x%.2X", id); + yvar_t *dev = yvar_find(root, "chip_id", buf); + if(dev == NULL){yvar_free(root); free(root); return NULL;} + + + wch_info_t *info = (wch_info_t*)malloc(sizeof(wch_info_t)); + if(info == NULL){return NULL;} + info->type = type; + info->id = id; + info->reg_count = 0; + info->reg = NULL; + info->reg_correct = 0; + info->errflag = 0; + + yvar_t *cur; + + // device name + cur = yvar_byname(dev->parent, "name"); + if(cur == NULL){ + fprintf(stderr, "Unable to read device name\n"); + info->name = strdup("Unknown"); + info->errflag = 1; + }else{ + info->name = strdup(cur->value); + } + + //flash size + cur = yvar_byname(dev->parent, "flash_size"); + if(cur == NULL){ + fprintf(stderr, "Unable to read flash size\n"); + info->flash_size = 0; + info->errflag = 1; + }else{ + info->flash_size = read_units(info, "flash_size", cur->value); + } + + //flash_total + cur = yvar_byname(dev->parent, "flash_total"); + if(cur == NULL){ + cur = yvar_byname(com->parent, "flash_total"); + } + if(cur == NULL){ + info->flash_total = 0; + }else{ + info->flash_total = read_units(info, "flash_total", cur->value); + } + + //eeprom size + info->eeprom_size = 0; + info->eeprom_start_addr = 0; + cur = yvar_byname(dev->parent, "eeprom_size"); + if(cur != NULL){ + info->eeprom_size = read_units(info, ", eeprom_size", cur->value); + } + cur = yvar_byname(dev->parent, "eeprom_start_addr"); + if(cur != NULL){ + info->eeprom_start_addr = read_units(info, "eeprom_start_addr", cur->value); + } + + //regs + cur = yvar_byname(dev->parent, "config_registers"); + if(cur != NULL){ + if(cur->type == YAMLT_REF){ + cur = yvar_byname(com->parent, cur->value); + } + } + if(cur == NULL){ + cur = yvar_byname(com->parent, "config_registers"); + if(cur != NULL){ + if(cur->type == YAMLT_REF){ + cur = yvar_byname(com->parent, cur->value); + } + } + } + if(cur == NULL || cur->type != YAMLT_ARRAY){ + fprintf(stderr, "Unable to read OPTION BYTES (field 'config_registers' in database)\n"); + info->reg_count = 0; + info->reg = NULL; + info->errflag = 1; + }else{ + info->reg = (wch_regs_t*)malloc(sizeof(wch_regs_t) * cur->count); + info->reg_count = cur->count; + for(int i=0; icount; i++){ + wch_regs_t *reg = &info->reg[i]; + yvar_t *r = yvar_byname(&cur->arr[i], "name"); + if(r == NULL)reg->name = strdup("Unknown"); else reg->name = strdup(r->value); + r = yvar_byname(&cur->arr[i], "offset"); + if(r == NULL)reg->offset = 0; else reg->offset = strtoull(r->value, NULL, 16); + r = yvar_byname(&cur->arr[i], "reset"); + if(r == NULL)reg->defval = 0xFFFFFFFF; else reg->defval = strtoull(r->value, NULL, 16); + reg->curval = reg->defval; + r = yvar_byname(&cur->arr[i], "fields"); + if(r == NULL){reg->field_count=0; reg->field = NULL; continue;} + reg->field_count = r->count; + reg->field = (wch_bitfield_t*)malloc(sizeof(wch_bitfield_t) * r->count); + for(int j=0; jcount; j++){ + wch_bitfield_t *fld = ®->field[j]; + yvar_t *f = yvar_byname(&r->arr[j], "name"); + if(f == NULL)fld->name = strdup("Unknown"); else fld->name = strdup(f->value); + f = yvar_byname(&r->arr[j], "bit_range"); + fld->var_count = 0; + fld->variant = NULL; + if(f == NULL){ + fld->st=fld->en = 0xFF; + }else{ + fld->en = strtoull(f->arr[0].value, NULL, 10); + fld->st = strtoull(f->arr[1].value, NULL, 10); + if(fld->st > fld->en){uint8_t temp = fld->st; fld->st = fld->en; fld->en = temp; } + f = yvar_byname(&r->arr[j], "explaination"); + if(f == NULL)continue; + fld->var_count = f->count; + fld->variant = (wch_bitfield_variant_t*)malloc(sizeof(wch_bitfield_variant_t) * f->count); + for(int k=0; kcount; k++){ + fld->variant[k].val = read_bitfield(f->arr[k].name); + fld->variant[k].name = strdup(f->arr[k].value); + } + } + } + } + } + + yvar_free(root); free(root); + return info; +} + +void wch_info_free(wch_info_t **info){ + if(*info == NULL)return; + free((*info)->name); + for(int i=0; i<(*info)->reg_count; i++){ + wch_regs_t *reg = &(*info)->reg[i]; + free(reg->name); + for(int j=0; jfield_count; j++){ + wch_bitfield_t *fld = ®->field[j]; + free(fld->name); + for(int k=0; k<(*info)->reg[i].field[j].var_count; k++){ + free(fld->variant[k].name); + } + if(fld->variant)free(fld->variant); + } + if(reg->field)free(reg->field); + } + free((*info)->reg); + free(*info); + *info = NULL; +} + +static char* show_bin(uint32_t x){ + char uns = ' '; + static char buf[10]; + if(x < 1024){ + uns = ' '; + }else if(x < (1024*1024)){ + x /= 1024; uns='k'; + }else if(x < (1024*1024*1024)){ + x /= (1024*1024); uns='M'; + }else{ + strcpy(buf, "(err)>1G"); return buf; + } + sprintf(buf, "%u%c", (unsigned int)x, uns); + return buf; +} + +uint32_t wch_bitfield_val(wch_bitfield_t *fld, uint32_t reg){ + if(fld == NULL){fprintf(stderr, "wch_bitfield_val: fld = NULL\n"); return reg;} + uint32_t mask = 0xFFFFFFFF; + mask = (mask << (31 - fld->en)) >> (31 - fld->en) >> (fld->st); + return (reg >> (fld->st)) & mask; +} + +static char* wch_bitfield_name(wch_bitfield_t *fld, uint32_t val){ + static char unk[] = ""; + if(fld == NULL){fprintf(stderr, "wch_bitfield_name: fld = NULL\n"); return unk;} + for(int i=0; ivar_count; i++){ + if(val == fld->variant[i].val)return fld->variant[i].name; + } + return unk; +} + +static uint32_t bitfield_apply(wch_info_t *info, wch_bitfield_t *fld, uint32_t reg, uint32_t val){ + if(info == NULL){fprintf(stderr, "bitfield_apply: info = NULL\n"); return reg;} + if(fld == NULL){fprintf(stderr, "bitfield_apply: fld = NULL\n"); return reg;} + uint32_t mask = 0xFFFFFFFF; + mask = (mask << (31 - fld->en)) >> (31 - fld->en) >> fld->st; + uint32_t nval = val & mask; + mask <<= fld->st; + uint32_t res = reg &~ mask; + if(nval != val){ + info->errflag = 1; + printf("Warning: field '%s' truncated: %X -> %X\n", fld->name, val, nval); + } + nval <<= fld->st; + res |= nval; + return res; +} + + +void wch_info_regs_import(wch_info_t *info, uint32_t *regs, size_t reg_count){ + if(info == NULL){fprintf(stderr, "wch_info_regs_import: info = NULL\n"); return;} + if(info->reg == NULL){fprintf(stderr, "wch_info_regs_import: info.regs = NULL\n"); return;} + if(reg_count > info->reg_count)reg_count = info->reg_count; + for(int i=0; ireg[i].offset / 4; + info->reg[i].curval = regs[idx]; + } + info->reg_correct = 1; +} + +void wch_info_regs_export(wch_info_t *info, uint32_t *regs, size_t reg_count){ + if(info == NULL){fprintf(stderr, "wch_info_regs_import: info = NULL\n"); return;} + if(info->reg == NULL){fprintf(stderr, "wch_info_regs_import: info.regs = NULL\n"); return;} + if(reg_count > info->reg_count)reg_count = info->reg_count; + if(info->reg_correct){ + for(int i=0; ireg[i].offset / 4; + regs[idx] = info->reg[i].curval; + } + }else{ + for(int i=0; ireg[i].offset / 4; + regs[idx] = info->reg[i].defval; + } + } +} + +static wch_regs_t* regs_byname(wch_info_t *info, char name[]){ + if(info == NULL){fprintf(stderr, "regs_byname: info = NULL\n"); return NULL;} + if(info->reg == NULL){fprintf(stderr, "regs_byname: info.regs = NULL\n"); return NULL;} + for(int i=0; ireg_count; i++){ + if(strcasecmp(info->reg[i].name, name)==0)return &info->reg[i]; + } + return NULL; +} + +wch_regs_t* wch_bitfield_byname(wch_info_t *info, char name[], wch_bitfield_t **res_field){ + *res_field = NULL; + if(info == NULL){fprintf(stderr, "wch_bitfield_byname: info = NULL\n"); return NULL;} + if(info->reg == NULL){fprintf(stderr, "wch_bitfield_byname: info.regs = NULL\n"); return NULL;} + for(int i=0; ireg_count; i++){ + for(int j=0; jreg[i].field_count; j++){ + if(strcasecmp(info->reg[i].field[j].name, name)==0){ + *res_field = &(info->reg[i].field[j]); + return &info->reg[i]; + } + } + } + return NULL; +} + +void wch_info_show(wch_info_t *info){ + if(info == NULL){fprintf(stderr, "wch_info_show: info = NULL\n"); return;} + printf("Device 0x%.2X 0x%.2X\n", info->type, info->id); + printf("name = [%s]\n", info->name); + printf("flash size = %s\n", show_bin(info->flash_size)); + //if(info->flash_total) + printf("total flash size = %s\n", show_bin(info->flash_total)); + if(info->eeprom_size){ + printf("eeprom %s, ", show_bin(info->eeprom_size)); + printf("starts from %s\n", show_bin(info->eeprom_start_addr)); + } + if(info->reg_count == 0){ + printf("Warning: unable to read Option bytes\n"); + info->errflag = 1; + return; + } + printf("Option bytes:\n"); + for(int i=0; ireg_count; i++){ + printf(" 0x%.2X %s ; default = 0x%.8X", info->reg[i].offset, info->reg[i].name, info->reg[i].defval); + if(info->reg_correct){ + printf(", current = 0x%.8X\n", info->reg[i].curval); + }else{ + printf("\n"); + } + for(int j=0; jreg[i].field_count; j++){ + wch_bitfield_t *fld = &(info->reg[i].field[j]); + if(fld->en == fld->st){ + printf(" [%i] : %s", fld->st, fld->name); + }else{ + printf(" [%i:%i] : %s", fld->en, fld->st, fld->name); + } + + if(!info->reg_correct){ + uint32_t def = wch_bitfield_val(fld, info->reg[i].defval); + char *name = wch_bitfield_name(fld, def); + printf(" = %X => \"%s\"\n", def, name); + }else{ + uint32_t def = wch_bitfield_val(fld, info->reg[i].defval); + uint32_t val = wch_bitfield_val(fld, info->reg[i].curval); + char *name = wch_bitfield_name(fld, val); + if(def == val){ + printf(" = %X => \"%s\"\n", def, name); + }else{ + printf(" = %X (def = %X) => \"%s\"\n", val, def, name); + } + } + + for(int k=0; kvar_count; k++){ + if(fld->variant[k].val <= 9){ + printf(" %X : %s\n", fld->variant[k].val, fld->variant[k].name); + }else{ + printf(" 0x%X : %s\n", fld->variant[k].val, fld->variant[k].name); + } + } + } + } +} + +char wch_info_modify(wch_info_t *info, char str[]){ + if(info == NULL){fprintf(stderr, "wch_info_modify: info = NULL\n"); info->errflag = 1; return 0;} + if(info->reg == NULL){fprintf(stderr, "wch_info_modify: info.regs = NULL\n"); info->errflag = 1; return 0;} + if(!(info->reg_correct)){fprintf(stderr, "wch_info_modify: no data read from MCU\n"); info->errflag = 1; return 0;} + const char delim[] = " \t\n,;"; + char *buf = strdup(str); + char *tok = strtok(buf, delim); + int N = 0; + do{ + N++; + char *val = NULL, *ch; + ch = strchr(tok, '='); + if(ch != NULL){ + val = ch+1; + ch[0] = 0; + if(val[0] == 0 || strchr(delim, val[0])!=NULL ){ + val = strtok(NULL, delim); + } + }else{ + val = strtok(NULL, delim); + if(val == NULL){ + fprintf(stderr, "Option bytes modify: wrong string\n"); + info->errflag = 1; + free(buf); + return 0; + } + if(val[0] == '='){ + if(val[1] == 0){ + val = strtok(NULL, delim); + }else{ + val++; + } + } + } + if(val == NULL){fprintf(stderr, "Option bytes modify: wrong string\n"); free(buf); info->errflag = 1; return 0;} + + uint32_t ival; + if(sscanf(val, "%i", &ival) < 1){ + fprintf(stderr, "Option bytes modify: wrong string\n"); + free(buf); + info->errflag = 1; + return 0; + } + + wch_regs_t *reg = regs_byname(info, tok); + if(reg != NULL){ + reg->curval = ival; + }else{ + wch_bitfield_t *fld = NULL; + reg = wch_bitfield_byname(info, tok, &fld); + if(fld == NULL){ + fprintf(stderr, "Incorrect name [%s]\n", tok); free(buf); info->errflag = 1; return 0; + } + reg->curval = bitfield_apply(info, fld, reg->curval, ival); + } + + }while((tok = strtok(NULL, delim)) ); + + free(buf); + return 1; +} + + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)\ + || defined(__APPLE__) || defined(__MACH__) + +#include +wch_info_t* wch_info_read_dir(char dirname[], char recur, uint8_t type, uint8_t id){ + wch_info_t *res = NULL; + DIR *dir; + struct dirent *entry; + size_t rootlen = strlen(dirname); + + dir = opendir(dirname); + if(!dir){fprintf(stderr, "Unable to open directory [%s]\n", dirname); return NULL;} + + while( (entry = readdir(dir)) != NULL){ + if(entry->d_type == DT_DIR){ + if(!recur)continue; + if(entry->d_name[0] == '.')continue; + size_t leaflen = strlen(entry->d_name); + char *newname = malloc(rootlen + leaflen + 2); + memcpy(newname, dirname, rootlen); + newname[rootlen] = '/'; + memcpy(&newname[rootlen+1], entry->d_name, leaflen); + newname[rootlen + 1 + leaflen] = 0; + res = wch_info_read_dir(newname, recur, type, id); + free(newname); + if(res){ + closedir(dir); + return res; + } + }else if(entry->d_type == DT_REG){ + size_t leaflen = strlen(entry->d_name); + if(leaflen < 5)continue; + if(strcasecmp(&(entry->d_name[leaflen-5]), ".yaml") != 0)continue; + char *newname = malloc(rootlen + leaflen + 2); + memcpy(newname, dirname, rootlen); + newname[rootlen] = '/'; + memcpy(&newname[rootlen+1], entry->d_name, leaflen); + newname[rootlen + 1 + leaflen] = 0; + res = wch_info_read_file(newname, type, id); + free(newname); + if(res){ + closedir(dir); + return res; + } + } + }; + + closedir(dir); + return NULL; +} + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + +#include +wch_info_t* wch_info_read_dir(char dirname[], char recur, uint8_t type, uint8_t id){ + wch_info_t *res = NULL; + size_t rootlen = strlen(dirname); + char *path = malloc(rootlen + 5); + memcpy(path, dirname, rootlen); + path[rootlen] = '/'; + path[rootlen+1] = '*'; + path[rootlen+2] = 0; + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile(path, &ffd); + do{ + if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ + if(!recur)continue; + if(ffd.cFileName[0] == '.')continue; + size_t leaflen = strlen(ffd.cFileName); + char *newname = malloc(rootlen + leaflen + 5); + memcpy(newname, dirname, rootlen); + newname[rootlen] = '/'; + memcpy(&newname[rootlen+1], ffd.cFileName, leaflen); + newname[rootlen + 1 + leaflen] = 0; + res = wch_info_read_dir(newname, recur, type, id); + free(newname); + if(res){ + FindClose(hFind); + free(path); + return res; + } + }else{ + size_t leaflen = strlen(ffd.cFileName); + if(leaflen < 5)continue; + if(strcasecmp(&(ffd.cFileName[leaflen-5]), ".yaml") != 0)continue; + char *newname = malloc(rootlen + leaflen + 2); + memcpy(newname, dirname, rootlen); + newname[rootlen] = '/'; + memcpy(&newname[rootlen+1], ffd.cFileName, leaflen); + newname[rootlen + 1 + leaflen] = 0; + res = wch_info_read_file(newname, type, id); + free(newname); + if(res){ + FindClose(hFind); + free(path); + return res; + } + } + }while(FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + free(path); + return NULL; +} + +#else + #error "Unsupported platform" +#endif + +#else //ifdef BUILD_SMALL + +#warning DATABASE DISABLED +#include +#include +#include +#include +#include "wch_yaml_parse.h" +wch_info_t* wch_info_read_file(char filename[], uint8_t type, uint8_t id){return NULL;} +wch_info_t* wch_info_read_dir(char dirname[], char recur, uint8_t type, uint8_t id){return NULL;} +void wch_info_free(wch_info_t **info){} +void wch_info_show(wch_info_t *info){} +void wch_info_regs_import(wch_info_t *info, uint32_t *regs, size_t reg_count){} +void wch_info_regs_export(wch_info_t *info, uint32_t *regs, size_t reg_count){} +char wch_info_modify(wch_info_t *info, char str[]){return 0;} +wch_regs_t* wch_bitfield_byname(wch_info_t *info, char name[], wch_bitfield_t **res_field){return NULL;} +uint32_t wch_bitfield_val(wch_bitfield_t *fld, uint32_t reg){return 0;} + + +#endif diff --git a/wch_yaml_parse.h b/wch_yaml_parse.h new file mode 100644 index 0000000..4ff385f --- /dev/null +++ b/wch_yaml_parse.h @@ -0,0 +1,50 @@ +#ifndef __WCH_YAML_PARSE_H__ +#define __WCH_YAML_PARSE_H__ + +typedef struct{ + uint32_t val; + char *name; +}wch_bitfield_variant_t; + +typedef struct{ + uint8_t st, en; + char *name; + char *descr; + size_t var_count; + wch_bitfield_variant_t *variant; +}wch_bitfield_t; + +typedef struct{ + uint8_t offset; + char *name; + uint32_t defval; + uint32_t curval; + size_t field_count; + wch_bitfield_t *field; +}wch_regs_t; + +typedef struct{ + uint8_t type, id; + char *name; + size_t flash_size; + size_t flash_total; + size_t eeprom_size; + size_t eeprom_start_addr; + size_t reg_count; + wch_regs_t *reg; + char reg_correct; + char errflag; +}wch_info_t; + + +wch_info_t* wch_info_read_file(char filename[], uint8_t type, uint8_t id); +wch_info_t* wch_info_read_dir(char dirname[], char recur, uint8_t type, uint8_t id); +void wch_info_free(wch_info_t **info); +void wch_info_show(wch_info_t *info); +void wch_info_regs_import(wch_info_t *info, uint32_t *regs, size_t reg_count); +void wch_info_regs_export(wch_info_t *info, uint32_t *regs, size_t reg_count); +char wch_info_modify(wch_info_t *info, char str[]); +wch_regs_t* wch_bitfield_byname(wch_info_t *info, char name[], wch_bitfield_t **res_field); +uint32_t wch_bitfield_val(wch_bitfield_t *fld, uint32_t reg); + +#endif \ No newline at end of file