@@ -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;
}
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@juliacomputing.com> --- ch341a_spi.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-)