
7 changed files with 906 additions and 759 deletions
@ -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 <ballymunboy@gmail.com>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
|
|
||||
#include <libusb-1.0/libusb.h> |
|
||||
//#include <asm/errno.h>
|
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdint.h> |
|
||||
#include <string.h> |
|
||||
#include <inttypes.h> |
|
||||
#include <getopt.h> |
|
||||
#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 <ballymunboy@gmail.com>\n\n" \ |
|
||||
"modified by <heck@heck.live> 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 <filename> 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;i++) { |
|
||||
if(!(i%16)) |
|
||||
fprintf(debugout, "\n%04x: ", i); |
|
||||
fprintf(debugout, "%02x ", readbuf[i]); |
|
||||
} |
|
||||
fprintf(debugout, "\n"); |
|
||||
|
|
||||
if(!(fp=fopen(filename, "wb"))) { |
|
||||
fprintf(stderr, "Couldnt open file [%s] for writing\n", filename); |
|
||||
goto shutdown; |
|
||||
} |
|
||||
|
|
||||
fwrite(readbuf, 1, eepromsize, fp); |
|
||||
if(ferror(fp)) { |
|
||||
fprintf(stderr, "Error writing file [%s]\n", filename); |
|
||||
if(fp) |
|
||||
fclose(fp); |
|
||||
goto shutdown; |
|
||||
} |
|
||||
fclose(fp); |
|
||||
fprintf(stdout, "Wrote [%d] bytes to file [%s]\n", eepromsize, filename); |
|
||||
break; |
|
||||
case 'w': // write
|
|
||||
if(!(fp=fopen(filename, "rb"))) { |
|
||||
fprintf(stderr, "Couldnt open file [%s] for reading\n", filename); |
|
||||
goto shutdown; |
|
||||
} |
|
||||
memset(readbuf, 0xff, MAX_EEPROM_SIZE); |
|
||||
bytesread = fread(readbuf, 1, MAX_EEPROM_SIZE, fp); |
|
||||
if(ferror(fp)) { |
|
||||
fprintf(stderr, "Error reading file [%s]\n", filename); |
|
||||
if(fp) |
|
||||
fclose(fp); |
|
||||
goto shutdown; |
|
||||
} |
|
||||
fclose(fp); |
|
||||
fprintf(stdout, "Read [%d] bytes from file [%s]\n", bytesread, filename); |
|
||||
|
|
||||
if(bytesread < eepromsize) |
|
||||
fprintf(stdout, "Padded to [%d] bytes for [%s] EEPROM\n", eepromsize, eepromname); |
|
||||
|
|
||||
if(bytesread > 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; |
|
||||
} |
|
||||
|
|
@ -1,152 +0,0 @@ |
|||||
// libUSB driver for the ch341a in i2c mode
|
|
||||
//
|
|
||||
// Copyright 2011 asbokid <ballymunboy@gmail.com>
|
|
||||
|
|
||||
#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); |
|
@ -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 <ballymunboy@gmail.com>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
|
|
||||
#include <libusb-1.0/libusb.h> |
|
||||
//#include <asm/errno.h>
|
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdint.h> |
|
||||
#include <string.h> |
|
||||
#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; |
|
||||
} |
|
@ -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 <ballymunboy@gmail.com>
|
||||
|
//
|
||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
#define CH341TOOLVERSION "0.5-fv1-heckmod" |
||||
|
|
||||
|
#include <getopt.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#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 <ballymunboy@gmail.com>\n\n" |
||||
|
"modified by <heck@heck.live> 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 <filename> 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); |
||||
|
} |
@ -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 <ballymunboy@gmail.com>
|
||||
|
//
|
||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
#include <libusb-1.0/libusb.h> |
||||
|
#include "libch341eeprom_internal.h" |
||||
|
#include "libch341eeprom.h" |
||||
|
#include <inttypes.h> |
||||
|
#include <string.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <stdint.h> |
||||
|
#include <string.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdarg.h> |
||||
|
|
||||
|
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; |
||||
|
} |
@ -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 |
@ -0,0 +1,150 @@ |
|||||
|
// libUSB driver for the ch341a in i2c mode
|
||||
|
//
|
||||
|
// Copyright 2011 asbokid <ballymunboy@gmail.com>
|
||||
|
|
||||
|
#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); |
Loading…
Reference in new issue