diff options
Diffstat (limited to 'ch341a_spi.c')
-rw-r--r-- | ch341a_spi.c | 214 |
1 files changed, 104 insertions, 110 deletions
diff --git a/ch341a_spi.c b/ch341a_spi.c index 84cc3fe7b..8f42f09c7 100644 --- a/ch341a_spi.c +++ b/ch341a_spi.c @@ -17,10 +17,10 @@ * GNU General Public License for more details. */ +#include <stdlib.h> #include <string.h> #include <libusb.h> #include "flash.h" -#include "platform.h" #include "programmer.h" /* LIBUSB_CALL ensures the right calling conventions on libusb callbacks. @@ -73,18 +73,20 @@ /* 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}; +struct ch341a_spi_data { + struct libusb_device_handle *handle; -/* Accumulate delays to be plucked between CS deassertion and CS assertions. */ -static unsigned int stored_delay_us = 0; + /* 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... */ + struct libusb_transfer *transfer_out; + struct libusb_transfer *transfer_ins[USB_IN_TRANSFERS]; -static struct libusb_device_handle *handle = NULL; + /* Accumulate delays to be plucked between CS deassertion and CS assertions. */ + unsigned int stored_delay_us; +}; -const struct dev_entry devs_ch341a_spi[] = { +static const struct dev_entry devs_ch341a_spi[] = { {0x1A86, 0x5512, OK, "Winchiphead (WCH)", "CH341A"}, {0}, @@ -132,20 +134,18 @@ 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) +static int32_t usb_transfer(const struct ch341a_spi_data *data, 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; + data->transfer_out->buffer = (uint8_t*)writearr; + data->transfer_out->length = writecnt; + data->transfer_out->user_data = &state_out; /* Schedule write first */ if (writecnt > 0) { state_out = TRANS_ACTIVE; - int ret = libusb_submit_transfer(transfer_out); + int ret = libusb_submit_transfer(data->transfer_out); if (ret) { msg_perr("%s: failed to submit OUT transfer: %s\n", func, libusb_error_name(ret)); state_out = TRANS_ERR; @@ -166,10 +166,10 @@ static int32_t usb_transfer(const char *func, unsigned int writecnt, unsigned in /* 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]); + data->transfer_ins[free_idx]->length = cur_todo; + data->transfer_ins[free_idx]->buffer = in_buf; + data->transfer_ins[free_idx]->user_data = &state_in[free_idx]; + int ret = libusb_submit_transfer(data->transfer_ins[free_idx]); if (ret) { state_in[free_idx] = TRANS_ERR; msg_perr("%s: failed to submit IN transfer: %s\n", @@ -224,14 +224,14 @@ err: (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) + if (libusb_cancel_transfer(data->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) + if (libusb_cancel_transfer(data->transfer_ins[i]) != 0) state_in[i] = TRANS_ERR; } } @@ -257,18 +257,15 @@ err: /* 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) +static int32_t config_stream(const struct ch341a_spi_data *data, 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); + int32_t ret = usb_transfer(data, __func__, sizeof(buf), 0, buf, NULL); if (ret < 0) { msg_perr("Could not configure stream interface.\n"); } @@ -288,7 +285,7 @@ static int32_t config_stream(uint32_t speed) * D6/21 unused (DIN2) * D7/22 SO/2 (DIN) */ -static int32_t enable_pins(bool enable) +static int32_t enable_pins(const struct ch341a_spi_data *data, bool enable) { uint8_t buf[] = { CH341A_CMD_UIO_STREAM, @@ -297,7 +294,7 @@ static int32_t enable_pins(bool enable) CH341A_CMD_UIO_STM_END, }; - int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL); + int32_t ret = usb_transfer(data, __func__, sizeof(buf), 0, buf, NULL); if (ret < 0) { msg_perr("Could not %sable output pins.\n", enable ? "en" : "dis"); } @@ -305,14 +302,14 @@ static int32_t enable_pins(bool enable) } /* De-assert and assert CS in one operation. */ -static void pluck_cs(uint8_t *ptr) +static void pluck_cs(uint8_t *ptr, unsigned int *stored_delay_us) { /* This was measured to give a minimum 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; + 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 */ @@ -323,23 +320,24 @@ static void pluck_cs(uint8_t *ptr) *ptr++ = CH341A_CMD_UIO_STM_END; } -void ch341a_spi_delay(unsigned int usecs) +static void ch341a_spi_delay(const struct flashctx *flash, unsigned int usecs) { + struct ch341a_spi_data *data = flash->mst->spi.data; + /* 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); + * default_delay() for durations over 20 us. */ + if ((usecs + data->stored_delay_us) > 20) { + unsigned int inc = 20 - data->stored_delay_us; + default_delay(usecs - inc); usecs = inc; } - stored_delay_us += usecs; + data->stored_delay_us += usecs; } static int ch341a_spi_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { - if (handle == NULL) - return -1; + struct ch341a_spi_data *data = flash->mst->spi.data; /* How many packets ... */ const size_t packets = (writecnt + readcnt + CH341_PACKET_LENGTH - 2) / (CH341_PACKET_LENGTH - 1); @@ -353,7 +351,7 @@ static int ch341a_spi_spi_send_command(const struct flashctx *flash, unsigned in 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); + pluck_cs(ptr, &data->stored_delay_us); unsigned int write_left = writecnt; unsigned int read_left = readcnt; unsigned int p; @@ -372,7 +370,7 @@ static int ch341a_spi_spi_send_command(const struct flashctx *flash, unsigned in write_left -= write_now; } - int32_t ret = usb_transfer(__func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt, + int32_t ret = usb_transfer(data, __func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt, writecnt + readcnt, wbuf[0], rbuf); if (ret < 0) return -1; @@ -385,50 +383,43 @@ static int ch341a_spi_spi_send_command(const struct flashctx *flash, unsigned in return 0; } +static int ch341a_spi_shutdown(void *data) +{ + struct ch341a_spi_data *ch341a_data = data; + + enable_pins(ch341a_data, false); + libusb_free_transfer(ch341a_data->transfer_out); + int i; + for (i = 0; i < USB_IN_TRANSFERS; i++) + libusb_free_transfer(ch341a_data->transfer_ins[i]); + libusb_release_interface(ch341a_data->handle, 0); + libusb_attach_kernel_driver(ch341a_data->handle, 0); + libusb_close(ch341a_data->handle); + libusb_exit(NULL); + + free(data); + return 0; +} + static const struct spi_master spi_master_ch341a_spi = { .features = SPI_MASTER_4BA, - /* flashrom's current maximum is 256 B. CH341A was tested on Linux and Windows to accept atleast + /* flashrom's current maximum is 256 B. CH341A was tested on Linux and Windows to accept at least * 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, + .shutdown = ch341a_spi_shutdown, + .delay = ch341a_spi_delay, }; -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) +static int ch341a_spi_init(const struct programmer_cfg *cfg) { - if (handle != NULL) { - msg_cerr("%s: handle already set! Please report a bug at flashrom@flashrom.org\n", __func__); - return -1; - } - int32_t ret = libusb_init(NULL); if (ret < 0) { - msg_perr("Couldnt initialize libusb!\n"); + msg_perr("Couldn't initialize libusb!\n"); return -1; } @@ -439,34 +430,33 @@ int ch341a_spi_init(void) libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); #endif + struct ch341a_spi_data *data = calloc(1, sizeof(*data)); + if (!data) { + msg_perr("Out of memory!\n"); + return 1; + } + 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) { + data->handle = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (data->handle == NULL) { msg_perr("Couldn't open device %04x:%04x.\n", vid, pid); - return -1; + goto free_data; } -/* 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_detach_kernel_driver(data->handle, 0); + if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) + msg_pwarn("Cannot detach the existing USB driver. Claiming the interface may fail. %s\n", + libusb_error_name(ret)); - ret = libusb_claim_interface(handle, 0); + ret = libusb_claim_interface(data->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))) { + if (!(dev = libusb_get_device(data->handle))) { msg_perr("Failed to get device from device handle.\n"); goto close_handle; } @@ -484,45 +474,49 @@ int ch341a_spi_init(void) (desc.bcdDevice >> 0) & 0x000F); /* Allocate and pre-fill transfer structures. */ - transfer_out = libusb_alloc_transfer(0); - if (!transfer_out) { + data->transfer_out = libusb_alloc_transfer(0); + if (!data->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) { + data->transfer_ins[i] = libusb_alloc_transfer(0); + if (data->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); + libusb_fill_bulk_transfer(data->transfer_out, data->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); + libusb_fill_bulk_transfer(data->transfer_ins[i], data->handle, READ_EP, NULL, 0, cb_in, NULL, USB_TIMEOUT); - if ((config_stream(CH341A_STM_I2C_100K) < 0) || (enable_pins(true) < 0)) + if ((config_stream(data, CH341A_STM_I2C_100K) < 0) || (enable_pins(data, true) < 0)) goto dealloc_transfers; - register_shutdown(ch341a_spi_shutdown, NULL); - register_spi_master(&spi_master_ch341a_spi); - - return 0; + return register_spi_master(&spi_master_ch341a_spi, data); dealloc_transfers: for (i = 0; i < USB_IN_TRANSFERS; i++) { - if (transfer_ins[i] == NULL) + if (data->transfer_ins[i] == NULL) break; - libusb_free_transfer(transfer_ins[i]); - transfer_ins[i] = NULL; + libusb_free_transfer(data->transfer_ins[i]); } - libusb_free_transfer(transfer_out); - transfer_out = NULL; + libusb_free_transfer(data->transfer_out); release_interface: - libusb_release_interface(handle, 0); + libusb_release_interface(data->handle, 0); close_handle: - libusb_close(handle); - handle = NULL; + libusb_attach_kernel_driver(data->handle, 0); + libusb_close(data->handle); +free_data: + free(data); return -1; } + +const struct programmer_entry programmer_ch341a_spi = { + .name = "ch341a_spi", + .type = USB, + .devs.dev = devs_ch341a_spi, + .init = ch341a_spi_init, +}; |