forked from setarcos/ch341prog
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit with minimal functions.
- Loading branch information
0 parents
commit be554c6
Showing
6 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.o | ||
ch341prog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CFLAGS=-std=gnu99 -Wall | ||
ch341prog: main.c ch341a.c | ||
gcc $(CFLAGS) ch341a.c main.c -o ch341prog -lusb-1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
Ch341Prog | ||
============ | ||
A simple programmer based on ch341a multi-functional chip. | ||
|
||
Description | ||
------------ | ||
There are a lot cheap SPI/IIC programmers based on ch341a, which can be | ||
as low as $3. However, the best I can find is a tool called `ch341eeprom' | ||
which can only deal with IIC EEPROMs, so I decide to write my own. | ||
|
||
After reading the source for ch341eeprom and sniffering the usb traffic, | ||
I thinks I have fully understood the usb protocol. So, here is what I have | ||
got so far. | ||
|
||
TODO List | ||
------------ | ||
* find and configure ch341 (done). | ||
* read flash ID (done). | ||
* read flash contents. | ||
* clear flash | ||
* blank check | ||
* write to flash memory |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#include <libusb-1.0/libusb.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <stdint.h> | ||
#include <string.h> | ||
#include "ch341a.h" | ||
|
||
/* Configure CH341A, find the device and set the default interface. */ | ||
struct libusb_device_handle *ch341Configure(uint16_t vid, uint16_t pid) | ||
{ | ||
struct libusb_device *dev; | ||
struct libusb_device_handle *devHandle; | ||
int32_t ret; | ||
|
||
uint8_t desc[0x12]; | ||
|
||
ret = libusb_init(NULL); | ||
if(ret < 0) { | ||
fprintf(stderr, "Couldnt initialise libusb\n"); | ||
return NULL; | ||
} | ||
|
||
libusb_set_debug(NULL, 3); | ||
|
||
if(!(devHandle = libusb_open_device_with_vid_pid(NULL, vid, pid))) { | ||
fprintf(stderr, "Couldn't open device [%04x:%04x].\n", vid, pid); | ||
return NULL; | ||
} | ||
|
||
if(!(dev = libusb_get_device(devHandle))) { | ||
fprintf(stderr, "Couldn't get bus number and address.\n"); | ||
return NULL; | ||
} | ||
|
||
if(libusb_kernel_driver_active(devHandle, 0)) { | ||
ret = libusb_detach_kernel_driver(devHandle, 0); | ||
if(ret) { | ||
fprintf(stderr, "Failed to detach kernel driver: '%s'\n", strerror(-ret)); | ||
return NULL; | ||
} | ||
} | ||
|
||
ret = libusb_claim_interface(devHandle, 0); | ||
|
||
if(ret) { | ||
fprintf(stderr, "Failed to claim interface 0: '%s'\n", strerror(-ret)); | ||
return NULL; | ||
} | ||
|
||
ret = libusb_get_descriptor(devHandle, LIBUSB_DT_DEVICE, 0x00, desc, 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", desc[12], desc[13]); | ||
return devHandle; | ||
} | ||
|
||
int32_t ch341Release(struct libusb_device_handle *devHandle) | ||
{ | ||
libusb_release_interface(devHandle, 0); | ||
libusb_close(devHandle); | ||
libusb_exit(NULL); | ||
return 0; | ||
} | ||
|
||
int32_t usbTransfer(const char * func, struct libusb_device_handle *devHandle, uint8_t type, uint8_t* buf, int len) | ||
{ | ||
int32_t ret; | ||
int transfered; | ||
ret = libusb_bulk_transfer(devHandle, type, buf, len, &transfered, DEFAULT_TIMEOUT); | ||
if (ret < 0) { | ||
fprintf(stderr, "%s: Failed to %s %d bytes '%s'\n", func, | ||
(type == BULK_WRITE_ENDPOINT) ? "write" : "read", len, strerror(-ret)); | ||
return -1; | ||
} | ||
return transfered; | ||
} | ||
|
||
/* set the i2c bus speed (speed(b1b0): 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz) | ||
* set the spi bus data width(speed(b2): 0 = Single, 1 = Double) */ | ||
int32_t ch341SetStream(struct libusb_device_handle *devHandle, uint32_t speed) { | ||
uint8_t buf[3]; | ||
|
||
buf[0] = CH341A_CMD_I2C_STREAM; | ||
buf[1] = CH341A_CMD_I2C_STM_SET | (speed & 0x7); | ||
buf[2] = CH341A_CMD_I2C_STM_END; | ||
|
||
return usbTransfer(__func__, devHandle, BULK_WRITE_ENDPOINT, buf, 3); | ||
} | ||
|
||
uint8_t swapByte(uint8_t c) | ||
{ | ||
uint8_t result=0; | ||
for (int i = 0; i < 8; ++i) | ||
{ | ||
result = result << 1; | ||
result |= (c & 1); | ||
c = c >> 1; | ||
} | ||
return result; | ||
} | ||
|
||
int32_t ch341SpiStream(struct libusb_device_handle *devHandle, uint8_t *out, uint8_t *in, uint32_t len) | ||
{ | ||
uint8_t outBuf[CH341_MAX_PACKET_LEN], *outp; | ||
|
||
if (len > CH341_MAX_PACKET_LEN - CH341_PACKET_LENGTH) | ||
return -1; | ||
outp = outBuf; | ||
*outp++ = CH341A_CMD_UIO_STREAM; | ||
*outp++ = CH341A_CMD_UIO_STM_OUT | 0x36; // chip select | ||
*outp++ = CH341A_CMD_UIO_STM_DIR | 0x3F; // pin direction | ||
*outp++ = CH341A_CMD_UIO_STM_END; | ||
outp = outBuf + CH341_PACKET_LENGTH; // don't care what's after CH341A_CMD_UIO_STM_END | ||
*outp++ = CH341A_CMD_SPI_STREAM; | ||
for (int i = 0; i < len; ++i) | ||
*outp++ = swapByte(*out++); | ||
usbTransfer(__func__, devHandle, BULK_WRITE_ENDPOINT, outBuf, len + CH341_PACKET_LENGTH + 1); | ||
usbTransfer(__func__, devHandle, BULK_READ_ENDPOINT, in, len); | ||
outp = outBuf; | ||
*outp++ = CH341A_CMD_UIO_STREAM; | ||
*outp++ = CH341A_CMD_UIO_STM_OUT | 0x37; // chip deselect | ||
*outp++ = CH341A_CMD_UIO_STM_END; | ||
usbTransfer(__func__, devHandle, BULK_WRITE_ENDPOINT, outBuf, 3); | ||
return 0; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#ifndef __CH341_H__ | ||
#define __CH341_H__ | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
#define DEFAULT_TIMEOUT 300 // 300mS for USB timeouts | ||
#define BULK_WRITE_ENDPOINT 0x02 | ||
#define BULK_READ_ENDPOINT 0x82 | ||
|
||
#define CH341_PACKET_LENGTH 0x20 | ||
#define CH341_MAX_PACKET_LEN 4096 | ||
#define CH341A_USB_VENDOR 0x1A86 | ||
#define CH341A_USB_PRODUCT 0x5512 | ||
|
||
#define CH341A_CMD_SET_OUTPUT 0xA1 | ||
#define CH341A_CMD_IO_ADDR 0xA2 | ||
#define CH341A_CMD_PRINT_OUT 0xA3 | ||
#define CH341A_CMD_SPI_STREAM 0xA8 | ||
#define CH341A_CMD_SIO_STREAM 0xA9 | ||
#define CH341A_CMD_I2C_STREAM 0xAA | ||
#define CH341A_CMD_UIO_STREAM 0xAB | ||
|
||
#define CH341A_CMD_I2C_STM_STA 0x74 | ||
#define CH341A_CMD_I2C_STM_STO 0x75 | ||
#define CH341A_CMD_I2C_STM_OUT 0x80 | ||
#define CH341A_CMD_I2C_STM_IN 0xC0 | ||
#define CH341A_CMD_I2C_STM_MAX ( min( 0x3F, CH341_PACKET_LENGTH ) ) | ||
#define CH341A_CMD_I2C_STM_SET 0x60 | ||
#define CH341A_CMD_I2C_STM_US 0x40 | ||
#define CH341A_CMD_I2C_STM_MS 0x50 | ||
#define CH341A_CMD_I2C_STM_DLY 0x0F | ||
#define CH341A_CMD_I2C_STM_END 0x00 | ||
|
||
#define CH341A_CMD_UIO_STM_IN 0x00 | ||
#define CH341A_CMD_UIO_STM_DIR 0x40 | ||
#define CH341A_CMD_UIO_STM_OUT 0x80 | ||
#define CH341A_CMD_UIO_STM_US 0xC0 | ||
#define CH341A_CMD_UIO_STM_END 0x20 | ||
|
||
|
||
int32_t usbTransfer(const char * func, struct libusb_device_handle *devHandle, uint8_t type, uint8_t* buf, int len); | ||
struct libusb_device_handle *ch341Configure(uint16_t vid, uint16_t pid); | ||
int32_t ch341SetStream(struct libusb_device_handle *devHandle, uint32_t speed); | ||
int32_t ch341SpiStream(struct libusb_device_handle *devHandle, uint8_t *out, uint8_t *in, uint32_t len); | ||
int32_t ch341Release(struct libusb_device_handle *devHandle); | ||
uint8_t swapByte(uint8_t c); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#include <libusb-1.0/libusb.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include "ch341a.h" | ||
|
||
int main(int argc, char* argv[]) | ||
{ | ||
struct libusb_device_handle *devHandle = NULL; | ||
uint8_t out[10]; | ||
uint8_t in[10], *ptr; | ||
uint32_t ret; | ||
|
||
devHandle = ch341Configure(CH341A_USB_VENDOR, CH341A_USB_PRODUCT); | ||
if (devHandle == NULL) | ||
return -1; | ||
ret = ch341SetStream(devHandle, 1); | ||
if (ret < 0) goto out; | ||
ptr = out; | ||
*ptr++ = 0x9F; // Read JEDEC ID | ||
for (int i = 0; i < 3; ++i) | ||
*ptr++ = 0x00; | ||
ret = ch341SpiStream(devHandle, out, in, 4); | ||
if (ret < 0) goto out; | ||
printf("Manufacturer ID: %02x\n", in[1]); | ||
printf("Memory Type: %02x\n", in[2]); | ||
printf("Capacity: %02x\n", in[3]); | ||
out: | ||
ch341Release(devHandle); | ||
return 0; | ||
} |