My programmer with this chip has a jumper to select between
USB <-> UART translation and USB <-> SPI translation. Only the latter
is applicable for use as an SPI programmer of course, but it's an
easy and non-obvious mistake to make. The UART mode (and a printer mode
not exposed on my programmer, but available on the chip according
to the datasheet), are indicated through different USB IDs, which we
can easily detect. Do so, and print an informative warning when
such a device is detected.
Signed-off-by: Keno Fischer <keno(a)juliacomputing.com>
---
ch341a_spi.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 5 deletions(-)
diff --git a/ch341a_spi.c b/ch341a_spi.c
index 6eb2804..7eba6b1 100644
--- a/ch341a_spi.c
+++ b/ch341a_spi.c
@@ -93,6 +93,16 @@ const struct dev_entry devs_ch341a_spi[] = {
{0},
};
+/* The CH341A has alternate modes, indicated as different USB device IDs and
+ * generally selectible on programmers via jumpers. Those alternate modes are
+ * unsuitable for use as a programmer, but we want to be able to detect them,
+ * to give a nice error message to the user. */
+const struct dev_entry devs_ch341a_spi_alternate[] = {
+ {0x1A86, 0x5523, OK, "Winchiphead (WCH)", "CH341A Serial"},
+ {0x1A86, 0x5584, OK, "Winchiphead (WCH)", "CH341A Printer"},
+ {0},
+};
+
enum trans_state {TRANS_ACTIVE = -2, TRANS_ERR = -1, TRANS_IDLE = 0};
static void print_hex(const void *buf, size_t len)
@@ -431,6 +441,47 @@ static int ch341a_spi_shutdown(void *data)
return 0;
}
+static bool device_matches(const struct libusb_device_descriptor *desc, const struct dev_entry *entry)
+{
+ return desc->idVendor == entry->vendor_id && desc->idProduct == entry->device_id;
+}
+
+static struct libusb_device_handle *ch341a_find_device()
+{
+ struct libusb_device *dev, **devs;
+ struct libusb_device_handle *dev_handle = NULL;
+ size_t i = 0;
+
+ if (libusb_get_device_list(NULL, &devs) < 0)
+ return dev_handle;
+
+ while ((dev = devs[i++]) != NULL) {
+ struct libusb_device_descriptor desc;
+ const struct dev_entry *entry;
+ if (libusb_get_device_descriptor(dev, &desc) < 0)
+ goto out;
+ for (entry = devs_ch341a_spi_alternate; entry->vendor_id != 0; ++entry) {
+ if (device_matches(&desc, entry)) {
+ msg_pwarn("CH341A device in alternate mode detected.\n"
+ "There may be a jumper on your programmer to switch modes.\n");
+ }
+ }
+ for (entry = devs_ch341a_spi; entry->vendor_id != 0; ++entry) {
+ if (device_matches(&desc, entry)) {
+ if (dev_handle) {
+ msg_pwarn("Multiple CH341A devices detected. Selecting the first.\n");
+ goto out;
+ }
+ libusb_open(dev, &dev_handle);
+ }
+ }
+ }
+
+out:
+ libusb_free_device_list(devs, 1);
+ return dev_handle;
+}
+
int ch341a_spi_init(void)
{
if (handle != NULL) {
@@ -444,13 +495,11 @@ int ch341a_spi_init(void)
return -1;
}
- libusb_set_debug(NULL, 3); // Enable information, warning and error messages (only).
+ libusb_set_debug(NULL, 4); // 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);
+ handle = ch341a_find_device();
if (handle == NULL) {
- msg_perr("Couldn't open device %04x:%04x.\n", vid, pid);
+ msg_perr("No usable CH341A USB device found.\n");
return -1;
}
--
1.7.10.4