[flashrom] [commit] r1921 - trunk

repository service svn at flashrom.org
Sun Jan 31 23:10:29 CET 2016


Author: stefanct
Date: Sun Jan 31 23:10:29 2016
New Revision: 1921
URL: http://flashrom.org/trac/flashrom/changeset/1921

Log:
Add support for WCH CH341A as an SPI programmer.

Signed-off-by: Urja Rannikko <urjaman at gmail.com>
Signed-off-by: Stefan Tauner <stefan.tauner at alumni.tuwien.ac.at>
Acked-by: Urja Rannikko <urjaman at gmail.com>

Added:
   trunk/ch341a_spi.c   (contents, props changed)
Modified:
   trunk/Makefile
   trunk/flashrom.8.tmpl
   trunk/flashrom.c
   trunk/programmer.h

Modified: trunk/Makefile
==============================================================================
--- trunk/Makefile	Sun Jan 31 23:10:14 2016	(r1920)
+++ trunk/Makefile	Sun Jan 31 23:10:29 2016	(r1921)
@@ -157,7 +157,7 @@
 else
 override CONFIG_PONY_SPI = no
 endif
-# Dediprog, USB-Blaster, PICkit2 and FT2232 are not supported under DOS (missing USB support).
+# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported under DOS (missing USB support).
 ifeq ($(CONFIG_DEDIPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes
 else
@@ -178,6 +178,11 @@
 else
 override CONFIG_PICKIT2_SPI = no
 endif
+ifeq ($(CONFIG_CH341A_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
+else
+override CONFIG_CH341A_SPI = no
+endif
 endif
 
 # FIXME: Should we check for Cygwin/MSVC as well?
@@ -303,7 +308,7 @@
 else
 override CONFIG_PONY_SPI = no
 endif
-# Dediprog, USB-Blaster, PICkit2 and FT2232 are not supported with libpayload (missing libusb support)
+# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported with libpayload (missing libusb support)
 ifeq ($(CONFIG_DEDIPROG), yes)
 UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes
 else
@@ -324,6 +329,11 @@
 else
 override CONFIG_PICKIT2_SPI = no
 endif
+ifeq ($(CONFIG_CH341A_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
+else
+override CONFIG_CH341A_SPI = no
+endif
 endif
 
 ifneq ($(TARGET_OS), Linux)
@@ -505,6 +515,9 @@
 # Always enable ITE IT8212F PATA controllers for now.
 CONFIG_IT8212 ?= yes
 
+# Winchiphead CH341A
+CONFIG_CH341A_SPI ?= yes
+
 # Disable wiki printing by default. It is only useful if you have wiki access.
 CONFIG_PRINT_WIKI ?= no
 
@@ -751,6 +764,12 @@
 PROGRAMMER_OBJS += mstarddc_spi.o
 endif
 
+ifeq ($(CONFIG_CH341A_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_CH341A_SPI=1'
+PROGRAMMER_OBJS += ch341a_spi.o
+NEED_LIBUSB1 := yes
+endif
+
 ifeq ($(NEED_SERIAL), yes)
 LIB_OBJS += serial.o
 endif

Added: trunk/ch341a_spi.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/ch341a_spi.c	Sun Jan 31 23:10:29 2016	(r1921)
@@ -0,0 +1,535 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 asbokid <ballymunboy at gmail.com>
+ * Copyright (C) 2014 Pluto Yang <yangyj.ee at gmail.com>
+ * Copyright (C) 2015-2016 Stefan Tauner
+ * Copyright (C) 2015 Urja Rannikko <urjaman at 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <string.h>
+#include <libusb.h>
+#include "flash.h"
+#include "programmer.h"
+
+/* LIBUSB_CALL ensures the right calling conventions on libusb callbacks.
+ * However, the macro is not defined everywhere. m(
+ */
+#ifndef LIBUSB_CALL
+#define LIBUSB_CALL
+#endif
+
+#define	 USB_TIMEOUT		1000	/* 1000 ms is plenty and we have no backup strategy anyway. */
+#define	 WRITE_EP		0x02
+#define	 READ_EP		0x82
+
+#define	 CH341_PACKET_LENGTH		0x20
+#define	 CH341_MAX_PACKETS		256
+#define	 CH341_MAX_PACKET_LEN		(CH341_PACKET_LENGTH * CH341_MAX_PACKETS)
+
+#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_START	0x74
+#define	 CH341A_CMD_I2C_STM_STOP	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 // bit 2: SPI with two data pairs D5,D4=out, D7,D6=in
+#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
+
+#define	 CH341A_STM_I2C_20K		0x00
+#define	 CH341A_STM_I2C_100K		0x01
+#define	 CH341A_STM_I2C_400K		0x02
+#define	 CH341A_STM_I2C_750K		0x03
+#define	 CH341A_STM_SPI_DBL		0x04
+
+
+/* Number of parallel IN transfers. 32 seems to produce the most stable throughput on Windows. */
+#define USB_IN_TRANSFERS 32
+
+/* We need to use many queued IN transfers for any resemblance of performance (especially on Windows)
+ * because USB spec says that transfers end on non-full packets and the device sends the 31 reply
+ * data bytes to each 32-byte packet with command + 31 bytes of data... */
+static struct libusb_transfer *transfer_out = NULL;
+static struct libusb_transfer *transfer_ins[USB_IN_TRANSFERS] = {0};
+
+/* Accumulate delays to be plucked between CS deassertion and CS assertions. */
+static unsigned int stored_delay_us = 0;
+
+static struct libusb_device_handle *handle = NULL;
+
+const struct dev_entry devs_ch341a_spi[] = {
+	{0x1A86, 0x5512, OK, "Winchiphead (WCH)", "CH341A"},
+
+	{0},
+};
+
+enum trans_state {TRANS_ACTIVE = -2, TRANS_ERR = -1, TRANS_IDLE = 0};
+
+static void print_hex(const void *buf, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++) {
+		msg_pspew(" %02x", ((uint8_t *)buf)[i]);
+		if (i % CH341_PACKET_LENGTH == CH341_PACKET_LENGTH - 1)
+			msg_pspew("\n");
+	}
+}
+
+static void cb_common(const char *func, struct libusb_transfer *transfer)
+{
+	int *transfer_cnt = (int*)transfer->user_data;
+
+	if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+		/* Silently ACK and exit. */
+		*transfer_cnt = TRANS_IDLE;
+		return;
+	}
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		msg_perr("\n%s: error: %s\n", func, libusb_error_name(transfer->status));
+		*transfer_cnt = TRANS_ERR;
+	} else {
+		*transfer_cnt = transfer->actual_length;
+	}
+}
+
+/* callback for bulk out async transfer */
+static void LIBUSB_CALL cb_out(struct libusb_transfer *transfer)
+{
+	cb_common(__func__, transfer);
+}
+
+/* callback for bulk in async transfer */
+static void LIBUSB_CALL cb_in(struct libusb_transfer *transfer)
+{
+	cb_common(__func__, transfer);
+}
+
+static int32_t usb_transfer(const char *func, unsigned int writecnt, unsigned int readcnt, const uint8_t *writearr, uint8_t *readarr)
+{
+	if (handle == NULL)
+		return -1;
+
+	int state_out = TRANS_IDLE;
+	transfer_out->buffer = (uint8_t*)writearr;
+	transfer_out->length = writecnt;
+	transfer_out->user_data = &state_out;
+
+	/* Schedule write first */
+	if (writecnt > 0) {
+		state_out = TRANS_ACTIVE;
+		int ret = libusb_submit_transfer(transfer_out);
+		if (ret) {
+			msg_perr("%s: failed to submit OUT transfer: %s\n", func, libusb_error_name(ret));
+			state_out = TRANS_ERR;
+			goto err;
+		}
+	}
+
+	/* Handle all asynchronous packets as long as we have stuff to write or read. The write(s) simply need
+	 * to complete but we need to scheduling reads as long as we are not done. */
+	unsigned int free_idx = 0; /* The IN transfer we expect to be free next. */
+	unsigned int in_idx = 0; /* The IN transfer we expect to be completed next. */
+	unsigned int in_done = 0;
+	unsigned int in_active = 0;
+	unsigned int out_done = 0;
+	uint8_t *in_buf = readarr;
+	int state_in[USB_IN_TRANSFERS] = {0};
+	do {
+		/* Schedule new reads as long as there are free transfers and unscheduled bytes to read. */
+		while ((in_done + in_active) < readcnt && state_in[free_idx] == TRANS_IDLE) {
+			unsigned int cur_todo = min(CH341_PACKET_LENGTH - 1, readcnt - in_done - in_active);
+			transfer_ins[free_idx]->length = cur_todo;
+			transfer_ins[free_idx]->buffer = in_buf;
+			transfer_ins[free_idx]->user_data = &state_in[free_idx];
+			int ret = libusb_submit_transfer(transfer_ins[free_idx]);
+			if (ret) {
+				state_in[free_idx] = TRANS_ERR;
+				msg_perr("%s: failed to submit IN transfer: %s\n",
+					 func, libusb_error_name(ret));
+				goto err;
+			}
+			in_buf += cur_todo;
+			in_active += cur_todo;
+			state_in[free_idx] = TRANS_ACTIVE;
+			free_idx = (free_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */
+		}
+
+		/* Actually get some work done. */
+		libusb_handle_events_timeout(NULL, &(struct timeval){1, 0});
+
+		/* Check for the write */
+		if (out_done < writecnt) {
+			if (state_out == TRANS_ERR) {
+				goto err;
+			} else if (state_out > 0) {
+				out_done += state_out;
+				state_out = TRANS_IDLE;
+			}
+		}
+		/* Check for completed transfers. */
+		while (state_in[in_idx] != TRANS_IDLE && state_in[in_idx] != TRANS_ACTIVE) {
+			if (state_in[in_idx] == TRANS_ERR) {
+				goto err;
+			}
+			/* If a transfer is done, record the number of bytes read and reuse it later. */
+			in_done += state_in[in_idx];
+			in_active -= state_in[in_idx];
+			state_in[in_idx] = TRANS_IDLE;
+			in_idx = (in_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */
+		}
+	} while ((out_done < writecnt) || (in_done < readcnt));
+
+	if (out_done > 0) {
+		msg_pspew("Wrote %d bytes:\n", out_done);
+		print_hex(writearr, out_done);
+		msg_pspew("\n\n");
+	}
+	if (in_done > 0) {
+		msg_pspew("Read %d bytes:\n", in_done);
+		print_hex(readarr, in_done);
+		msg_pspew("\n\n");
+	}
+	return 0;
+err:
+	/* Clean up on errors. */
+	msg_perr("%s: Failed to %s %d bytes\n", func, (state_out == TRANS_ERR) ? "write" : "read",
+		 (state_out == TRANS_ERR) ? writecnt : readcnt);
+	/* First, we must cancel any ongoing requests and wait for them to be canceled. */
+	if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) {
+		if (libusb_cancel_transfer(transfer_out) != 0)
+			state_out = TRANS_ERR;
+	}
+	if (readcnt > 0) {
+		unsigned int i;
+		for (i = 0; i < USB_IN_TRANSFERS; i++) {
+			if (state_in[i] == TRANS_ACTIVE)
+				if (libusb_cancel_transfer(transfer_ins[i]) != 0)
+					state_in[i] = TRANS_ERR;
+		}
+	}
+
+	/* Wait for cancellations to complete. */
+	while (1) {
+		bool finished = true;
+		if ((writecnt > 0) && (state_out == TRANS_ACTIVE))
+			finished = false;
+		if (readcnt > 0) {
+			unsigned int i;
+			for (i = 0; i < USB_IN_TRANSFERS; i++) {
+				if (state_in[i] == TRANS_ACTIVE)
+					finished = false;
+			}
+		}
+		if (finished)
+			break;
+		libusb_handle_events_timeout(NULL, &(struct timeval){1, 0});
+	}
+	return -1;
+}
+
+/*   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).  */
+static int32_t config_stream(uint32_t speed)
+{
+	if (handle == NULL)
+		return -1;
+
+	uint8_t buf[] = {
+		CH341A_CMD_I2C_STREAM,
+		CH341A_CMD_I2C_STM_SET | (speed & 0x7),
+		CH341A_CMD_I2C_STM_END
+	};
+
+	int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL);
+	if (ret < 0) {
+		msg_perr("Could not configure stream interface.\n");
+	}
+	return ret;
+}
+
+/* ch341 requires LSB first, swap the bit order before send and after receive */
+static uint8_t swap_byte(uint8_t x)
+{
+	x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa);
+	x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc);
+	x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0);
+	return x;
+}
+
+/* The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip:
+ * UIO	CH341A	SPI	CH341A SPI name
+ * 0	D0/15	CS/1 	(CS0)
+ * 1	D1/16	unused	(CS1)
+ * 2	D2/17	unused	(CS2)
+ * 3	D3/18	SCK/6	(DCK)
+ * 4	D4/19	unused	(DOUT2)
+ * 5	D5/20	SI/5	(DOUT)
+ * - The UIO stream commands seem to only have 6 bits of output, and D6/D7 are the SPI inputs,
+ *  mapped as follows:
+ *	D6/21	unused	(DIN2)
+ *	D7/22	SO/2	(DIN)
+ */
+static int32_t enable_pins(bool enable)
+{
+	uint8_t buf[] = {
+		CH341A_CMD_UIO_STREAM,
+		CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1
+		CH341A_CMD_UIO_STM_DIR | (enable ? 0x3F : 0x00), // Interface output enable / disable
+		CH341A_CMD_UIO_STM_END,
+	};
+
+	int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL);
+	if (ret < 0) {
+		msg_perr("Could not %sable output pins.\n", enable ? "en" : "dis");
+	}
+	return ret;
+}
+
+/* De-assert and assert CS in one operation. */
+static void pluck_cs(uint8_t *ptr)
+{
+	/* This was measured to give a minumum deassertion time of 2.25 us,
+	 * >20x more than needed for most SPI chips (100ns). */
+	int delay_cnt = 2;
+	if (stored_delay_us) {
+		delay_cnt = (stored_delay_us * 4) / 3;
+		stored_delay_us = 0;
+	}
+	*ptr++ = CH341A_CMD_UIO_STREAM;
+	*ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* deasserted */
+	int i;
+	for (i = 0; i < delay_cnt; i++)
+		*ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* "delay" */
+	*ptr++ = CH341A_CMD_UIO_STM_OUT | 0x36; /* asserted */
+	*ptr++ = CH341A_CMD_UIO_STM_END;
+}
+
+void ch341a_spi_delay(unsigned int usecs)
+{
+	/* There is space for 28 bytes instructions of 750 ns each in the CS packet (32 - 4 for the actual CS
+	 * instructions), thus max 21 us, but we avoid getting too near to this boundary and use
+	 * internal_delay() for durations over 20 us. */
+	if ((usecs + stored_delay_us) > 20) {
+		unsigned int inc = 20 - stored_delay_us;
+		internal_delay(usecs - inc);
+		usecs = inc;
+	}
+	stored_delay_us += usecs;
+}
+
+static int ch341a_spi_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr)
+{
+	if (handle == NULL)
+		return -1;
+
+	/* How many packets ... */
+	const size_t packets = (writecnt + readcnt + CH341_PACKET_LENGTH - 2) / (CH341_PACKET_LENGTH - 1);
+
+	/* We pluck CS/timeout handling into the first packet thus we need to allocate one extra package. */
+	uint8_t wbuf[packets+1][CH341_PACKET_LENGTH];
+	uint8_t rbuf[writecnt + readcnt];
+	/* Initialize the write buffer to zero to prevent writing random stack contents to device. */
+	memset(wbuf[0], 0, CH341_PACKET_LENGTH);
+
+	uint8_t *ptr = wbuf[0];
+	/* CS usage is optimized by doing both transitions in one packet.
+	 * Final transition to deselected state is in the pin disable. */
+	pluck_cs(ptr);
+	unsigned int write_left = writecnt;
+	unsigned int read_left = readcnt;
+	unsigned int p;
+	for (p = 0; p < packets; p++) {
+		unsigned int write_now = min(CH341_PACKET_LENGTH - 1, write_left);
+		unsigned int read_now = min ((CH341_PACKET_LENGTH - 1) - write_now, read_left);
+		ptr = wbuf[p+1];
+		*ptr++ = CH341A_CMD_SPI_STREAM;
+		unsigned int i;
+		for (i = 0; i < write_now; ++i)
+			*ptr++ = swap_byte(*writearr++);
+		if (read_now) {
+			memset(ptr, 0xFF, read_now);
+			read_left -= read_now;
+		}
+		write_left -= write_now;
+	}
+
+	int32_t ret = usb_transfer(__func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt,
+				    writecnt + readcnt, wbuf[0], rbuf);
+	if (ret < 0)
+		return -1;
+
+	unsigned int i;
+	for (i = 0; i < readcnt; i++) {
+		*readarr++ = swap_byte(rbuf[writecnt + i]);
+	}
+
+	return 0;
+}
+
+static const struct spi_master spi_master_ch341a_spi = {
+	.type		= SPI_CONTROLLER_CH341A_SPI,
+	/* flashrom's current maximum is 256 B. CH341A was tested on Linux and Windows to accept atleast
+	 * 128 kB. Basically there should be no hard limit because transfers are broken up into USB packets
+	 * sent to the device and most of their payload streamed via SPI. */
+	.max_data_read	= 4 * 1024,
+	.max_data_write	= 4 * 1024,
+	.command	= ch341a_spi_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
+	.write_aai	= default_spi_write_aai,
+};
+
+static int ch341a_spi_shutdown(void *data)
+{
+	if (handle == NULL)
+		return -1;
+
+	enable_pins(false);
+	libusb_free_transfer(transfer_out);
+	transfer_out = NULL;
+	int i;
+	for (i = 0; i < USB_IN_TRANSFERS; i++) {
+		libusb_free_transfer(transfer_ins[i]);
+		transfer_ins[i] = NULL;
+	}
+	libusb_release_interface(handle, 0);
+	libusb_close(handle);
+	libusb_exit(NULL);
+	handle = NULL;
+	return 0;
+}
+
+int ch341a_spi_init(void)
+{
+	if (handle != NULL) {
+		msg_cerr("%s: handle already set! Please report a bug at flashrom at flashrom.org\n", __func__);
+		return -1;
+	}
+
+	int32_t ret = libusb_init(NULL);
+	if (ret < 0) {
+		msg_perr("Couldnt initialize libusb!\n");
+		return -1;
+	}
+
+	libusb_set_debug(NULL, 3); // Enable information, warning and error messages (only).
+
+	uint16_t vid = devs_ch341a_spi[0].vendor_id;
+	uint16_t pid = devs_ch341a_spi[0].device_id;
+	handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
+	if (handle == NULL) {
+		msg_perr("Couldn't open device %04x:%04x.\n", vid, pid);
+		return -1;
+	}
+
+/* libusb_detach_kernel_driver() and friends basically only work on Linux. We simply try to detach on Linux
+ * without a lot of passion here. If that works fine else we will fail on claiming the interface anyway. */
+#if IS_LINUX
+	ret = libusb_detach_kernel_driver(handle, 0);
+	if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
+		msg_pwarn("Detaching kernel drivers is not supported. Further accesses may fail.\n");
+	} else if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) {
+		msg_pwarn("Failed to detach kernel driver: '%s'. Further accesses will probably fail.\n",
+			  libusb_error_name(ret));
+	}
+#endif
+
+	ret = libusb_claim_interface(handle, 0);
+	if (ret != 0) {
+		msg_perr("Failed to claim interface 0: '%s'\n", libusb_error_name(ret));
+		goto close_handle;
+	}
+
+	struct libusb_device *dev;
+	if (!(dev = libusb_get_device(handle))) {
+		msg_perr("Failed to get device from device handle.\n");
+		goto close_handle;
+	}
+
+	struct libusb_device_descriptor desc;
+	ret = libusb_get_device_descriptor(dev, &desc);
+	if (ret < 0) {
+		msg_perr("Failed to get device descriptor: '%s'\n", libusb_error_name(ret));
+		goto release_interface;
+	}
+
+	msg_pdbg("Device revision is %d.%01d.%01d\n",
+		(desc.bcdDevice >> 8) & 0x00FF,
+		(desc.bcdDevice >> 4) & 0x000F,
+		(desc.bcdDevice >> 0) & 0x000F);
+
+	/* Allocate and pre-fill transfer structures. */
+	transfer_out = libusb_alloc_transfer(0);
+	if (!transfer_out) {
+		msg_perr("Failed to alloc libusb OUT transfer\n");
+		goto release_interface;
+	}
+	int i;
+	for (i = 0; i < USB_IN_TRANSFERS; i++) {
+		transfer_ins[i] = libusb_alloc_transfer(0);
+		if (transfer_ins[i] == NULL) {
+			msg_perr("Failed to alloc libusb IN transfer %d\n", i);
+			goto dealloc_transfers;
+		}
+	}
+	/* We use these helpers but dont fill the actual buffer yet. */
+	libusb_fill_bulk_transfer(transfer_out, handle, WRITE_EP, NULL, 0, cb_out, NULL, USB_TIMEOUT);
+	for (i = 0; i < USB_IN_TRANSFERS; i++)
+		libusb_fill_bulk_transfer(transfer_ins[i], handle, READ_EP, NULL, 0, cb_in, NULL, USB_TIMEOUT);
+
+	if ((config_stream(CH341A_STM_I2C_100K) < 0) || (enable_pins(true) < 0))
+		goto dealloc_transfers;
+
+	register_shutdown(ch341a_spi_shutdown, NULL);
+	register_spi_master(&spi_master_ch341a_spi);
+
+	return 0;
+
+dealloc_transfers:
+	for (i = 0; i < USB_IN_TRANSFERS; i++) {
+		if (transfer_ins[i] == NULL)
+			break;
+		libusb_free_transfer(transfer_ins[i]);
+		transfer_ins[i] = NULL;
+	}
+	libusb_free_transfer(transfer_out);
+	transfer_out = NULL;
+release_interface:
+	libusb_release_interface(handle, 0);
+close_handle:
+	libusb_close(handle);
+	handle = NULL;
+	return -1;
+}

Modified: trunk/flashrom.8.tmpl
==============================================================================
--- trunk/flashrom.8.tmpl	Sun Jan 31 23:10:14 2016	(r1920)
+++ trunk/flashrom.8.tmpl	Sun Jan 31 23:10:29 2016	(r1921)
@@ -271,6 +271,8 @@
 .sp
 .BR "* pickit2_spi" " (for SPI flash ROMs accessible via Microchip PICkit2)"
 .sp
+.BR "* ch341a_spi" " (for SPI flash ROMs attached to WCH CH341A)"
+.sp
 Some programmers have optional or mandatory parameters which are described
 in detail in the
 .B PROGRAMMER-SPECIFIC INFORMATION
@@ -1019,6 +1021,10 @@
 parameter, once the flash read/write operation you intended to perform has completed successfully.
 .sp
 Please also note that the mstarddc_spi driver only works on Linux.
+.SS
+.BR "ch341a_spi " programmer
+The WCH CH341A programmer does not support any parameters currently. SPI frequency is fixed at 2 MHz, and CS0 is
+used as per the device.
 .SH EXAMPLES
 To back up and update your BIOS, run
 .sp
@@ -1078,6 +1084,9 @@
 .BR dediprog ", " ft2232_spi ", " usbblaster_spi " and " pickit2_spi
 need access to the respective USB device via libusb API version 0.1.
 .sp
+.BR ch341a_spi
+needs access to the respective USB device via libusb API version 1.0.
+.sp
 .B dummy
 needs no access permissions at all.
 .sp
@@ -1085,7 +1094,8 @@
 .BR gfxnvidia ", " drkaiser ", " satasii ", " satamv ", " atahpt ", " atavia " and " atapromise
 have to be run as superuser/root, and need additional raw access permission.
 .sp
-.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi ", " ft2232_spi " and " pickit2_spi
+.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi ", " ft2232_spi ", " pickit2_spi " and " \
+ch341a_spi
 can be run as normal user on most operating systems if appropriate device
 permissions are set.
 .sp

Modified: trunk/flashrom.c
==============================================================================
--- trunk/flashrom.c	Sun Jan 31 23:10:14 2016	(r1920)
+++ trunk/flashrom.c	Sun Jan 31 23:10:29 2016	(r1921)
@@ -392,6 +392,18 @@
 	},
 #endif
 
+#if CONFIG_CH341A_SPI == 1
+	{
+		.name			= "ch341a_spi",
+		.type			= USB,
+		.devs.dev		= devs_ch341a_spi,
+		.init			= ch341a_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= ch341a_spi_delay,
+	},
+#endif
+
 	{0}, /* This entry corresponds to PROGRAMMER_INVALID. */
 };
 

Modified: trunk/programmer.h
==============================================================================
--- trunk/programmer.h	Sun Jan 31 23:10:14 2016	(r1920)
+++ trunk/programmer.h	Sun Jan 31 23:10:29 2016	(r1921)
@@ -108,6 +108,9 @@
 #if CONFIG_PICKIT2_SPI == 1
 	PROGRAMMER_PICKIT2_SPI,
 #endif
+#if CONFIG_CH341A_SPI == 1
+	PROGRAMMER_CH341A_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
@@ -526,6 +529,13 @@
 int dediprog_init(void);
 #endif
 
+/* ch341a_spi.c */
+#if CONFIG_CH341A_SPI == 1
+int ch341a_spi_init(void);
+void ch341a_spi_delay(unsigned int usecs);
+extern const struct dev_entry devs_ch341a_spi[];
+#endif
+
 /* flashrom.c */
 struct decode_sizes {
 	uint32_t parallel;
@@ -585,6 +595,9 @@
 #if CONFIG_PICKIT2_SPI == 1
 	SPI_CONTROLLER_PICKIT2,
 #endif
+#if CONFIG_CH341A_SPI == 1
+	SPI_CONTROLLER_CH341A_SPI,
+#endif
 };
 
 #define MAX_DATA_UNSPECIFIED 0




More information about the flashrom mailing list