diff --git a/ChangeLog b/ChangeLog index bba6e491..bc4ac224 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2016-02-20 Joerg Wunsch + + * jtag3.c: add support for libhidapi as (optional) transport for + CMSIS-DAP compliant debuggers (JTAGICE3 with firmware 3+, + AtmelICE, EDBG, mEDBG) + * usb_hidapi.c: (New file) + * libavrdude.h: Mention usbhid_serdev + * configure.ac: Bump version date + 2016-02-18 Joerg Wunsch (Obtained from patch #8717: pattch for mcprog and libhidapi support) diff --git a/Makefile.am b/Makefile.am index 632ea1ca..d1ed4784 100644 --- a/Makefile.am +++ b/Makefile.am @@ -169,6 +169,7 @@ libavrdude_a_SOURCES = \ usbasp.c \ usbasp.h \ usbdevs.h \ + usb_hidapi.c \ usb_libusb.c \ usbtiny.h \ usbtiny.c \ diff --git a/NEWS b/NEWS index a72013d8..be0c9239 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ Current: * Major changes compared to the previous version: + - libhidapi support (part of patch #8717) + - use libhidapi as (optional) transport for CMSIS-DAP compliant + debuggers (JTAGICE3 with firmware 3+, AtmelICE, EDBG, mEDBG) + * New devices supported: * New programmers supported: diff --git a/configure.ac b/configure.ac index 6ea8849a..8194123a 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.3-20160216, avrdude-dev@nongnu.org) +AC_INIT(avrdude, 6.3-20160220, avrdude-dev@nongnu.org) AC_CANONICAL_BUILD AC_CANONICAL_HOST diff --git a/jtag3.c b/jtag3.c index 4b718e6e..3084c320 100644 --- a/jtag3.c +++ b/jtag3.c @@ -1314,8 +1314,8 @@ int jtag3_open_common(PROGRAMMER * pgm, char * port) LNODEID usbpid; int rv = -1; -#if !defined(HAVE_LIBUSB) - avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n"); +#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBHIDAPI) + avrdude_message(MSG_INFO, "avrdude was compiled without USB or HIDAPI support.\n"); return -1; #endif @@ -1325,7 +1325,6 @@ int jtag3_open_common(PROGRAMMER * pgm, char * port) return -1; } - serdev = &usb_serdev_frame; if (pgm->usbvid) pinfo.usbinfo.vid = pgm->usbvid; else @@ -1335,17 +1334,42 @@ int jtag3_open_common(PROGRAMMER * pgm, char * port) if (lfirst(pgm->usbpid) == NULL) ladd(pgm->usbpid, (void *)USB_DEVICE_JTAGICE3); +#if defined(HAVE_LIBHIDAPI) + /* + * Try HIDAPI first. LibUSB is more generic, but might then cause + * troubles for HID-class devices in some OSes (like Windows). + */ + serdev = &usbhid_serdev; for (usbpid = lfirst(pgm->usbpid); rv < 0 && usbpid != NULL; usbpid = lnext(usbpid)) { pinfo.usbinfo.flags = PINFO_FL_SILENT; pinfo.usbinfo.pid = *(int *)(ldata(usbpid)); 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; - pgm->fd.usb.eep = USBDEV_EVT_EP_READ_3; + pgm->fd.usb.eep = 0; strcpy(pgm->port, port); rv = serial_open(port, pinfo, &pgm->fd); } + if (rv < 0) { +#endif /* HAVE_LIBHIDAPI */ +#if defined(HAVE_LIBUSB) + serdev = &usb_serdev_frame; + for (usbpid = lfirst(pgm->usbpid); rv < 0 && usbpid != NULL; usbpid = lnext(usbpid)) { + pinfo.usbinfo.flags = PINFO_FL_SILENT; + pinfo.usbinfo.pid = *(int *)(ldata(usbpid)); + 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; + pgm->fd.usb.eep = USBDEV_EVT_EP_READ_3; + + strcpy(pgm->port, port); + rv = serial_open(port, pinfo, &pgm->fd); + } +#endif /* HAVE_LIBUSB */ +#if defined(HAVE_LIBHIDAPI) + } +#endif if (rv < 0) { avrdude_message(MSG_INFO, "%s: jtag3_open_common(): Did not find any device matching VID 0x%04x and PID list: ", progname, (unsigned)pinfo.usbinfo.vid); diff --git a/libavrdude.h b/libavrdude.h index ac79640c..d5f32e46 100644 --- a/libavrdude.h +++ b/libavrdude.h @@ -563,6 +563,7 @@ extern struct serial_device serial_serdev; extern struct serial_device usb_serdev; extern struct serial_device usb_serdev_frame; extern struct serial_device avrdoper_serdev; +extern struct serial_device usbhid_serdev; #define serial_open (serdev->open) #define serial_setspeed (serdev->setspeed) diff --git a/usb_hidapi.c b/usb_hidapi.c new file mode 100644 index 00000000..aa306b69 --- /dev/null +++ b/usb_hidapi.c @@ -0,0 +1,353 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2016 Joerg Wunsch + * + * 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, see . + */ + +/* $Id$ */ + +/* + * USB interface via libhidapi for avrdude. + */ + +#include "ac_cfg.h" +#if defined(HAVE_LIBHIDAPI) + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "avrdude.h" +#include "libavrdude.h" + +#include "usbdevs.h" + +#if defined(WIN32NATIVE) +/* someone has defined "interface" to "struct" in Cygwin */ +# undef interface +#endif + +/* + * The "baud" parameter is meaningless for USB devices, so we reuse it + * to pass the desired USB device ID. + */ +static int usbhid_open(char * port, union pinfo pinfo, union filedescriptor *fd) +{ + hid_device *dev; + char *serno, *cp2; + size_t x; + unsigned char usbbuf[USBDEV_MAX_XFER_3 + 1]; + + if (fd->usb.max_xfer == 0) + fd->usb.max_xfer = USBDEV_MAX_XFER_3; + + /* + * The syntax for usb devices is defined as: + * + * -P usb[:serialnumber] + * + * See if we've got a serial number passed here. The serial number + * might contain colons which we remove below, and we compare it + * right-to-left, so only the least significant nibbles need to be + * specified. + */ + if ((serno = strchr(port, ':')) != NULL) + { + /* first, drop all colons there if any */ + cp2 = ++serno; + + while ((cp2 = strchr(cp2, ':')) != NULL) + { + x = strlen(cp2) - 1; + memmove(cp2, cp2 + 1, x); + cp2[x] = '\0'; + } + + if (strlen(serno) > 12) + { + avrdude_message(MSG_INFO, "%s: usbhid_open(): invalid serial number \"%s\"\n", + progname, serno); + return -1; + } + + wchar_t wserno[15]; + mbstowcs(wserno, serno, 15); + size_t serlen = strlen(serno); + + /* + * Now, try finding all devices matching VID:PID, and compare + * their serial numbers against the requested one. + */ + struct hid_device_info *list, *walk; + list = hid_enumerate(pinfo.usbinfo.vid, pinfo.usbinfo.pid); + if (list == NULL) + return -1; + + walk = list; + while (walk) + { + avrdude_message(MSG_NOTICE, "%s: usbhid_open(): Found %ls, serno: %ls\n", + progname, walk->product_string, walk->serial_number); + if (wcscmp(walk->serial_number + serlen, wserno) == 0) + { + /* found matching serial number */ + break; + } + avrdude_message(MSG_DEBUG, "%s: usbhid_open(): serial number doesn't match\n", + progname); + } + if (walk == NULL) + { + avrdude_message(MSG_INFO, "%s: usbhid_open(): No matching device found\n", + progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: usbhid_open(): Opening path %s\n", + progname, walk->path); + dev = hid_open_path(walk->path); + hid_free_enumeration(list); + if (dev == NULL) + { + avrdude_message(MSG_INFO, + "%s: usbhid_open(): Found device, but hid_open_path() failed\n", + progname); + return -1; + } + } + else + { + /* + * No serial number requested, pass straight to hid_open() + */ + dev = hid_open(pinfo.usbinfo.vid, pinfo.usbinfo.pid, NULL); + if (dev == NULL) + { + avrdude_message(MSG_INFO, "%s: usbhid_open(): No device found\n", + progname); + return -1; + } + } + + fd->usb.handle = dev; + + /* + * Try finding out the endpoint size. Alas, libhidapi doesn't + * provide us with an API function for that, nor for the report + * descriptor (which also contains that information). + * + * Since the Atmel tools a very picky to only respond to incoming + * packets that have full size, we need to know whether our device + * handles 512-byte data (JTAGICE3 in CMSIS-DAP mode, or AtmelICE, + * both on USB 2.0 connections), or 64-byte data only (both these on + * USB 1.1 connections, or mEDBG devices). + * + * In order to find out, we send a CMSIS-DAP DAP_Info command + * (0x00), with an ID of 0xFF (get maximum packet size). In theory, + * this gets us the desired information, but this suffers from a + * chicken-and-egg problem: the request must be sent with a + * full-sized packet lest the ICE won't answer. Thus, we send a + * 64-byte packet first, and if we don't get a timely reply, + * complete that request by sending another 448 bytes, and hope it + * will eventually reply. + * + * Note that libhidapi always requires a report ID as the first + * byte. If the target doesn't use report IDs (Atmel targets + * don't), this first byte must be 0x00. However, the length must + * be incremented by one, as the report ID will be omitted by the + * hidapi library. + */ + if (pinfo.usbinfo.vid == USB_VENDOR_ATMEL) + { + avrdude_message(MSG_DEBUG, "%s: usbhid_open(): Probing for max. packet size\n", + progname); + memset(usbbuf, 0, sizeof usbbuf); + usbbuf[0] = 0; /* no HID reports used */ + usbbuf[1] = 0; /* DAP_Info */ + usbbuf[2] = 0xFF; /* get max. packet size */ + + hid_write(dev, usbbuf, 65); + fd->usb.max_xfer = 64; /* first guess */ + + memset(usbbuf, 0, sizeof usbbuf); + int res = hid_read_timeout(dev, usbbuf, 10 /* bytes */, 50 /* milliseconds */); + if (res == 0) { + /* no timely response, assume 512 byte size */ + hid_write(dev, usbbuf, (512 - 64) + 1); + fd->usb.max_xfer = 512; + res = hid_read_timeout(dev, usbbuf, 10, 50); + } + if (res <= 0) { + avrdude_message(MSG_INFO, "%s: usbhid_open(): No response from device\n", + progname); + hid_close(dev); + return -1; + } + if (usbbuf[0] != 0 || usbbuf[1] != 2) { + avrdude_message(MSG_INFO, + "%s: usbhid_open(): Unexpected reply to DAP_Info: 0x%02x 0x%02x\n", + progname, usbbuf[0], usbbuf[1]); + } else { + fd->usb.max_xfer = usbbuf[2] + (usbbuf[3] << 8); + avrdude_message(MSG_DEBUG, + "%s: usbhid_open(): Setting max_xfer from DAP_Info response to %d\n", + progname, fd->usb.max_xfer); + } + } + if (fd->usb.max_xfer > USBDEV_MAX_XFER_3) { + avrdude_message(MSG_INFO, + "%s: usbhid_open(): Unexpected max size %d, reducing to %d\n", + progname, fd->usb.max_xfer, USBDEV_MAX_XFER_3); + fd->usb.max_xfer = USBDEV_MAX_XFER_3; + } + + return 0; +} + +static void usbhid_close(union filedescriptor *fd) +{ + hid_device *udev = (hid_device *)fd->usb.handle; + + if (udev == NULL) + return; + + hid_close(udev); +} + + +static int usbhid_send(union filedescriptor *fd, const unsigned char *bp, size_t mlen) +{ + hid_device *udev = (hid_device *)fd->usb.handle; + int rv; + int i = mlen; + const unsigned char * p = bp; + unsigned char usbbuf[USBDEV_MAX_XFER_3]; + + + int tx_size; + + if (udev == NULL) + return -1; + + tx_size = (mlen < USBDEV_MAX_XFER_3)? mlen: USBDEV_MAX_XFER_3; + usbbuf[0] = 0; /* no report ID used */ + memcpy(usbbuf + 1, bp, tx_size); + rv = hid_write(udev, usbbuf, tx_size + 1); + if (rv < 0) { + avrdude_message(MSG_INFO, "%s: Failed to write %d bytes to USB\n", + progname, tx_size); + return -1; + } + if (rv != tx_size + 1) + avrdude_message(MSG_INFO, "%s: Short write to USB: %d bytes out of %d written\n", + progname, rv, tx_size + 1); + + if (verbose > 4) + { + avrdude_message(MSG_TRACE2, "%s: Sent: ", progname); + + while (i) { + unsigned char c = *p; + if (isprint(c)) { + avrdude_message(MSG_TRACE2, "%c ", c); + } + else { + avrdude_message(MSG_TRACE2, ". "); + } + avrdude_message(MSG_TRACE2, "[%02x] ", c); + + p++; + i--; + } + avrdude_message(MSG_TRACE2, "\n"); + } + return 0; +} + +static int usbhid_recv(union filedescriptor *fd, unsigned char *buf, size_t nbytes) +{ + hid_device *udev = (hid_device *)fd->usb.handle; + int i, rv; + unsigned char * p = buf; + + if (udev == NULL) + return -1; + + rv = i = hid_read_timeout(udev, buf, nbytes, 100); + if (i != nbytes) + avrdude_message(MSG_INFO, + "%s: Short read, read only %d out of %u bytes\n", + progname, i, nbytes); + + if (verbose > 4) + { + avrdude_message(MSG_TRACE2, "%s: Recv: ", progname); + + while (i) { + unsigned char c = *p; + if (isprint(c)) { + avrdude_message(MSG_TRACE2, "%c ", c); + } + else { + avrdude_message(MSG_TRACE2, ". "); + } + avrdude_message(MSG_TRACE2, "[%02x] ", c); + + p++; + i--; + } + avrdude_message(MSG_TRACE2, "\n"); + } + + return rv; +} + +static int usbhid_drain(union filedescriptor *fd, int display) +{ + /* + * There is not much point in trying to flush any data + * on an USB endpoint, as the endpoint is supposed to + * start afresh after being configured from the host. + * + * As trying to flush the data here caused strange effects + * in some situations (see + * https://savannah.nongnu.org/bugs/index.php?43268 ) + * better avoid it. + */ + + return 0; +} + +/* + * Device descriptor. + */ +struct serial_device usbhid_serdev = +{ + .open = usbhid_open, + .close = usbhid_close, + .send = usbhid_send, + .recv = usbhid_recv, + .drain = usbhid_drain, + .flags = SERDEV_FL_NONE, +}; + +#endif /* HAVE_LIBHIDAPI */