Hi, Review-ish... with some only-commentary included too.
On Mon, Jan 18, 2016 at 1:28 AM, Stefan Tauner <[email protected]> wrote: > Signed-off-by: Urja Rannikko <[email protected]> > Signed-off-by: Stefan Tauner <[email protected]> > --- > Makefile | 23 ++- > ch341a_spi.c | 531 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > flashrom.8.tmpl | 12 +- > flashrom.c | 12 ++ > programmer.h | 13 ++ > 5 files changed, 588 insertions(+), 3 deletions(-) > create mode 100644 ch341a_spi.c > > diff --git a/Makefile b/Makefile > index 6862200..676bb66 100644 > --- a/Makefile > +++ b/Makefile > @@ -157,7 +157,7 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes > 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 @@ UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes > 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? > @@ -301,7 +306,7 @@ UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes > 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 > @@ -322,6 +327,11 @@ UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes > 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) > @@ -500,6 +510,9 @@ CONFIG_LINUX_SPI ?= yes > # 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 > > @@ -740,6 +753,12 @@ NEED_LINUX_I2C := yes > 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 > diff --git a/ch341a_spi.c b/ch341a_spi.c > new file mode 100644 > index 0000000..36282ed > --- /dev/null > +++ b/ch341a_spi.c > @@ -0,0 +1,531 @@ > +/* > + * This file is part of the flashrom project. > + * > + * Copyright (C) 2011 asbokid <[email protected]> > + * Copyright (C) 2014 Pluto Yang <[email protected]> > + * Copyright (C) 2015-2016 Stefan Tauner > + * Copyright (C) 2015 Urja Rannikko <[email protected]> > + * > + * 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 unsigned char *writearr, unsigned char *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; > + 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 = readarr; > + 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; > + } > + readarr += cur_todo; > + in_active += cur_todo; > + state_in[free_idx] = TRANS_ACTIVE; > + free_idx = (free_idx + 1) % USB_IN_TRANSFERS; /* > Increment (and wrap around). */ Yeah this obvious construct is something I didnt use because I'm such an embedded guy that I'd never ever use % in performance related code... (i think i had some if idx>=USB_IN_TRANSFERS) idx = 0 thing)... thanks for making it saner. > + } > + > + /* 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; > +} > + I had a fancier delay implementation done (all the way upto ms scale using the I2C commands), but since i didnt get that fully tested, this one will do, i'll post it as an improvement later. > +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); > + > + uint8_t wbuf[packets+1][CH341_PACKET_LENGTH]; > + uint8_t rbuf[writecnt + readcnt]; > + memset(wbuf[0], 0, CH341_PACKET_LENGTH); // dont want to write stack > random to device... > + > + 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++) { > + /* FIXME: does working on words instead of bytes improve > speed? */ ... drop the comment? i dont really care, but i think our consensus on IRC was that a words-wide implementation would have more issues (alignment and such) than performance. Or make a comment with that info. > + *readarr++ = swap_byte(rbuf[writecnt + i]); > + } > + > + return 0; > +} > + > +static const struct spi_master spi_master_ch341a_spi = { > + .type = SPI_CONTROLLER_CH341A_SPI, > + /* TODO: flashrom's current maximum is 256 B. Device was tested on > Linux to accept atleast 16 kB. */ > + .max_data_read = 16 * 1024, > + .max_data_write = 16 * 1024, On linux. Not on windows usb "stack" i think... set these back to 1k? Or test for the limit (maybe something like 4k?) and change the 1000ms timeout, since i think the big packet would timeout on windows if nothing else failed... > + .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 > [email protected]\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; > +} > diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl > index 7c8c70c..0dd8322 100644 > --- a/flashrom.8.tmpl > +++ b/flashrom.8.tmpl > @@ -269,6 +269,8 @@ bitbanging adapter) > .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 > @@ -1013,6 +1015,10 @@ an operation), without the > 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 > @@ -1072,6 +1078,9 @@ needs userspace access to a serial port. > .BR dediprog ", " ft2232_spi ", " usbblaster_spi " and " pickit2_spi > need access to the respective USB device via libusb-0.1. > .sp > +.BR ch341a_spi > +needs access to the respective USB device via libusb-1.0. > +.sp > .B dummy > needs no access permissions at all. > .sp > @@ -1079,7 +1088,8 @@ needs no access permissions at all. > .BR gfxnvidia ", " drkaiser ", " satasii ", " satamv ", " atahpt" and " > atavia > 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 > diff --git a/flashrom.c b/flashrom.c > index 3853e19..0ff088f 100644 > --- a/flashrom.c > +++ b/flashrom.c > @@ -380,6 +380,18 @@ const struct programmer_entry programmer_table[] = { > }, > #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. */ > }; > > diff --git a/programmer.h b/programmer.h > index 97f0ffa..b9f8708 100644 > --- a/programmer.h > +++ b/programmer.h > @@ -105,6 +105,9 @@ enum programmer { > #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. */ > }; > > @@ -516,6 +519,13 @@ int linux_spi_init(void); > 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; > @@ -575,6 +585,9 @@ enum spi_controller { > #if CONFIG_PICKIT2_SPI == 1 > SPI_CONTROLLER_PICKIT2, > #endif > +#if CONFIG_CH341A_SPI == 1 > + SPI_CONTROLLER_CH341A_SPI, > +#endif > }; > > #define MAX_DATA_UNSPECIFIED 0 > -- > Kind regards, Stefan Tauner > With atleast the limits dropped (or tested), this is: Acked-by: Urja Rannikko <[email protected]> -- Urja _______________________________________________ flashrom mailing list [email protected] http://www.flashrom.org/mailman/listinfo/flashrom
