diff --git a/ChangeLog b/ChangeLog index daea7b05..b3b96431 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2014-02-26 Joerg Wunsch + + * jtag3_private.h: Add EDBG/CMSIS-DAP specific constants. + * jtag3.c: Add EDBG/CMSIS-DAP protocol implementation. + * serial.h: (Dito.) + * usbdevs.h: (Dito.) + * usb_libusb.c: (Dito.) + * configure.ac: (Dito.) + * avrdude.conf.in: Add JTAGICE3 and XplainedPro entries using + EDBG. + * configure.ac: Bump version date. + 2014-02-22 Joerg Wunsch * usb_libusb.c (usbdev_recv_frame): Fix a bug where a new recv diff --git a/NEWS b/NEWS index 9a09c3c0..1ac31e71 100644 --- a/NEWS +++ b/NEWS @@ -8,7 +8,7 @@ Approximate change log for AVRDUDE by version. Current: * Major changes compared to the previous version: - - ... + - Atmel EDBG protocol support added (JTAGICE3, XplainedPro) * New devices supported: - ... diff --git a/avrdude.conf.in b/avrdude.conf.in index b247d644..e6a521bd 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -994,6 +994,7 @@ programmer desc = "Atmel AVR JTAGICE3 in JTAG mode"; type = "jtagice3"; connection_type = usb; + usbpid = 0x2110; ; programmer @@ -1001,6 +1002,7 @@ programmer desc = "Atmel AVR JTAGICE3 in PDI mode"; type = "jtagice3_pdi"; connection_type = usb; + usbpid = 0x2110; ; programmer @@ -1008,6 +1010,7 @@ programmer desc = "Atmel AVR JTAGICE3 in debugWIRE mode"; type = "jtagice3_dw"; connection_type = usb; + usbpid = 0x2110; ; programmer @@ -1015,6 +1018,47 @@ programmer desc = "Atmel AVR JTAGICE3 in ISP mode"; type = "jtagice3_isp"; connection_type = usb; + usbpid = 0x2110; +; + +programmer + id = "jtag3edbg"; + desc = "Atmel AVR JTAGICE3 in JTAG mode, EDBG protocol"; + type = "jtagice3"; + connection_type = usb; + usbpid = 0x2140; +; + +programmer + id = "jtag3pdi_edbg"; + desc = "Atmel AVR JTAGICE3 in PDI mode, EDBG protocol"; + type = "jtagice3_pdi"; + connection_type = usb; + usbpid = 0x2140; +; + +programmer + id = "jtag3dw_edbg"; + desc = "Atmel AVR JTAGICE3 in debugWIRE mode, EDBG protocol"; + type = "jtagice3_dw"; + connection_type = usb; + usbpid = 0x2140; +; + +programmer + id = "jtag3isp_edbg"; + desc = "Atmel AVR JTAGICE3 in ISP mode, EDBG protocol"; + type = "jtagice3_isp"; + connection_type = usb; + usbpid = 0x2140; +; + +programmer + id = "xplainedpro"; + desc = "Atmel AVR XplainedPro in JTAG mode"; + type = "jtagice3"; + connection_type = usb; + usbpid = 0x2111; ; diff --git a/configure.ac b/configure.ac index 2dc712c4..bb82f3f5 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.60) -AC_INIT(avrdude, 6.1-svn-20131205, avrdude-dev@nongnu.org) +AC_INIT(avrdude, 6.1-svn-20140226, avrdude-dev@nongnu.org) AC_CANONICAL_BUILD AC_CANONICAL_HOST diff --git a/jtag3.c b/jtag3.c index 0a1ef41a..f34f6e37 100644 --- a/jtag3.c +++ b/jtag3.c @@ -84,8 +84,13 @@ struct pdata #define PGM_FL_IS_DW (0x0001) #define PGM_FL_IS_PDI (0x0002) #define PGM_FL_IS_JTAG (0x0004) +#define PGM_FL_IS_EDBG (0x0008) static int jtag3_open(PROGRAMMER * pgm, char * port); +static int jtag3_edbg_prepare(PROGRAMMER * pgm); +static int jtag3_edbg_signoff(PROGRAMMER * pgm); +static int jtag3_edbg_send(PROGRAMMER * pgm, unsigned char * data, size_t len); +static int jtag3_edbg_recv_frame(PROGRAMMER * pgm, unsigned char **msg); static int jtag3_initialize(PROGRAMMER * pgm, AVRPART * p); static int jtag3_chip_erase(PROGRAMMER * pgm, AVRPART * p); @@ -399,6 +404,9 @@ int jtag3_send(PROGRAMMER * pgm, unsigned char * data, size_t len) { unsigned char *buf; + if (pgm->flag & PGM_FL_IS_EDBG) + return jtag3_edbg_send(pgm, data, len); + if (verbose >= 3) fprintf(stderr, "\n%s: jtag3_send(): sending %lu bytes\n", progname, (unsigned long)len); @@ -419,7 +427,7 @@ int jtag3_send(PROGRAMMER * pgm, unsigned char * data, size_t len) fprintf(stderr, "%s: jtag3_send(): failed to send command to serial port\n", progname); - exit(1); + return -1; } free(buf); @@ -427,6 +435,193 @@ int jtag3_send(PROGRAMMER * pgm, unsigned char * data, size_t len) return 0; } +static int jtag3_edbg_send(PROGRAMMER * pgm, unsigned char * data, size_t len) +{ + unsigned char buf[USBDEV_MAX_XFER_3]; + unsigned char status[USBDEV_MAX_XFER_3]; + int rv; + + if (verbose >= 4) + { + memset(buf, 0, pgm->fd.usb.max_xfer); + memset(status, 0, pgm->fd.usb.max_xfer); + } + + if (verbose >= 3) + fprintf(stderr, "\n%s: jtag3_edbg_send(): sending %lu bytes\n", + progname, (unsigned long)len); + + if (len + 8 > pgm->fd.usb.max_xfer) + { + fprintf(stderr, + "%s: jtag3_edbg_send(): Fragmentation not (yet) implemented!\n", + progname); + return -1; + } + buf[0] = EDBG_VENDOR_AVR_CMD; + buf[1] = (1 << 4) | 1; /* first out of a total of 1 fragments */ + buf[2] = (len + 4) >> 8; + buf[3] = (len + 4) & 0xff; + buf[4] = TOKEN; + buf[5] = 0; /* dummy */ + u16_to_b2(buf + 6, PDATA(pgm)->command_sequence); + memcpy(buf + 8, data, len); + + if (serial_send(&pgm->fd, buf, pgm->fd.usb.max_xfer) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_send(): failed to send command to serial port\n", + progname); + return -1; + } + rv = serial_recv(&pgm->fd, status, pgm->fd.usb.max_xfer); + + if (rv < 0) { + /* timeout in receive */ + if (verbose > 1) + fprintf(stderr, + "%s: jtag3_edbg_send(): Timeout receiving packet\n", + progname); + return -1; + } + if (status[0] != EDBG_VENDOR_AVR_CMD || status[1] != 0x01) + { + /* what to do in this case? */ + fprintf(stderr, + "%s: jtag3_edbg_send(): Unexpected response 0x%02x, 0x%02x\n", + progname, status[0], status[1]); + } + + return 0; +} + +/* + * Send out all the CMSIS-DAP stuff needed to prepare the ICE. + */ +static int jtag3_edbg_prepare(PROGRAMMER * pgm) +{ + unsigned char buf[USBDEV_MAX_XFER_3]; + unsigned char status[USBDEV_MAX_XFER_3]; + int rv; + + if (verbose >= 3) + fprintf(stderr, "\n%s: jtag3_edbg_prepare()\n", + progname); + + if (verbose >= 4) + memset(buf, 0, USBDEV_MAX_XFER_3); + + buf[0] = CMSISDAP_CMD_CONNECT; + buf[1] = CMSISDAP_CONN_SWD; + if (serial_send(&pgm->fd, buf, USBDEV_MAX_XFER_3) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_prepare(): failed to send command to serial port\n", + progname); + return -1; + } + rv = serial_recv(&pgm->fd, status, USBDEV_MAX_XFER_3); + if (rv != USBDEV_MAX_XFER_3) { + fprintf(stderr, + "%s: jtag3_edbg_prepare(): failed to read from serial port (%d)\n", + progname, rv); + return -1; + } + if (status[0] != CMSISDAP_CMD_CONNECT || + status[1] == 0) + fprintf(stderr, + "%s: jtag3_edbg_prepare(): unexpected response 0x%02x, 0x%02x\n", + progname, status[0], status[1]); + if (verbose >= 2) + fprintf(stderr, + "%s: jtag3_edbg_prepare(): connection status 0x%02x\n", + progname, status[1]); + + buf[0] = CMSISDAP_CMD_LED; + buf[1] = CMSISDAP_LED_CONNECT; + buf[2] = 1; + if (serial_send(&pgm->fd, buf, USBDEV_MAX_XFER_3) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_prepare(): failed to send command to serial port\n", + progname); + return -1; + } + rv = serial_recv(&pgm->fd, status, USBDEV_MAX_XFER_3); + if (rv != USBDEV_MAX_XFER_3) { + fprintf(stderr, + "%s: jtag3_edbg_prepare(): failed to read from serial port (%d)\n", + progname, rv); + return -1; + } + if (status[0] != CMSISDAP_CMD_LED || + status[1] != 0) + fprintf(stderr, + "%s: jtag3_edbg_prepare(): unexpected response 0x%02x, 0x%02x\n", + progname, status[0], status[1]); + + return 0; +} + + +/* + * Send out all the CMSIS-DAP stuff when signing off. + */ +static int jtag3_edbg_signoff(PROGRAMMER * pgm) +{ + unsigned char buf[USBDEV_MAX_XFER_3]; + unsigned char status[USBDEV_MAX_XFER_3]; + int rv; + + if (verbose >= 3) + fprintf(stderr, "\n%s: jtag3_edbg_signoff()\n", + progname); + + if (verbose >= 4) + memset(buf, 0, USBDEV_MAX_XFER_3); + + buf[0] = CMSISDAP_CMD_LED; + buf[1] = CMSISDAP_LED_CONNECT; + buf[2] = 0; + if (serial_send(&pgm->fd, buf, USBDEV_MAX_XFER_3) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_signoff(): failed to send command to serial port\n", + progname); + return -1; + } + rv = serial_recv(&pgm->fd, status, USBDEV_MAX_XFER_3); + if (rv != USBDEV_MAX_XFER_3) { + fprintf(stderr, + "%s: jtag3_edbg_signoff(): failed to read from serial port (%d)\n", + progname, rv); + return -1; + } + if (status[0] != CMSISDAP_CMD_LED || + status[1] != 0) + fprintf(stderr, + "%s: jtag3_edbg_signoff(): unexpected response 0x%02x, 0x%02x\n", + progname, status[0], status[1]); + + buf[0] = CMSISDAP_CMD_DISCONNECT; + if (serial_send(&pgm->fd, buf, USBDEV_MAX_XFER_3) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_signoff(): failed to send command to serial port\n", + progname); + return -1; + } + rv = serial_recv(&pgm->fd, status, USBDEV_MAX_XFER_3); + if (rv != USBDEV_MAX_XFER_3) { + fprintf(stderr, + "%s: jtag3_edbg_signoff(): failed to read from serial port (%d)\n", + progname, rv); + return -1; + } + if (status[0] != CMSISDAP_CMD_DISCONNECT || + status[1] != 0) + fprintf(stderr, + "%s: jtag3_edbg_signoff(): unexpected response 0x%02x, 0x%02x\n", + progname, status[0], status[1]); + + return 0; +} + static int jtag3_drain(PROGRAMMER * pgm, int display) { @@ -446,6 +641,9 @@ static int jtag3_recv_frame(PROGRAMMER * pgm, unsigned char **msg) { int rv; unsigned char *buf = NULL; + if (pgm->flag & PGM_FL_IS_EDBG) + return jtag3_edbg_recv_frame(pgm, msg); + if (verbose >= 4) fprintf(stderr, "%s: jtag3_recv():\n", progname); @@ -454,6 +652,8 @@ static int jtag3_recv_frame(PROGRAMMER * pgm, unsigned char **msg) { progname); return -1; } + if (verbose >= 4) + memset(buf, 0, pgm->fd.usb.max_xfer); rv = serial_recv(&pgm->fd, buf, pgm->fd.usb.max_xfer); @@ -472,6 +672,62 @@ static int jtag3_recv_frame(PROGRAMMER * pgm, unsigned char **msg) { return rv; } +static int jtag3_edbg_recv_frame(PROGRAMMER * pgm, unsigned char **msg) { + int rv, len; + unsigned char *buf = NULL; + + if (verbose >= 4) + fprintf(stderr, "%s: jtag3_edbg_recv():\n", progname); + + if ((buf = malloc(pgm->fd.usb.max_xfer)) == NULL) { + fprintf(stderr, "%s: jtag3_edbg_recv(): out of memory\n", + progname); + return -1; + } + + buf[0] = EDBG_VENDOR_AVR_RSP; + + if (serial_send(&pgm->fd, buf, pgm->fd.usb.max_xfer) != 0) { + fprintf(stderr, + "%s: jtag3_edbg_recv(): error sending CMSIS-DAP vendor command\n", + progname); + return -1; + } + + rv = serial_recv(&pgm->fd, buf, pgm->fd.usb.max_xfer); + + if (rv < 0) { + /* timeout in receive */ + if (verbose > 1) + fprintf(stderr, + "%s: jtag3_edbg_recv(): Timeout receiving packet\n", + progname); + free(buf); + return -1; + } + + if (buf[0] != EDBG_VENDOR_AVR_RSP || + buf[1] != ((1 << 4) | 1)) { + fprintf(stderr, + "%s: jtag3_edbg_recv(): Unexpected response 0x%02x, 0x%02x\n", + progname, buf[0], buf[1]); + return -1; + } + /* calculate length from response; CMSIS-DAP response might be larger */ + len = (buf[2] << 8) | buf[3]; + if (len > rv + 4) { + fprintf(stderr, + "%s: jtag3_edbg_recv(): Unexpected length value (%d > %d)\n", + progname, len, rv + 4); + len = rv + 4; + } + memmove(buf, buf + 4, len); + + *msg = buf; + + return rv; +} + int jtag3_recv(PROGRAMMER * pgm, unsigned char **msg) { unsigned short r_seqno; int rv; @@ -562,6 +818,11 @@ int jtag3_getsync(PROGRAMMER * pgm, int mode) { if (verbose >= 3) fprintf(stderr, "%s: jtag3_getsync()\n", progname); + if (pgm->flag & PGM_FL_IS_EDBG) { + if (jtag3_edbg_prepare(pgm) < 0) + return -1; + } + /* Get the sign-on information. */ buf[0] = SCOPE_GENERAL; buf[1] = CMD3_SIGN_ON; @@ -1034,9 +1295,15 @@ static int jtag3_open(PROGRAMMER * pgm, char * port) if (strncmp(port, "usb", 3) == 0) { #if defined(HAVE_LIBUSB) serdev = &usb_serdev_frame; - pinfo.usbinfo.vid = USB_VENDOR_ATMEL; + if (pgm->usbvid) + pinfo.usbinfo.vid = pgm->usbvid; + else + pinfo.usbinfo.vid = USB_VENDOR_ATMEL; pinfo.usbinfo.flags = 0; - pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; + if (pgm->usbpid) + pinfo.usbinfo.pid = pgm->usbpid; + else + pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_3; pgm->fd.usb.rep = USBDEV_BULK_EP_READ_3; pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_3; @@ -1052,6 +1319,17 @@ static int jtag3_open(PROGRAMMER * pgm, char * port) return -1; } + if (pgm->fd.usb.eep == 0) + { + /* The event EP has been deleted by usb_open(), so we are + running on a CMSIS-DAP device, using EDBG protocol */ + pgm->flag |= PGM_FL_IS_EDBG; + if (verbose) + fprintf(stderr, + "%s: Found CMSIS-DAP compliant device, using EDBG protocol\n", + progname); + } + /* * drain any extraneous input */ @@ -1078,9 +1356,15 @@ static int jtag3_open_dw(PROGRAMMER * pgm, char * port) if (strncmp(port, "usb", 3) == 0) { #if defined(HAVE_LIBUSB) serdev = &usb_serdev_frame; - pinfo.usbinfo.vid = USB_VENDOR_ATMEL; + if (pgm->usbvid) + pinfo.usbinfo.vid = pgm->usbvid; + else + pinfo.usbinfo.vid = USB_VENDOR_ATMEL; pinfo.usbinfo.flags = 0; - pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; + if (pgm->usbpid) + pinfo.usbinfo.pid = pgm->usbpid; + else + pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_3; pgm->fd.usb.rep = USBDEV_BULK_EP_READ_3; pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_3; @@ -1096,6 +1380,17 @@ static int jtag3_open_dw(PROGRAMMER * pgm, char * port) return -1; } + if (pgm->fd.usb.eep == 0) + { + /* The event EP has been deleted by usb_open(), so we are + running on a CMSIS-DAP device, using EDBG protocol */ + pgm->flag |= PGM_FL_IS_EDBG; + if (verbose) + fprintf(stderr, + "%s: Found CMSIS-DAP compliant device, using EDBG protocol\n", + progname); + } + /* * drain any extraneous input */ @@ -1122,9 +1417,15 @@ static int jtag3_open_pdi(PROGRAMMER * pgm, char * port) if (strncmp(port, "usb", 3) == 0) { #if defined(HAVE_LIBUSB) serdev = &usb_serdev_frame; - pinfo.usbinfo.vid = USB_VENDOR_ATMEL; + if (pgm->usbvid) + pinfo.usbinfo.vid = pgm->usbvid; + else + pinfo.usbinfo.vid = USB_VENDOR_ATMEL; pinfo.usbinfo.flags = 0; - pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; + if (pgm->usbpid) + pinfo.usbinfo.pid = pgm->usbpid; + else + pinfo.usbinfo.pid = USB_DEVICE_JTAGICE3; pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_3; pgm->fd.usb.rep = USBDEV_BULK_EP_READ_3; pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_3; @@ -1140,6 +1441,17 @@ static int jtag3_open_pdi(PROGRAMMER * pgm, char * port) return -1; } + if (pgm->fd.usb.eep == 0) + { + /* The event EP has been deleted by usb_open(), so we are + running on a CMSIS-DAP device, using EDBG protocol */ + pgm->flag |= PGM_FL_IS_EDBG; + if (verbose) + fprintf(stderr, + "%s: Found CMSIS-DAP compliant device, using EDBG protocol\n", + progname); + } + /* * drain any extraneous input */ @@ -1172,6 +1484,9 @@ void jtag3_close(PROGRAMMER * pgm) if (jtag3_command(pgm, buf, 4, &resp, "sign-off") >= 0) free(resp); + if (pgm->flag & PGM_FL_IS_EDBG) + jtag3_edbg_signoff(pgm); + serial_close(&pgm->fd); pgm->fd.ifd = -1; } diff --git a/jtag3_private.h b/jtag3_private.h index c226cfe0..8ee37505 100644 --- a/jtag3_private.h +++ b/jtag3_private.h @@ -222,6 +222,47 @@ #define XMEGA_ERASE_EEPROM_PAGE 0x06 #define XMEGA_ERASE_USERSIG 0x07 +/* EDBG vendor commands */ +#define EDBG_VENDOR_AVR_CMD 0x80 +#define EDBG_VENDOR_AVR_RSP 0x81 +#define EDBG_VENDOR_AVR_EVT 0x82 + +/* CMSIS-DAP commands */ +#define CMSISDAP_CMD_INFO 0x00 /* get info, followed by INFO byte */ +# define CMSISDAP_INFO_VID 0x01 /* vendor ID (string) */ +# define CMSISDAP_INFO_PID 0x02 /* product ID (string) */ +# define CMSISDAP_INFO_SERIAL 0x03 /* serial number (string) */ +# define CMSISDAP_INFO_FIRMWARE 0x04 /* firmware version (string) */ +# define CMSISDAP_INFO_TARGET_VENDOR 0x05 /* target device vendor (string) */ +# define CMSISDAP_INFO_TARGET_NAME 0x06 /* target device name (string) */ +# define CMSISDAP_INFO_CAPABILITIES 0xF0 /* debug unit capabilities (byte) */ +# define CMSISDAP_INFO_PACKET_COUNT 0xFE /* packet count (byte) (which packets, anyway?) */ +# define CMSISDAP_INFO_PACKET_SIZE 0xFF /* packet size (short) */ + +#define CMSISDAP_CMD_LED 0x01 /* LED control, followed by LED number and on/off byte */ +# define CMSISDAP_LED_CONNECT 0x00 /* connect LED */ +# define CMSISDAP_LED_RUNNING 0x01 /* running LED */ + +#define CMSISDAP_CMD_CONNECT 0x02 /* connect to target, followed by DAP mode */ +# define CMSISDAP_CONN_DEFAULT 0x00 +# define CMSISDAP_CONN_SWD 0x01 /* serial wire debug */ +# define CMSISDAP_CONN_JTAG 0x02 /* JTAG mode */ + +#define CMSISDAP_CMD_DISCONNECT 0x03 /* disconnect from target */ + +#define CMSISDAP_XFR_CONFIGURE 0x04 /* configure transfers; idle cycles (byte); + wait retry (short); match retry (short) */ + +#define CMSISDAP_CMD_WRITEAPBORT 0x08 /* write to CoreSight ABORT register of target */ + +#define CMSISDAP_CMD_DELAY 0x09 /* delay for number of microseconds (short) */ + +#define CMSISDAP_CMD_RESET 0x0A /* reset target */ + +#define CMSISDAP_CMD_SWJ_CLOCK 0x11 /* SWD/JTAG clock, (word) */ + +#define CMSISDAP_CMD_SWD_CONFIGURE 0x13 /* configure SWD protocol; (byte) */ + #if !defined(JTAG3_PRIVATE_EXPORTED) struct mega_device_desc { diff --git a/serial.h b/serial.h index 57266283..29f73bcf 100644 --- a/serial.h +++ b/serial.h @@ -41,6 +41,7 @@ union filedescriptor int wep; /* bulk write endpoint */ int eep; /* event read endpoint */ int max_xfer; /* max transfer size */ + int use_interrupt_xfer; /* device uses interrupt transfers */ } usb; }; @@ -52,6 +53,7 @@ union pinfo unsigned short vid; unsigned short pid; unsigned short flags; +#define PINFO_FL_USEHID 0x0001 } usbinfo; }; diff --git a/usb_libusb.c b/usb_libusb.c index b8e412a0..4e37be0e 100644 --- a/usb_libusb.c +++ b/usb_libusb.c @@ -70,6 +70,7 @@ static int usbdev_open(char * port, union pinfo pinfo, union filedescriptor *fd) usb_dev_handle *udev; char *serno, *cp2; int i; + int iface; size_t x; /* @@ -151,6 +152,21 @@ static int usbdev_open(char * port, union pinfo pinfo, union filedescriptor *fd) progname, usb_strerror()); strcpy(product, "[unnamed product]"); } + /* + * The CMSIS-DAP specification mandates the string + * "CMSIS-DAP" must be present somewhere in the + * product name string for a device compliant to + * that protocol. Use this for the decisision + * whether we have to search for a HID interface + * below. + */ + if(strstr(product, "CMSIS-DAP") != NULL) + { + pinfo.usbinfo.flags |= PINFO_FL_USEHID; + /* The JTAGICE3 running the CMSIS-DAP firmware doesn't + * use a separate endpoint for event reception. */ + fd->usb.eep = 0; + } if (verbose) fprintf(stderr, @@ -186,66 +202,102 @@ static int usbdev_open(char * port, union pinfo pinfo, union filedescriptor *fd) if (usb_set_configuration(udev, dev->config[0].bConfigurationValue)) { fprintf(stderr, - "%s: usbdev_open(): error setting configuration %d: %s\n", + "%s: usbdev_open(): WARNING: failed to set configuration %d: %s\n", progname, dev->config[0].bConfigurationValue, usb_strerror()); - goto trynext; + /* let's hope it has already been configured */ + // goto trynext; } - usb_interface = dev->config[0].interface[0].altsetting[0].bInterfaceNumber; - if (usb_claim_interface(udev, usb_interface)) + for (iface = 0; iface < dev->config[0].bNumInterfaces; iface++) + { + usb_interface = dev->config[0].interface[iface].altsetting[0].bInterfaceNumber; +#ifdef LIBUSB_HAS_GET_DRIVER_NP + /* + * Many Linux systems attach the usbhid driver + * by default to any HID-class device. On + * those, the driver needs to be detached before + * we can claim the interface. + */ + (void)usb_detach_kernel_driver_np(udev, usb_interface); +#endif + if (usb_claim_interface(udev, usb_interface)) + { + fprintf(stderr, + "%s: usbdev_open(): error claiming interface %d: %s\n", + progname, usb_interface, usb_strerror()); + } + else + { + if (pinfo.usbinfo.flags & PINFO_FL_USEHID) + { + /* only consider an interface that is of class HID */ + if (dev->config[0].interface[iface].altsetting[0].bInterfaceClass != + USB_CLASS_HID) + continue; + fd->usb.use_interrupt_xfer = 1; + } + break; + } + } + if (iface == dev->config[0].bNumInterfaces) { fprintf(stderr, - "%s: usbdev_open(): error claiming interface %d: %s\n", - progname, usb_interface, usb_strerror()); + "%s: usbdev_open(): no usable interface found\n", + progname); goto trynext; } fd->usb.handle = udev; if (fd->usb.rep == 0) - { - /* Try finding out what our read endpoint is. */ - for (i = 0; i < dev->config[0].interface[0].altsetting[0].bNumEndpoints; i++) { - int possible_ep = dev->config[0].interface[0].altsetting[0]. - endpoint[i].bEndpointAddress; + /* Try finding out what our read endpoint is. */ + for (i = 0; i < dev->config[0].interface[iface].altsetting[0].bNumEndpoints; i++) + { + int possible_ep = dev->config[0].interface[iface].altsetting[0]. + endpoint[i].bEndpointAddress; - if ((possible_ep & USB_ENDPOINT_DIR_MASK) != 0) - { - if (verbose > 1) + if ((possible_ep & USB_ENDPOINT_DIR_MASK) != 0) + { + if (verbose > 1) + { + fprintf(stderr, + "%s: usbdev_open(): using read endpoint 0x%02x\n", + progname, possible_ep); + } + fd->usb.rep = possible_ep; + break; + } + } + if (fd->usb.rep == 0) { fprintf(stderr, - "%s: usbdev_open(): using read endpoint 0x%02x\n", - progname, possible_ep); + "%s: usbdev_open(): cannot find a read endpoint, using 0x%02x\n", + progname, USBDEV_BULK_EP_READ_MKII); + fd->usb.rep = USBDEV_BULK_EP_READ_MKII; } - fd->usb.rep = possible_ep; - break; - } } - if (fd->usb.rep == 0) + for (i = 0; i < dev->config[0].interface[iface].altsetting[0].bNumEndpoints; i++) { - fprintf(stderr, - "%s: usbdev_open(): cannot find a read endpoint, using 0x%02x\n", - progname, USBDEV_BULK_EP_READ_MKII); - fd->usb.rep = USBDEV_BULK_EP_READ_MKII; - } - } - for (i = 0; i < dev->config[0].interface[0].altsetting[0].bNumEndpoints; i++) - { - if ((dev->config[0].interface[0].altsetting[0].endpoint[i].bEndpointAddress == fd->usb.rep || - dev->config[0].interface[0].altsetting[0].endpoint[i].bEndpointAddress == fd->usb.wep) && - dev->config[0].interface[0].altsetting[0].endpoint[i].wMaxPacketSize < fd->usb.max_xfer) + if ((dev->config[0].interface[iface].altsetting[0].endpoint[i].bEndpointAddress == fd->usb.rep || + dev->config[0].interface[iface].altsetting[0].endpoint[i].bEndpointAddress == fd->usb.wep) && + dev->config[0].interface[iface].altsetting[0].endpoint[i].wMaxPacketSize < fd->usb.max_xfer) { if (verbose != 0) fprintf(stderr, "%s: max packet size expected %d, but found %d due to EP 0x%02x's wMaxPacketSize\n", progname, fd->usb.max_xfer, - dev->config[0].interface[0].altsetting[0].endpoint[i].wMaxPacketSize, - dev->config[0].interface[0].altsetting[0].endpoint[i].bEndpointAddress); - fd->usb.max_xfer = dev->config[0].interface[0].altsetting[0].endpoint[i].wMaxPacketSize; + dev->config[0].interface[iface].altsetting[0].endpoint[i].wMaxPacketSize, + dev->config[0].interface[iface].altsetting[0].endpoint[i].bEndpointAddress); + fd->usb.max_xfer = dev->config[0].interface[iface].altsetting[0].endpoint[i].wMaxPacketSize; } } + if (pinfo.usbinfo.flags & PINFO_FL_USEHID) + { + if (usb_control_msg(udev, 0x21, 0x0a /* SET_IDLE */, 0, 0, NULL, 0, 100) < 0) + fprintf(stderr, "%s: usbdev_open(): SET_IDLE failed\n", progname); + } return 0; trynext: usb_close(udev); @@ -299,7 +351,10 @@ static int usbdev_send(union filedescriptor *fd, unsigned char *bp, size_t mlen) */ do { tx_size = (mlen < fd->usb.max_xfer)? mlen: fd->usb.max_xfer; - rv = usb_bulk_write(udev, fd->usb.wep, (char *)bp, tx_size, 10000); + if (fd->usb.use_interrupt_xfer) + rv = usb_interrupt_write(udev, fd->usb.wep, (char *)bp, tx_size, 10000); + else + rv = usb_bulk_write(udev, fd->usb.wep, (char *)bp, tx_size, 10000); if (rv != tx_size) { fprintf(stderr, "%s: usbdev_send(): wrote %d out of %d bytes, err = %s\n", @@ -341,16 +396,20 @@ static int usbdev_send(union filedescriptor *fd, unsigned char *bp, size_t mlen) * empty and more data are requested. */ static int -usb_fill_buf(usb_dev_handle *udev, int maxsize, int ep) +usb_fill_buf(usb_dev_handle *udev, int maxsize, int ep, int use_interrupt_xfer) { int rv; - rv = usb_bulk_read(udev, ep, usbbuf, maxsize, 10000); + if (use_interrupt_xfer) + rv = usb_interrupt_read(udev, ep, usbbuf, maxsize, 10000); + else + rv = usb_bulk_read(udev, ep, usbbuf, maxsize, 10000); if (rv < 0) { if (verbose > 1) - fprintf(stderr, "%s: usb_fill_buf(): usb_bulk_read() error %s\n", - progname, usb_strerror()); + fprintf(stderr, "%s: usb_fill_buf(): usb_%s_read() error %s\n", + progname, (use_interrupt_xfer? "interrupt": "bulk"), + usb_strerror()); return -1; } @@ -370,7 +429,7 @@ static int usbdev_recv(union filedescriptor *fd, unsigned char *buf, size_t nbyt { if (buflen <= bufptr) { - if (usb_fill_buf(udev, fd->usb.max_xfer, fd->usb.rep) < 0) + if (usb_fill_buf(udev, fd->usb.max_xfer, fd->usb.rep, fd->usb.use_interrupt_xfer) < 0) return -1; } amnt = buflen - bufptr > nbytes? nbytes: buflen - bufptr; @@ -441,13 +500,18 @@ static int usbdev_recv_frame(union filedescriptor *fd, unsigned char *buf, size_ n = 0; do { - rv = usb_bulk_read(udev, fd->usb.rep, usbbuf, - fd->usb.max_xfer, 10000); + if (fd->usb.use_interrupt_xfer) + rv = usb_interrupt_read(udev, fd->usb.rep, usbbuf, + fd->usb.max_xfer, 10000); + else + rv = usb_bulk_read(udev, fd->usb.rep, usbbuf, + fd->usb.max_xfer, 10000); if (rv < 0) { if (verbose > 1) - fprintf(stderr, "%s: usbdev_recv_frame(): usb_bulk_read(): %s\n", - progname, usb_strerror()); + fprintf(stderr, "%s: usbdev_recv_frame(): usb_%s_read(): %s\n", + progname, (fd->usb.use_interrupt_xfer? "interrupt": "bulk"), + usb_strerror()); return -1; } @@ -495,7 +559,10 @@ static int usbdev_drain(union filedescriptor *fd, int display) int rv; do { - rv = usb_bulk_read(udev, fd->usb.rep, usbbuf, fd->usb.max_xfer, 100); + if (fd->usb.use_interrupt_xfer) + rv = usb_interrupt_read(udev, fd->usb.rep, usbbuf, fd->usb.max_xfer, 100); + else + rv = usb_bulk_read(udev, fd->usb.rep, usbbuf, fd->usb.max_xfer, 100); if (rv > 0 && verbose >= 4) fprintf(stderr, "%s: usbdev_drain(): flushed %d characters\n", progname, rv); diff --git a/usbdevs.h b/usbdevs.h index c546f500..5bf91fdb 100644 --- a/usbdevs.h +++ b/usbdevs.h @@ -31,6 +31,8 @@ #define USB_DEVICE_STK600 0x2106 #define USB_DEVICE_AVRDRAGON 0x2107 #define USB_DEVICE_JTAGICE3 0x2110 +#define USB_DEVICE_XPLAINEDPRO 0x2111 +#define USB_DEVICE_JTAG3_EDBG 0x2140 /* JTAGICEmkII, AVRISPmkII */ #define USBDEV_BULK_EP_WRITE_MKII 0x02