From 56902434f0916fe9b47b57053a8b5875e7c36444 Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 10 Aug 2023 02:17:39 +0200 Subject: [PATCH] Extract libch341eeprom and separate into exe and lib. --- src/ch341eeprom.c | 239 -------------- src/ch341eeprom.h | 152 --------- src/ch341funcs.c | 368 --------------------- src/fvflash.c | 141 ++++++++ src/libch341eeprom.c | 596 ++++++++++++++++++++++++++++++++++ src/libch341eeprom.h | 19 ++ src/libch341eeprom_internal.h | 150 +++++++++ 7 files changed, 906 insertions(+), 759 deletions(-) delete mode 100644 src/ch341eeprom.c delete mode 100644 src/ch341eeprom.h delete mode 100644 src/ch341funcs.c create mode 100644 src/fvflash.c create mode 100644 src/libch341eeprom.c create mode 100644 src/libch341eeprom.h create mode 100644 src/libch341eeprom_internal.h diff --git a/src/ch341eeprom.c b/src/ch341eeprom.c deleted file mode 100644 index f20690e..0000000 --- a/src/ch341eeprom.c +++ /dev/null @@ -1,239 +0,0 @@ -// -// ch341eeprom programmer version 0.1 (Beta) -// -// Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A IC -// -// (c) December 2011 asbokid -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -//#include -#include -#include -#include -#include -#include -#include -#include "ch341eeprom.h" - -FILE *debugout, *verbout; -uint8_t *readbuf = NULL; - -int main(int argc, char **argv) { - int i, ret = 0, eepromsize = 4096, bytesread = 0; - uint8_t debug = FALSE, verbose = TRUE; - struct libusb_device_handle *devHandle = NULL; - char *filename = NULL, eepromname[12], operation = 0; - FILE *fp; - - static char version_msg[] = - "fvflash - an i2c EEPROM programming tool for the WCH CH341a IC\n" \ - "Version " CH341TOOLVERSION " copyright (c) 2011 asbokid \n\n" \ - "modified by for the specific use case of fv-1 programming on macos\n\n" \ - "This program comes with asbolutely no warranty; This is free software,\n" \ - "and you are welcome to redistribute it under certain conditions:\n" \ - "GNU GPL v3 License: http://www.gnu.org/licenses/gpl.html\n"; - - static char usage_msg[] = - "Usage:\n" \ - " fvflash [options] [operation] filename\n" \ - " writes filename into fv1 (must be in bin format)\n\n" - - "operation:\n"\ - " default operation is write fv1\n"\ - " -e, --erase erase fv1 (fill with 0xff)\n" \ - " -r, --read read fv1 and save image to filename\n\n" - - "options:\n"\ - " -h, --help display this text\n" \ - " -d, --debug debug output\n" \ - " -s, --size size of fv1 {24c32|24c64} default=24c32\n\n" \ - - "Example: fvflash bank.bin - writes bank.bin into fv1\n"; - - static struct option longopts[] = { - {"help", no_argument, 0, 'h'}, - {"debug", no_argument, 0, 'd'}, - {"erase", no_argument, 0, 'e'}, - {"size", required_argument, 0, 's'}, - {"read", required_argument, 0, 'r'}, - {0, 0, 0, 0} - }; - - int32_t last_optidx = 0; - while (TRUE) { - int32_t optidx = 0; - int8_t c = getopt_long(argc,argv,"hdes:r:", longopts, &optidx); - if (c == -1) { - break; - } - - switch (c) { - case 'h': fprintf(stdout, "%s\n%s", version_msg, usage_msg); - return 0; - case 'd': debug = TRUE; - break; - case 's': if((eepromsize = parseEEPsize(optarg)) > 0) - strncpy(eepromname, optarg, 10); - break; - case 'e': if(!operation) - operation = 'e'; - else { - fprintf(stderr, "Conflicting command line options\n"); - goto shutdown; - } - break; - case 'r': if(!operation) { - operation = 'r'; - filename = (char *) malloc(strlen(optarg)+1); - strcpy(filename, optarg); - } else { - fprintf(stderr, "Conflicting command line options\n"); - goto shutdown; - } - break; - default: - case '?': fprintf(stdout, "%s\bn", version_msg); - fprintf(stderr, "%s", usage_msg); - goto shutdown; - } - last_optidx = optidx; - } - - if(!operation) { - if(argc < 2) { - fprintf(stderr, "%s", usage_msg); - goto shutdown; - } - printf("writing %s\n", argv[argc-1]); - operation = 'w'; - filename = (char *) malloc(strlen(argv[argc-1])+1); - strcpy(filename, argv[argc-1]); - } - - debugout = (debug == TRUE) ? stdout : fopen("/dev/null","w"); - verbout = (verbose == TRUE) ? stdout : fopen("/dev/null","w"); - fprintf(debugout, "Debug Enabled\n"); - - if(eepromsize <= 0) { - fprintf(stderr, "Invalid EEPROM size\n"); - goto shutdown; - } - - readbuf = (uint8_t *) malloc(MAX_EEPROM_SIZE); // space to store loaded EEPROM - if(!readbuf) { - fprintf(stderr, "Couldnt malloc space needed for EEPROM image\n"); - goto shutdown; - } - - if(!(devHandle = ch341configure(USB_LOCK_VENDOR, USB_LOCK_PRODUCT))) { - fprintf(stderr, "Couldnt configure USB device\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); - goto shutdown; - } - fprintf(verbout, "Configured USB device\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); - - if(ch341setstream(devHandle, CH341_I2C_STANDARD_SPEED) < 0) { - fprintf(stderr, "Couldnt set i2c bus speed\n"); - goto shutdown; - } - fprintf(verbout, "Set i2c bus speed to [100kHz]\n"); - - switch(operation) { - case 'r': // read - memset(readbuf, 0xff, MAX_EEPROM_SIZE); - - if(ch341readEEPROM(devHandle, readbuf, eepromsize) < 0) { - fprintf(stderr, "Couldnt read [%d] bytes from [%s] EEPROM\n", eepromsize, eepromname); - goto shutdown; - } - fprintf(stdout, "Read [%d] bytes from [%s] EEPROM\n", eepromsize, eepromname); - for(i=0;i eepromsize) - fprintf(stdout, "Truncated to [%d] bytes for [%s] EEPROM\n", eepromsize, eepromname); - - if(ch341writeEEPROM(devHandle, readbuf, eepromsize) < 0) { - fprintf(stderr,"Failed to write [%d] bytes from [%s] to [%s] EEPROM\n", eepromsize, filename, eepromname); - goto shutdown; - } - fprintf(stdout, "Wrote [%d] bytes to [%s] EEPROM\n", eepromsize, eepromname); - break; - case 'e': // erase - memset(readbuf, 0xff, MAX_EEPROM_SIZE); - if(ch341writeEEPROM(devHandle, readbuf, eepromsize) < 0) { - fprintf(stderr,"Failed to erase [%d] bytes of [%s] EEPROM\n", eepromsize, eepromname); - goto shutdown; - } - fprintf(stdout, "Erased [%d] bytes of [%s] EEPROM\n", eepromsize, eepromname); - break; - default: - fprintf(stderr, "Unknown option\n"); - goto shutdown; - } - -shutdown: - if(readbuf) - free(readbuf); - if(filename) - free(filename); - if(devHandle) { - libusb_release_interface(devHandle, DEFAULT_INTERFACE); - fprintf(debugout, "Released device interface [%d]\n", DEFAULT_INTERFACE); - libusb_close(devHandle); - fprintf(verbout, "Closed USB device\n"); - libusb_exit(NULL); - } - return 0; -} - diff --git a/src/ch341eeprom.h b/src/ch341eeprom.h deleted file mode 100644 index 6eab42e..0000000 --- a/src/ch341eeprom.h +++ /dev/null @@ -1,152 +0,0 @@ -// libUSB driver for the ch341a in i2c mode -// -// Copyright 2011 asbokid - -#define CH341TOOLVERSION "0.5" - -#define USB_LOCK_VENDOR 0x1a86 // Dev : (1a86) QinHeng Electronics -#define USB_LOCK_PRODUCT 0x5512 // (5512) CH341A in i2c mode - - -#define MAX_EEPROM_SIZE 16384 - -#define BULK_WRITE_ENDPOINT 0x02 -#define BULK_READ_ENDPOINT 0x82 -#define DEFAULT_INTERFACE 0x00 - -#define DEFAULT_CONFIGURATION 0x01 -#define DEFAULT_TIMEOUT 300 // 300mS for USB timeouts - -#define IN_BUF_SZ 0x100 -#define EEPROM_WRITE_BUF_SZ 0x2b // only for 24c64 / 24c32 ?? -#define EEPROM_READ_BULKIN_BUF_SZ 0x20 -#define EEPROM_READ_BULKOUT_BUF_SZ 0x65 - -// Based on (closed-source) DLL V1.9 for USB by WinChipHead (c) 2005. -// Supports USB chips: CH341, CH341A - -#define mCH341_PACKET_LENGTH 32 -#define mCH341_PKT_LEN_SHORT 8 - -#define mCH341_ENDP_INTER_UP 0x81 -#define mCH341_ENDP_INTER_DOWN 0x01 -#define mCH341_ENDP_DATA_UP 0x82 -#define mCH341_ENDP_DATA_DOWN 0x02 - -#define mCH341_VENDOR_READ 0xC0 -#define mCH341_VENDOR_WRITE 0x40 - -#define mCH341_PARA_INIT 0xB1 -#define mCH341_I2C_STATUS 0x52 -#define mCH341_I2C_COMMAND 0x53 - -#define mCH341_PARA_CMD_R0 0xAC -#define mCH341_PARA_CMD_R1 0xAD -#define mCH341_PARA_CMD_W0 0xA6 -#define mCH341_PARA_CMD_W1 0xA7 -#define mCH341_PARA_CMD_STS 0xA0 - -#define mCH341A_CMD_SET_OUTPUT 0xA1 -#define mCH341A_CMD_IO_ADDR 0xA2 -#define mCH341A_CMD_PRINT_OUT 0xA3 -#define mCH341A_CMD_SPI_STREAM 0xA8 -#define mCH341A_CMD_SIO_STREAM 0xA9 -#define mCH341A_CMD_I2C_STREAM 0xAA -#define mCH341A_CMD_UIO_STREAM 0xAB - -#define mCH341A_BUF_CLEAR 0xB2 -#define mCH341A_I2C_CMD_X 0x54 -#define mCH341A_DELAY_MS 0x5E -#define mCH341A_GET_VER 0x5F - -#define mCH341_EPP_IO_MAX ( mCH341_PACKET_LENGTH - 1 ) -#define mCH341A_EPP_IO_MAX 0xFF - -#define mCH341A_CMD_IO_ADDR_W 0x00 -#define mCH341A_CMD_IO_ADDR_R 0x80 - -#define mCH341A_CMD_I2C_STM_STA 0x74 -#define mCH341A_CMD_I2C_STM_STO 0x75 -#define mCH341A_CMD_I2C_STM_OUT 0x80 -#define mCH341A_CMD_I2C_STM_IN 0xC0 -#define mCH341A_CMD_I2C_STM_MAX ( min( 0x3F, mCH341_PACKET_LENGTH ) ) -#define mCH341A_CMD_I2C_STM_SET 0x60 -#define mCH341A_CMD_I2C_STM_US 0x40 -#define mCH341A_CMD_I2C_STM_MS 0x50 -#define mCH341A_CMD_I2C_STM_DLY 0x0F -#define mCH341A_CMD_I2C_STM_END 0x00 - -#define mCH341A_CMD_UIO_STM_IN 0x00 -#define mCH341A_CMD_UIO_STM_DIR 0x40 -#define mCH341A_CMD_UIO_STM_OUT 0x80 -#define mCH341A_CMD_UIO_STM_US 0xC0 -#define mCH341A_CMD_UIO_STM_END 0x20 - -#define mCH341_PARA_MODE_EPP 0x00 -#define mCH341_PARA_MODE_EPP17 0x00 -#define mCH341_PARA_MODE_EPP19 0x01 -#define mCH341_PARA_MODE_MEM 0x02 - - -#define CH341_I2C_LOW_SPEED 0 // low speed - 20kHz -#define CH341_I2C_STANDARD_SPEED 1 // standard speed - 100kHz -#define CH341_I2C_FAST_SPEED 2 // fast speed - 400kHz -#define CH341_I2C_HIGH_SPEED 3 // high speed - 750kHz - -// CH341a READ EEPROM setup packet for the 24c64 -// this needs putting into a struct to allow convenient access to individual elements - -#define CH341_EEPROM_READ_SETUP_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x06\x04\x00\x00" \ - "\x00\x00\x00\x00\x40\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ - "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ - "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ - "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ - "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ - "\xaa\xdf\xc0\x75\x00" - -// for 24c64 -#define CH341_EEPROM_READ_NEXT_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x10\x00\x00\x00" \ - "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ - "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ - "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ - "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\x12\x01\x19\x00" \ - "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x2e\x68\x00" \ - "\xaa\xdf\xc0\x75\x00" -#define CH341_EEPROM_READ_CMD_SZ 0x65 - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -#define TRUE 1 -#define FALSE 0 - -struct EEPROM { - char *name; - uint32_t size; -}; - -const static struct EEPROM eepromlist[] = { -//{ "24c01", 128 }, // 1kbit = 128 bits -//{ "24c02", 256 }, -//{ "24c04", 512 }, -//{ "24c08", 1024 }, -//{ "24c16", 2048 }, - { "24c32", 4096 }, // 32kbit = 4kbyte - { "24c64", 8192 }, -//{ "24c128", 16384 }, -//{ "24c256", 32768 }, -//{ "24c512", 65536 }, -//{ "24c1024", 131072}, - { 0, 0} -}; - - -int32_t ch341readEEPROM(struct libusb_device_handle *devHandle, uint8_t *buf, uint32_t bytes); -int32_t ch341writeEEPROM(struct libusb_device_handle *devHandle, uint8_t *buf, uint32_t bytes); -struct libusb_device_handle *ch341configure(uint16_t vid, uint16_t pid); -int32_t ch341setstream(struct libusb_device_handle *devHandle, uint32_t speed); -int32_t parseEEPsize(char *eepromname); - -// callback functions for async USB transfers -void cbBulkIn(struct libusb_transfer *transfer); -void cbBulkOut(struct libusb_transfer *transfer); diff --git a/src/ch341funcs.c b/src/ch341funcs.c deleted file mode 100644 index c6229ec..0000000 --- a/src/ch341funcs.c +++ /dev/null @@ -1,368 +0,0 @@ -// -// ch341eeprom programmer version 0.1 (Beta) -// -// Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A IC -// -// (c) December 2011 asbokid -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -//#include -#include -#include -#include -#include -#include "ch341eeprom.h" - -extern FILE *debugout, *verbout; -uint32_t getnextpkt; // set by the callback function -uint32_t syncackpkt; // synch / ack flag used by BULK OUT cb function -uint16_t byteoffset; -extern uint8_t *readbuf; - -// -------------------------------------------------------------------------- -// ch341configure() -// lock USB device for exclusive use -// claim default interface -// set default configuration -// retrieve device descriptor -// identify device revision -// returns *usb device handle - -struct libusb_device_handle *ch341configure(uint16_t vid, uint16_t pid) { - - struct libusb_device *dev; - struct libusb_device_handle *devHandle; - int32_t ret=0, ret2 = 0; // set to < 0 to indicate USB errors - uint32_t i = 0, j = 0; - int32_t currentConfig = 0; - - uint8_t ch341DescriptorBuffer[0x12]; - uint8_t ch341InBuffer[IN_BUF_SZ]; // 0x100 bytes in size - uint8_t ch341OutBuffer[EEPROM_READ_BULKOUT_BUF_SZ]; - - ret = libusb_init(NULL); - if(ret < 0) { - fprintf(stderr, "Couldnt initialise libusb\n"); - return NULL; - } - - libusb_set_debug(NULL, 3); // maximum debug logging level - - fprintf(verbout, "Searching USB buses for WCH CH341a i2c EEPROM programmer [%04x:%04x]\n", - USB_LOCK_VENDOR, USB_LOCK_PRODUCT); - - if(!(devHandle = libusb_open_device_with_vid_pid(NULL, USB_LOCK_VENDOR, USB_LOCK_PRODUCT))) { - fprintf(stderr, "Couldn't open device [%04x:%04x]\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); - return NULL; - } - - if(!(dev = libusb_get_device(devHandle))) { - fprintf(stderr, "Couldnt get bus number and address of device\n"); - return NULL; - } - - fprintf(verbout, "Found [%04x:%04x] as device [%d] on USB bus [%d]\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT, - libusb_get_device_address(dev), libusb_get_bus_number(dev)); - - fprintf(verbout, "Opened device [%04x:%04x]\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); - - - if(libusb_kernel_driver_active(devHandle, DEFAULT_INTERFACE)) { - ret = libusb_detach_kernel_driver(devHandle, DEFAULT_INTERFACE); - if(ret) { - fprintf(stderr, "Failed to detach kernel driver: '%s'\n", strerror(-ret)); - return NULL; - } else - fprintf(verbout, "Detached kernel driver\n"); - } - - ret = libusb_get_configuration(devHandle, ¤tConfig); - if(ret) { - fprintf(stderr, "Failed to get current device configuration: '%s'\n", strerror(-ret)); - return NULL; - } - - if(currentConfig != DEFAULT_CONFIGURATION) - ret = libusb_set_configuration(devHandle, currentConfig); - - if(ret) { - fprintf(stderr, "Failed to set device configuration to %d: '%s'\n", DEFAULT_CONFIGURATION, strerror(-ret)); - return NULL; - } - - ret = libusb_claim_interface(devHandle, DEFAULT_INTERFACE); // interface 0 - - if(ret) { - fprintf(stderr, "Failed to claim interface %d: '%s'\n", DEFAULT_INTERFACE, strerror(-ret)); - return NULL; - } - - fprintf(verbout, "Claimed device interface [%d]\n", DEFAULT_INTERFACE); - - ret = libusb_get_descriptor(devHandle, LIBUSB_DT_DEVICE, 0x00, ch341DescriptorBuffer, 0x12); - - if(ret < 0) { - fprintf(stderr, "Failed to get device descriptor: '%s'\n", strerror(-ret)); - return NULL; - } - - fprintf(verbout, "Device reported its revision [%d.%02d]\n", - ch341DescriptorBuffer[12], ch341DescriptorBuffer[13]); - - for(i=0;i<0x12;i++) - fprintf(debugout,"%02x ", ch341DescriptorBuffer[i]); - fprintf(debugout,"\n"); - - return devHandle; -} - - -// -------------------------------------------------------------------------- -// ch341setstream() -// set the i2c bus speed (speed: 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz) -int32_t ch341setstream(struct libusb_device_handle *devHandle, uint32_t speed) { - int32_t ret, i; - uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ], *outptr; - int32_t actuallen = 0; - - outptr = ch341outBuffer; - - *outptr++ = mCH341A_CMD_I2C_STREAM; - *outptr++ = mCH341A_CMD_I2C_STM_SET | (speed & 0x3); - *outptr = mCH341A_CMD_I2C_STM_END; - - ret = libusb_bulk_transfer(devHandle, BULK_WRITE_ENDPOINT, ch341outBuffer, 3, &actuallen, DEFAULT_TIMEOUT); - - if(ret < 0) { - fprintf(stderr, "ch341setstream(): Failed write %d bytes '%s'\n", 3, strerror(-ret)); - return -1; - } - - fprintf(debugout, "ch341setstream(): Wrote %d bytes: ", 3); - for(i=0; i < 3; i++) - fprintf(debugout, "%02x ", ch341outBuffer[i]); - fprintf(debugout, "\n"); - return 0; -} - -// -------------------------------------------------------------------------- -// ch341readEEPROM() -// read n bytes from device (in packets of 32 bytes) -int32_t ch341readEEPROM(struct libusb_device_handle *devHandle, uint8_t *buffer, uint32_t bytestoread) { - - uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ]; - uint8_t ch341inBuffer[IN_BUF_SZ]; // 0x100 bytes - int32_t ret = 0, i, exitflag = 0, readpktcount; - uint32_t actuallen = 0; - struct libusb_transfer *xferBulkIn, *xferBulkOut; - struct timeval tv = {0, 100}; // our async polling interval - - xferBulkIn = libusb_alloc_transfer(0); - xferBulkOut = libusb_alloc_transfer(0); - - if(!xferBulkIn || !xferBulkOut) { - fprintf(stderr, "Couldnt allocate USB transfer structures\n"); - return -1; - } - - fprintf(debugout, "Allocated USB transfer structures\n"); - - memset(ch341inBuffer, 0, EEPROM_READ_BULKIN_BUF_SZ); - memcpy(ch341outBuffer, CH341_EEPROM_READ_SETUP_CMD, EEPROM_READ_BULKOUT_BUF_SZ); - - libusb_fill_bulk_transfer(xferBulkIn, devHandle, BULK_READ_ENDPOINT, ch341inBuffer, - EEPROM_READ_BULKIN_BUF_SZ, cbBulkIn, NULL, DEFAULT_TIMEOUT); - - libusb_fill_bulk_transfer(xferBulkOut, devHandle, BULK_WRITE_ENDPOINT, - ch341outBuffer, EEPROM_READ_BULKOUT_BUF_SZ,cbBulkOut, NULL, DEFAULT_TIMEOUT); - - fprintf(debugout, "Filled USB transfer structures\n"); - - libusb_submit_transfer(xferBulkIn); - fprintf(debugout, "Submitted BULK IN start packet\n"); - libusb_submit_transfer(xferBulkOut); - fprintf(debugout, "Submitted BULK OUT setup packet\n"); - - readbuf = buffer; - - byteoffset = 0; - - while (byteoffset < bytestoread) { - fprintf(stdout, "Read [%d] of [%d] bytes \r", byteoffset, bytestoread); - ret = libusb_handle_events_timeout(NULL, &tv); - - if (ret < 0 || getnextpkt == -1) { // indicates an error - fprintf(stderr, "ret from libusb_handle_timeout = %d\n", ret); - fprintf(stderr, "getnextpkt = %d\n", getnextpkt); - fprintf(stderr, "USB read error : %s\n", strerror(-ret)); - goto out_deinit; - } - if(getnextpkt == 1) { // callback function reports a new BULK IN packet received - getnextpkt = 0; // reset the flag - readpktcount++; // increment the read packet counter - byteoffset += EEPROM_READ_BULKIN_BUF_SZ; - fprintf(debugout, "\nRe-submitting transfer request to BULK IN endpoint\n"); - libusb_submit_transfer(xferBulkIn); // re-submit request for next BULK IN packet of EEPROM data - if(syncackpkt) - syncackpkt = 0; - // if 4th packet received, we are at end of 0x80 byte data block, - // if it is not the last block, then resubmit request for data - if(readpktcount==4 && byteoffset < bytestoread) { - fprintf(debugout, "\nSubmitting next transfer request to BULK OUT endpoint\n"); - readpktcount = 0; - - memcpy(ch341outBuffer, CH341_EEPROM_READ_NEXT_CMD, CH341_EEPROM_READ_CMD_SZ); - ch341outBuffer[4] = (uint8_t) (byteoffset >> 8 & 0xff); // MSB (big-endian) byte address - ch341outBuffer[5] = (uint8_t) (byteoffset & 0xff); // LSB of 16-bit byte address - - libusb_fill_bulk_transfer(xferBulkOut, devHandle, BULK_WRITE_ENDPOINT, ch341outBuffer, - EEPROM_READ_BULKOUT_BUF_SZ, cbBulkOut, NULL, DEFAULT_TIMEOUT); - - libusb_submit_transfer(xferBulkOut);// update transfer struct (with new EEPROM page offset) - // and re-submit next transfer request to BULK OUT endpoint - } - } - } - -out_deinit: - libusb_free_transfer(xferBulkIn); - libusb_free_transfer(xferBulkOut); - return 0; -} - -// Callback function for async bulk in comms -void cbBulkIn(struct libusb_transfer *transfer) { - int i; - - switch(transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - // display the contents of the BULK IN data buffer - fprintf(debugout,"\ncbBulkIn(): status %d - Read %d bytes\n",transfer->status,transfer->actual_length); - - for(i=0; i < transfer->actual_length; i++) { - if(!(i%16)) - fprintf(debugout, "\n "); - fprintf(debugout, "%02x ", transfer->buffer[i]); - } - fprintf(debugout, "\n"); - // copy read data to our EEPROM buffer - memcpy(readbuf + byteoffset, transfer->buffer, transfer->actual_length); - getnextpkt = 1; - break; - default: - fprintf(stderr, "\ncbBulkIn: error : %d\n", transfer->status); - getnextpkt = -1; - } - return; -} - -// Callback function for async bulk out comms -void cbBulkOut(struct libusb_transfer *transfer) { - syncackpkt = 1; - fprintf(debugout, "\ncbBulkOut(): Sync/Ack received: status %d\n", transfer->status); - return; -} - -// -------------------------------------------------------------------------- -// ch341writeEEPROM() -// write n bytes to 24c32/24c64 device (in packets of 32 bytes) -int32_t ch341writeEEPROM(struct libusb_device_handle *devHandle, uint8_t *buffer, uint32_t bytesum) { - - uint8_t ch341outBuffer[EEPROM_WRITE_BUF_SZ], *outptr, *bufptr; - int32_t ret = 0, i; - uint16_t byteoffset = 0, bytes = bytesum; - uint8_t addrbytecount = 3; // 24c32 and 24c64 (and other 24c??) use 3 bytes for addressing - int32_t actuallen = 0; - - bufptr = buffer; - - while(bytes) { - outptr = ch341outBuffer; - *outptr++ = mCH341A_CMD_I2C_STREAM; - *outptr++ = mCH341A_CMD_I2C_STM_STA; - *outptr++ = mCH341A_CMD_I2C_STM_OUT + addrbytecount + MIN(bytes, 25); - *outptr++ = 0xa0; // EEPROM device address - *outptr++ = (uint8_t) (byteoffset >> 8 & 0xff); // MSB (big-endian) byte address - *outptr++ = (uint8_t) (byteoffset & 0xff); // LSB of 16-bit byte address - - memcpy(outptr, bufptr, MIN(bytes, 25)); // payload has two parts: 25 bytes & up to 7 more bytes - - outptr += MIN(bytes, 25); - bufptr += MIN(bytes, 25); - bytes -= MIN(bytes, 25); - - *outptr++ = mCH341A_CMD_I2C_STM_END; - - if(bytes) { - *outptr++ = mCH341A_CMD_I2C_STREAM; - *outptr++ = mCH341A_CMD_I2C_STM_OUT + MIN(bytes, 7); - memcpy(outptr, bufptr, MIN(bytes, 7)); - - outptr += MIN(bytes, 7); - bufptr += MIN(bytes, 7); - bytes -= MIN(bytes, 7); - } - - *outptr++ = mCH341A_CMD_I2C_STM_STO; - *outptr = mCH341A_CMD_I2C_STM_END; - - byteoffset += 0x20; - - for(i=0; i < EEPROM_WRITE_BUF_SZ; i++) { - if(!(i%0x10)) - fprintf(debugout, "\n%04x : ", i); - fprintf(debugout, "%02x ", ch341outBuffer[i]); - } - fprintf(debugout, "\n"); - - ret = libusb_bulk_transfer(devHandle, BULK_WRITE_ENDPOINT, - ch341outBuffer, EEPROM_WRITE_BUF_SZ, &actuallen, DEFAULT_TIMEOUT); - - if(ret < 0) { - fprintf(stderr, "Failed to write to EEPROM: '%s'\n", strerror(-ret)); - return -1; - } - - fprintf(debugout, "Writing [aa 5a 00] to EEPROM\n"); // Magic CH341a packet! Undocumented, unknown purpose - - outptr = ch341outBuffer; - *outptr++ = mCH341A_CMD_I2C_STREAM; - *outptr++ = 0x5a; // what is this 0x5a?? - *outptr++ = mCH341A_CMD_I2C_STM_END; - - ret = libusb_bulk_transfer(devHandle, BULK_WRITE_ENDPOINT, ch341outBuffer, 3, &actuallen, DEFAULT_TIMEOUT); - - if(ret < 0) { - fprintf(stderr, "Failed to write to EEPROM: '%s'\n", strerror(-ret)); - return -1; - } - fprintf(stdout, "Written [%d] of [%d] bytes \r", bytes, bytesum); - } - return 0; -} - - -// -------------------------------------------------------------------------- -// parseEEPsize() -// passed an EEPROM name (case-sensitive), returns its byte size -int32_t parseEEPsize(char *eepromname) { - int i; - - for(i=0; eepromlist[i].size; i++) - if(strstr(eepromlist[i].name, eepromname)) - return(eepromlist[i].size); - return -1; -} diff --git a/src/fvflash.c b/src/fvflash.c new file mode 100644 index 0000000..702ff43 --- /dev/null +++ b/src/fvflash.c @@ -0,0 +1,141 @@ +// +// ch341eeprom programmer version 0.1 (Beta) +// +// Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A +// IC +// +// (c) December 2011 asbokid +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#define CH341TOOLVERSION "0.5-fv1-heckmod" + +#include +#include +#include +#include +#include +#include "libch341eeprom.h" +#include "libch341eeprom_internal.h" + +int main(int argc, char **argv) +{ + uint8_t debug = FALSE; + uint8_t verbose = TRUE; + + // in struct + int eepromsize = 4096; + char *filename = NULL; + char *eepromname = strdup("24c32"); + char operation = 0; + + static char + version_msg[] = "fvflash - an i2c EEPROM programming tool for the WCH CH341a IC\n" + "Version " CH341TOOLVERSION + " copyright (c) 2011 asbokid \n\n" + "modified by for the specific use case of fv-1 " + "programming on macos\n\n" + "This program comes with asbolutely no warranty; This is free software,\n" + "and you are welcome to redistribute it under certain conditions:\n" + "GNU GPL v3 License: http://www.gnu.org/licenses/gpl.html\n"; + + static char usage_msg[] = "Usage:\n" + " fvflash [options] [operation] filename\n" + " writes filename into fv1 (must be in bin format)\n\n" + + "operation:\n" + " default operation is write fv1\n" + " -e, --erase erase fv1 (fill with 0xff)\n" + " -r, --read read fv1 and save image to filename\n\n" + + "options:\n" + " -h, --help display this text\n" + " -d, --debug debug output\n" + " -s, --size size of fv1 {24c32|24c64} default=24c32\n\n" + + "Example: fvflash bank.bin - writes bank.bin into fv1\n"; + + static struct option longopts[] = { + { "help", no_argument, 0, 'h' }, { "debug", no_argument, 0, 'd' }, + { "erase", no_argument, 0, 'e' }, { "size", required_argument, 0, 's' }, + { "read", required_argument, 0, 'r' }, { 0, 0, 0, 0 } + }; + + int32_t last_optidx = 0; + + while (TRUE) { + int32_t optidx = 0; + int8_t c = getopt_long(argc, argv, "hdes:r:", longopts, &optidx); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + fprintf(stdout, "%s\n%s", version_msg, usage_msg); + return 0; + case 'd': + debug = TRUE; + break; + case 's': + if ((eepromsize = parseEEPsize(optarg)) > 0) { + strcpy(eepromname, optarg); + } else { + fprintf(stderr, "Invalid EEPROM name %s \n", optarg); + exit(-4); + } + break; + case 'e': + if (!operation) + operation = 'e'; + else { + fprintf(stderr, "Conflicting command line options\n"); + exit(1); + } + break; + case 'r': + if (!operation) { + operation = 'r'; + filename = (char *)malloc(strlen(optarg) + 1); + strcpy(filename, optarg); + } else { + fprintf(stderr, "Conflicting command line options\n"); + exit(1); + } + break; + default: + case '?': + fprintf(stdout, "%s\bn", version_msg); + fprintf(stderr, "%s", usage_msg); + exit(2); + } + last_optidx = optidx; + } + + if (!operation) { + if (argc < 2) { + fprintf(stderr, "%s", usage_msg); + exit(-3); + } + printf("writing %s\n", argv[argc - 1]); + operation = 'w'; + filename = (char *)malloc(strlen(argv[argc - 1]) + 1); + strcpy(filename, argv[argc - 1]); + } + + ch341_operation_desc opdesc; + opdesc.operation = operation; + opdesc.eepromname = eepromname; + opdesc.filename = filename; + ch341_operation(opdesc); +} diff --git a/src/libch341eeprom.c b/src/libch341eeprom.c new file mode 100644 index 0000000..6ea1282 --- /dev/null +++ b/src/libch341eeprom.c @@ -0,0 +1,596 @@ +// +// ch341eeprom programmer version 0.1 (Beta) +// +// Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A IC +// +// (c) December 2011 asbokid +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include "libch341eeprom_internal.h" +#include "libch341eeprom.h" +#include +#include +#include +#include +#include +#include +#include + +uint32_t getnextpkt; // set by the callback function +uint32_t syncackpkt; // synch / ack flag used by BULK OUT cb function +uint16_t byteoffset; +uint8_t *readbuf; + +int debug_log_enabled = FALSE; + +void log_debug(const char* fmt, ...) { + if (debug_log_enabled == TRUE) { + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } +} +// -------------------------------------------------------------------------- +// parseEEPsize() +// passed an EEPROM name (case-sensitive), returns its byte size +int32_t parseEEPsize(const char *eepromname) +{ + for (int i = 0; eepromlist[i].size; i++) { + if (strstr(eepromlist[i].name, eepromname)) { + return (eepromlist[i].size); + } + } + return -1; +} + +// -------------------------------------------------------------------------- +// ch341configure() +// lock USB device for exclusive use +// claim default interface +// set default configuration +// retrieve device descriptor +// identify device revision +// returns *usb device handle + +struct libusb_device_handle *ch341configure(uint16_t vid, uint16_t pid) +{ + + struct libusb_device *dev; + struct libusb_device_handle *devHandle; + int32_t ret = 0, ret2 = 0; // set to < 0 to indicate USB errors + uint32_t i = 0, j = 0; + int32_t currentConfig = 0; + + uint8_t ch341DescriptorBuffer[0x12]; + uint8_t ch341InBuffer[IN_BUF_SZ]; // 0x100 bytes in size + uint8_t ch341OutBuffer[EEPROM_READ_BULKOUT_BUF_SZ]; + + ret = libusb_init(NULL); + if (ret < 0) { + fprintf(stderr, "Couldnt initialise libusb\n"); + return NULL; + } + + libusb_set_debug(NULL, 3); // maximum debug logging level + + log_debug( + "Searching USB buses for WCH CH341a i2c EEPROM programmer [%04x:%04x]\n", + USB_LOCK_VENDOR, + USB_LOCK_PRODUCT); + + if (!(devHandle = libusb_open_device_with_vid_pid(NULL, USB_LOCK_VENDOR, USB_LOCK_PRODUCT))) { + fprintf(stderr, "Couldn't open device [%04x:%04x]\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); + return NULL; + } + + if (!(dev = libusb_get_device(devHandle))) { + fprintf(stderr, "Couldnt get bus number and address of device\n"); + return NULL; + } + + printf( + "Found [%04x:%04x] as device [%d] on USB bus [%d]\n", + USB_LOCK_VENDOR, + USB_LOCK_PRODUCT, + libusb_get_device_address(dev), + libusb_get_bus_number(dev)); + + log_debug("Opened device [%04x:%04x]\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); + + + if (libusb_kernel_driver_active(devHandle, DEFAULT_INTERFACE)) { + ret = libusb_detach_kernel_driver(devHandle, DEFAULT_INTERFACE); + if (ret) { + fprintf(stderr, "Failed to detach kernel driver: '%s'\n", strerror(-ret)); + return NULL; + } else + log_debug("Detached kernel driver\n"); + } + + ret = libusb_get_configuration(devHandle, ¤tConfig); + if (ret) { + fprintf(stderr, "Failed to get current device configuration: '%s'\n", strerror(-ret)); + return NULL; + } + + if (currentConfig != DEFAULT_CONFIGURATION) + ret = libusb_set_configuration(devHandle, currentConfig); + + if (ret) { + fprintf( + stderr, + "Failed to set device configuration to %d: '%s'\n", + DEFAULT_CONFIGURATION, + strerror(-ret)); + return NULL; + } + + ret = libusb_claim_interface(devHandle, DEFAULT_INTERFACE); // interface 0 + + if (ret) { + fprintf(stderr, "Failed to claim interface %d: '%s'\n", DEFAULT_INTERFACE, strerror(-ret)); + return NULL; + } + + log_debug("Claimed device interface [%d]\n", DEFAULT_INTERFACE); + + ret = libusb_get_descriptor(devHandle, LIBUSB_DT_DEVICE, 0x00, ch341DescriptorBuffer, 0x12); + + if (ret < 0) { + fprintf(stderr, "Failed to get device descriptor: '%s'\n", strerror(-ret)); + return NULL; + } + + printf( + "Device reported its revision [%d.%02d]\n", + ch341DescriptorBuffer[12], + ch341DescriptorBuffer[13]); + + for (i = 0; i < 0x12; i++) { + log_debug("%02x ", ch341DescriptorBuffer[i]); + } + log_debug("\n"); + + return devHandle; +} + + +// -------------------------------------------------------------------------- +// ch341setstream() +// set the i2c bus speed (speed: 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz) +int32_t ch341setstream(struct libusb_device_handle *devHandle, uint32_t speed) +{ + int32_t ret, i; + uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ], *outptr; + int32_t actuallen = 0; + + outptr = ch341outBuffer; + + *outptr++ = mCH341A_CMD_I2C_STREAM; + *outptr++ = mCH341A_CMD_I2C_STM_SET | (speed & 0x3); + *outptr = mCH341A_CMD_I2C_STM_END; + + ret = libusb_bulk_transfer( + devHandle, + BULK_WRITE_ENDPOINT, + ch341outBuffer, + 3, + &actuallen, + DEFAULT_TIMEOUT); + + if (ret < 0) { + fprintf(stderr, "ch341setstream(): Failed write %d bytes '%s'\n", 3, strerror(-ret)); + return -1; + } + + log_debug("ch341setstream(): Wrote %d bytes: ", 3); + for (i = 0; i < 3; i++) + log_debug("%02x ", ch341outBuffer[i]); + log_debug("\n"); + return 0; +} + +// -------------------------------------------------------------------------- +// ch341readEEPROM() +// read n bytes from device (in packets of 32 bytes) +int32_t ch341readEEPROM(struct libusb_device_handle *devHandle, uint8_t *buffer, uint32_t bytestoread) +{ + + uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ]; + uint8_t ch341inBuffer[IN_BUF_SZ]; // 0x100 bytes + int32_t ret = 0, i, exitflag = 0, readpktcount; + uint32_t actuallen = 0; + struct libusb_transfer *xferBulkIn, *xferBulkOut; + struct timeval tv = { 0, 100 }; // our async polling interval + + xferBulkIn = libusb_alloc_transfer(0); + xferBulkOut = libusb_alloc_transfer(0); + + if (!xferBulkIn || !xferBulkOut) { + fprintf(stderr, "Couldnt allocate USB transfer structures\n"); + return -1; + } + + log_debug("Allocated USB transfer structures\n"); + + memset(ch341inBuffer, 0, EEPROM_READ_BULKIN_BUF_SZ); + memcpy(ch341outBuffer, CH341_EEPROM_READ_SETUP_CMD, EEPROM_READ_BULKOUT_BUF_SZ); + + libusb_fill_bulk_transfer( + xferBulkIn, + devHandle, + BULK_READ_ENDPOINT, + ch341inBuffer, + EEPROM_READ_BULKIN_BUF_SZ, + cbBulkIn, + NULL, + DEFAULT_TIMEOUT); + + libusb_fill_bulk_transfer( + xferBulkOut, + devHandle, + BULK_WRITE_ENDPOINT, + ch341outBuffer, + EEPROM_READ_BULKOUT_BUF_SZ, + cbBulkOut, + NULL, + DEFAULT_TIMEOUT); + + log_debug("Filled USB transfer structures\n"); + + libusb_submit_transfer(xferBulkIn); + log_debug("Submitted BULK IN start packet\n"); + libusb_submit_transfer(xferBulkOut); + log_debug("Submitted BULK OUT setup packet\n"); + + readbuf = buffer; + + byteoffset = 0; + + while (byteoffset < bytestoread) { + log_debug( "Read [%d] of [%d] bytes \r", byteoffset, bytestoread); + ret = libusb_handle_events_timeout(NULL, &tv); + + if (ret < 0 || getnextpkt == -1) { // indicates an error + log_debug( "ret from libusb_handle_timeout = %d\n", ret); + log_debug( "getnextpkt = %d\n", getnextpkt); + fprintf(stderr, "USB read error : %s\n", strerror(-ret)); + goto out_deinit; + } + if (getnextpkt == 1) { // callback function reports a new BULK IN packet received + getnextpkt = 0; // reset the flag + readpktcount++; // increment the read packet counter + byteoffset += EEPROM_READ_BULKIN_BUF_SZ; + log_debug("\nRe-submitting transfer request to BULK IN endpoint\n"); + libusb_submit_transfer(xferBulkIn); // re-submit request for next BULK IN packet of EEPROM data + if (syncackpkt) + syncackpkt = 0; + // if 4th packet received, we are at end of 0x80 byte data block, + // if it is not the last block, then resubmit request for data + if (readpktcount == 4 && byteoffset < bytestoread) { + log_debug("\nSubmitting next transfer request to BULK OUT endpoint\n"); + readpktcount = 0; + + memcpy(ch341outBuffer, CH341_EEPROM_READ_NEXT_CMD, CH341_EEPROM_READ_CMD_SZ); + ch341outBuffer[4] = (uint8_t)(byteoffset >> 8 & 0xff); // MSB (big-endian) byte address + ch341outBuffer[5] = (uint8_t)(byteoffset & 0xff); // LSB of 16-bit byte address + + libusb_fill_bulk_transfer( + xferBulkOut, + devHandle, + BULK_WRITE_ENDPOINT, + ch341outBuffer, + EEPROM_READ_BULKOUT_BUF_SZ, + cbBulkOut, + NULL, + DEFAULT_TIMEOUT); + + libusb_submit_transfer( + xferBulkOut); // update transfer struct (with new EEPROM page offset) + // and re-submit next transfer request to BULK OUT endpoint + } + } + } + +out_deinit: + libusb_free_transfer(xferBulkIn); + libusb_free_transfer(xferBulkOut); + return 0; +} + +// Callback function for async bulk in comms +void cbBulkIn(struct libusb_transfer *transfer) +{ + int i; + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + // display the contents of the BULK IN data buffer + log_debug( + "\ncbBulkIn(): status %d - Read %d bytes\n", + transfer->status, + transfer->actual_length); + + for (i = 0; i < transfer->actual_length; i++) { + if (!(i % 16)) + log_debug("\n "); + log_debug("%02x ", transfer->buffer[i]); + } + log_debug("\n"); + // copy read data to our EEPROM buffer + memcpy(readbuf + byteoffset, transfer->buffer, transfer->actual_length); + getnextpkt = 1; + break; + default: + fprintf(stderr, "\ncbBulkIn: error : %d\n", transfer->status); + getnextpkt = -1; + } + return; +} + +// Callback function for async bulk out comms +void cbBulkOut(struct libusb_transfer *transfer) +{ + syncackpkt = 1; + log_debug("\ncbBulkOut(): Sync/Ack received: status %d\n", transfer->status); + return; +} + +// -------------------------------------------------------------------------- +// ch341writeEEPROM() +// write n bytes to 24c32/24c64 device (in packets of 32 bytes) +int32_t ch341writeEEPROM(struct libusb_device_handle *devHandle, uint8_t *buffer, uint32_t bytesum) +{ + uint8_t ch341outBuffer[EEPROM_WRITE_BUF_SZ], *outptr, *bufptr; + int32_t ret = 0, i; + uint16_t byteoffset = 0, bytes = bytesum; + uint8_t addrbytecount = 3; // 24c32 and 24c64 (and other 24c??) use 3 bytes for addressing + int32_t actuallen = 0; + + bufptr = buffer; + + while (bytes) { + outptr = ch341outBuffer; + *outptr++ = mCH341A_CMD_I2C_STREAM; + *outptr++ = mCH341A_CMD_I2C_STM_STA; + *outptr++ = mCH341A_CMD_I2C_STM_OUT + addrbytecount + MIN(bytes, 25); + *outptr++ = 0xa0; // EEPROM device address + *outptr++ = (uint8_t)(byteoffset >> 8 & 0xff); // MSB (big-endian) byte address + *outptr++ = (uint8_t)(byteoffset & 0xff); // LSB of 16-bit byte address + + memcpy(outptr, bufptr, MIN(bytes, 25)); // payload has two parts: 25 bytes & up to 7 more bytes + + outptr += MIN(bytes, 25); + bufptr += MIN(bytes, 25); + bytes -= MIN(bytes, 25); + + *outptr++ = mCH341A_CMD_I2C_STM_END; + + if (bytes) { + *outptr++ = mCH341A_CMD_I2C_STREAM; + *outptr++ = mCH341A_CMD_I2C_STM_OUT + MIN(bytes, 7); + memcpy(outptr, bufptr, MIN(bytes, 7)); + + outptr += MIN(bytes, 7); + bufptr += MIN(bytes, 7); + bytes -= MIN(bytes, 7); + } + + *outptr++ = mCH341A_CMD_I2C_STM_STO; + *outptr = mCH341A_CMD_I2C_STM_END; + + byteoffset += 0x20; + + for (i = 0; i < EEPROM_WRITE_BUF_SZ; i++) { + if (!(i % 0x10)) + log_debug("\n%04x : ", i); + log_debug("%02x ", ch341outBuffer[i]); + } + log_debug("\n"); + + ret = libusb_bulk_transfer( + devHandle, + BULK_WRITE_ENDPOINT, + ch341outBuffer, + EEPROM_WRITE_BUF_SZ, + &actuallen, + DEFAULT_TIMEOUT); + + if (ret < 0) { + fprintf(stderr, "Failed to write to EEPROM: '%s'\n", strerror(-ret)); + return -1; + } + + log_debug("Writing [aa 5a 00] to EEPROM\n"); // Magic CH341a packet! Undocumented, unknown purpose + + outptr = ch341outBuffer; + *outptr++ = mCH341A_CMD_I2C_STREAM; + *outptr++ = 0x5a; // what is this 0x5a?? + *outptr++ = mCH341A_CMD_I2C_STM_END; + + ret = libusb_bulk_transfer( + devHandle, + BULK_WRITE_ENDPOINT, + ch341outBuffer, + 3, + &actuallen, + DEFAULT_TIMEOUT); + + if (ret < 0) { + fprintf(stderr, "Failed to write to EEPROM: '%s'\n", strerror(-ret)); + return -1; + } + log_debug( "Written [%d] of [%d] bytes \r", bytes, bytesum); + } + return 0; +} + + +int ch341_operation(ch341_operation_desc opdesc) +{ + int ret = 0; + + + + struct libusb_device_handle *devHandle = NULL; + FILE *fp; + int bytesread = 0; + int eepromsize = 0; + + if ((eepromsize = parseEEPsize(opdesc.eepromname)) <= 0) { + fprintf(stderr, "Invalid EEPROM name %s \n", opdesc.eepromname); + + goto error; + } + + if (eepromsize <= 0) { + fprintf(stderr, "Invalid EEPROM size\n"); + ret = 255; + goto error; + } + + readbuf = (uint8_t *)malloc(MAX_EEPROM_SIZE); // space to store loaded EEPROM + if (!readbuf) { + fprintf(stderr, "Couldnt malloc space needed for EEPROM image\n"); + ret = 255; + goto error; + } + + if (!(devHandle = ch341configure(USB_LOCK_VENDOR, USB_LOCK_PRODUCT))) { + fprintf(stderr, "Couldnt configure USB device\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); + ret = 255; + goto error; + } + log_debug("Configured USB device\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); + + if (ch341setstream(devHandle, CH341_I2C_STANDARD_SPEED) < 0) { + fprintf(stderr, "Couldnt set i2c bus speed\n"); + ret = 255; + goto error; + } + log_debug("Set i2c bus speed to [100kHz]\n"); + + switch (opdesc.operation) { + case 'r': // read + memset(readbuf, 0xff, MAX_EEPROM_SIZE); + + if (ch341readEEPROM(devHandle, readbuf, eepromsize) < 0) { + fprintf( + stderr, + "Couldnt read [%d] bytes from [%s] EEPROM\n", + eepromsize, + opdesc.eepromname); + ret = 255; + goto error; + } + fprintf(stdout, "Read [%d] bytes from [%s] EEPROM\n", eepromsize, opdesc.eepromname); + for (int i = 0; i < eepromsize; i++) { + if (!(i % 16)) + log_debug("\n%04x: ", i); + log_debug("%02x ", readbuf[i]); + } + log_debug("\n"); + + if (!(fp = fopen(opdesc.filename, "wb"))) { + fprintf(stderr, "Couldnt open file [%s] for writing\n", opdesc.filename); + ret = 255; + goto error; + } + + fwrite(readbuf, 1, eepromsize, fp); + if (ferror(fp)) { + fprintf(stderr, "Error writing file [%s]\n", opdesc.filename); + if (fp) + fclose(fp); + ret = 255; + goto error; + } + fclose(fp); + fprintf(stdout, "Wrote [%d] bytes to file [%s]\n", eepromsize, opdesc.filename); + break; + case 'w': // write + if (!(fp = fopen(opdesc.filename, "rb"))) { + fprintf(stderr, "Couldnt open file [%s] for reading\n", opdesc.filename); + ret = 255; + goto error; + } + memset(readbuf, 0xff, MAX_EEPROM_SIZE); + bytesread = fread(readbuf, 1, MAX_EEPROM_SIZE, fp); + if (ferror(fp)) { + fprintf(stderr, "Error reading file [%s]\n", opdesc.filename); + if (fp) + fclose(fp); + ret = 255; + goto error; + } + fclose(fp); + fprintf(stdout, "Read [%d] bytes from file [%s]\n", bytesread, opdesc.filename); + + if (bytesread < eepromsize) + fprintf(stdout, "Padded to [%d] bytes for [%s] EEPROM\n", eepromsize, opdesc.eepromname); + + if (bytesread > eepromsize) + fprintf( + stdout, + "Truncated to [%d] bytes for [%s] EEPROM\n", + eepromsize, + opdesc.eepromname); + + if (ch341writeEEPROM(devHandle, readbuf, eepromsize) < 0) { + fprintf( + stderr, + "Failed to write [%d] bytes from [%s] to [%s] EEPROM\n", + eepromsize, + opdesc.filename, + opdesc.eepromname); + ret = 255; + goto error; + } + fprintf(stdout, "Wrote [%d] bytes to [%s] EEPROM\n", eepromsize, opdesc.eepromname); + break; + case 'e': // erase + memset(readbuf, 0xff, MAX_EEPROM_SIZE); + if (ch341writeEEPROM(devHandle, readbuf, eepromsize) < 0) { + fprintf( + stderr, + "Failed to erase [%d] bytes of [%s] EEPROM\n", + eepromsize, + opdesc.eepromname); + ret = 255; + goto error; + } + fprintf(stdout, "Erased [%d] bytes of [%s] EEPROM\n", eepromsize, opdesc.eepromname); + break; + default: + fprintf(stderr, "Unknown option\n"); + ret = 255; + goto error; + } + +error: + if (readbuf) { + free(readbuf); + } + if (devHandle) { + libusb_release_interface(devHandle, DEFAULT_INTERFACE); + printf("Released device interface [%d]\n", DEFAULT_INTERFACE); + libusb_close(devHandle); + printf("Closed USB device\n"); + libusb_exit(NULL); + } + return ret; +} diff --git a/src/libch341eeprom.h b/src/libch341eeprom.h new file mode 100644 index 0000000..3c3cf5d --- /dev/null +++ b/src/libch341eeprom.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + // THIS IS THE COMPLETE API + + typedef struct _ch341_operation_desc { + char operation; + char* eepromname; + char* filename; + } ch341_operation_desc; + + int32_t parseEEPsize(const char *eepromname); + int ch341_operation(ch341_operation_desc opdesc); + +#ifdef __cplusplus +}; +#endif diff --git a/src/libch341eeprom_internal.h b/src/libch341eeprom_internal.h new file mode 100644 index 0000000..9b80b07 --- /dev/null +++ b/src/libch341eeprom_internal.h @@ -0,0 +1,150 @@ +// libUSB driver for the ch341a in i2c mode +// +// Copyright 2011 asbokid + +#define USB_LOCK_VENDOR 0x1a86 // Dev : (1a86) QinHeng Electronics +#define USB_LOCK_PRODUCT 0x5512 // (5512) CH341A in i2c mode + +#define MAX_EEPROM_SIZE 16384 + +#define BULK_WRITE_ENDPOINT 0x02 +#define BULK_READ_ENDPOINT 0x82 +#define DEFAULT_INTERFACE 0x00 + +#define DEFAULT_CONFIGURATION 0x01 +#define DEFAULT_TIMEOUT 300 // 300mS for USB timeouts + +#define IN_BUF_SZ 0x100 +#define EEPROM_WRITE_BUF_SZ 0x2b // only for 24c64 / 24c32 ?? +#define EEPROM_READ_BULKIN_BUF_SZ 0x20 +#define EEPROM_READ_BULKOUT_BUF_SZ 0x65 + +// Based on (closed-source) DLL V1.9 for USB by WinChipHead (c) 2005. +// Supports USB chips: CH341, CH341A + +#define mCH341_PACKET_LENGTH 32 +#define mCH341_PKT_LEN_SHORT 8 + +#define mCH341_ENDP_INTER_UP 0x81 +#define mCH341_ENDP_INTER_DOWN 0x01 +#define mCH341_ENDP_DATA_UP 0x82 +#define mCH341_ENDP_DATA_DOWN 0x02 + +#define mCH341_VENDOR_READ 0xC0 +#define mCH341_VENDOR_WRITE 0x40 + +#define mCH341_PARA_INIT 0xB1 +#define mCH341_I2C_STATUS 0x52 +#define mCH341_I2C_COMMAND 0x53 + +#define mCH341_PARA_CMD_R0 0xAC +#define mCH341_PARA_CMD_R1 0xAD +#define mCH341_PARA_CMD_W0 0xA6 +#define mCH341_PARA_CMD_W1 0xA7 +#define mCH341_PARA_CMD_STS 0xA0 + +#define mCH341A_CMD_SET_OUTPUT 0xA1 +#define mCH341A_CMD_IO_ADDR 0xA2 +#define mCH341A_CMD_PRINT_OUT 0xA3 +#define mCH341A_CMD_SPI_STREAM 0xA8 +#define mCH341A_CMD_SIO_STREAM 0xA9 +#define mCH341A_CMD_I2C_STREAM 0xAA +#define mCH341A_CMD_UIO_STREAM 0xAB + +#define mCH341A_BUF_CLEAR 0xB2 +#define mCH341A_I2C_CMD_X 0x54 +#define mCH341A_DELAY_MS 0x5E +#define mCH341A_GET_VER 0x5F + +#define mCH341_EPP_IO_MAX (mCH341_PACKET_LENGTH - 1) +#define mCH341A_EPP_IO_MAX 0xFF + +#define mCH341A_CMD_IO_ADDR_W 0x00 +#define mCH341A_CMD_IO_ADDR_R 0x80 + +#define mCH341A_CMD_I2C_STM_STA 0x74 +#define mCH341A_CMD_I2C_STM_STO 0x75 +#define mCH341A_CMD_I2C_STM_OUT 0x80 +#define mCH341A_CMD_I2C_STM_IN 0xC0 +#define mCH341A_CMD_I2C_STM_MAX (min(0x3F, mCH341_PACKET_LENGTH)) +#define mCH341A_CMD_I2C_STM_SET 0x60 +#define mCH341A_CMD_I2C_STM_US 0x40 +#define mCH341A_CMD_I2C_STM_MS 0x50 +#define mCH341A_CMD_I2C_STM_DLY 0x0F +#define mCH341A_CMD_I2C_STM_END 0x00 + +#define mCH341A_CMD_UIO_STM_IN 0x00 +#define mCH341A_CMD_UIO_STM_DIR 0x40 +#define mCH341A_CMD_UIO_STM_OUT 0x80 +#define mCH341A_CMD_UIO_STM_US 0xC0 +#define mCH341A_CMD_UIO_STM_END 0x20 + +#define mCH341_PARA_MODE_EPP 0x00 +#define mCH341_PARA_MODE_EPP17 0x00 +#define mCH341_PARA_MODE_EPP19 0x01 +#define mCH341_PARA_MODE_MEM 0x02 + +#define CH341_I2C_LOW_SPEED 0 // low speed - 20kHz +#define CH341_I2C_STANDARD_SPEED 1 // standard speed - 100kHz +#define CH341_I2C_FAST_SPEED 2 // fast speed - 400kHz +#define CH341_I2C_HIGH_SPEED 3 // high speed - 750kHz + +// CH341a READ EEPROM setup packet for the 24c64 +// this needs putting into a struct to allow convenient access to individual +// elements + +#define CH341_EEPROM_READ_SETUP_CMD \ + "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x06\x04\x00\x00" \ + "\x00\x00\x00\x00\x40\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ + "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ + "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ + "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ + "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ + "\xaa\xdf\xc0\x75\x00" + +// for 24c64 +#define CH341_EEPROM_READ_NEXT_CMD \ + "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x10\x00\x00\x00" \ + "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ + "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ + "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ + "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\x12\x01\x19\x00" \ + "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x2e\x68\x00" \ + "\xaa\xdf\xc0\x75\x00" +#define CH341_EEPROM_READ_CMD_SZ 0x65 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define TRUE 1 +#define FALSE 0 + +struct EEPROM { + char *name; + uint32_t size; +}; + +const static struct EEPROM eepromlist[] = { + //{ "24c01", 128 }, // 1kbit = 128 bits + //{ "24c02", 256 }, + //{ "24c04", 512 }, + //{ "24c08", 1024 }, + //{ "24c16", 2048 }, + { "24c32", 4096 }, // 32kbit = 4kbyte + { "24c64", 8192 }, + //{ "24c128", 16384 }, + //{ "24c256", 32768 }, + //{ "24c512", 65536 }, + //{ "24c1024", 131072}, + { 0, 0 } +}; + + +int32_t ch341readEEPROM(struct libusb_device_handle *devHandle, uint8_t *buf, uint32_t bytes); +int32_t ch341writeEEPROM(struct libusb_device_handle *devHandle, uint8_t *buf, uint32_t bytes); +struct libusb_device_handle *ch341configure(uint16_t vid, uint16_t pid); +int32_t ch341setstream(struct libusb_device_handle *devHandle, uint32_t speed); + +// callback functions for async USB transfers +void cbBulkIn(struct libusb_transfer *transfer); +void cbBulkOut(struct libusb_transfer *transfer);