Skip to content

Commit

Permalink
Initial commit with minimal functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
setarcos committed May 19, 2014
0 parents commit be554c6
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.o
ch341prog
3 changes: 3 additions & 0 deletions Makefile
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
22 changes: 22 additions & 0 deletions README.md
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
130 changes: 130 additions & 0 deletions ch341a.c
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;
}

53 changes: 53 additions & 0 deletions ch341a.h
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
30 changes: 30 additions & 0 deletions main.c
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;
}

0 comments on commit be554c6

Please sign in to comment.