diff --git a/ChangeLog b/ChangeLog index 3e7587db..226056bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2006-12-21 Joerg Wunsch + + Submitted by Christian Starkjohann: + patch #5507: Support for AVR-Doper USB programmer in HID mode + * configure.ac: Add hooks to detect the Win32 HID library, + as well as the existence of . + * Makefile.am: Add new files. + * my_ddk_hidsdi.h: (New file.) + * ser_avrdoper.c: (New file.) + * serial.h: Add declaration for avrdoper_serdev. + * stk500v2.c: Add hook to divert to the AVR Doper code. + * avrdude.1: Document the AVR Doper support. + * doc/avrdude.texi: (Ditto.) + 2006-12-15 Joerg Wunsch Submitted by ivanv at netman.ru diff --git a/Makefile.am b/Makefile.am index b2300a07..5f881ab3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,6 +98,7 @@ avrdude_SOURCES = \ lists.c \ lists.h \ main.c \ + my_ddk_hidsdi.h \ par.c \ par.h \ pgm.c \ @@ -112,6 +113,7 @@ avrdude_SOURCES = \ serbb.h \ serbb_posix.c \ serbb_win32.c \ + ser_avrdoper.c \ ser_posix.c \ ser_win32.c \ solaris_ecpp.h \ diff --git a/NEWS b/NEWS index 99655380..bca82d82 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ Current: * Add support for debugWire (both, JTAG ICE mkII, and AVR Dragon). + * Add support for the AVR Doper USB HID-class programmer. + * Bugfixes. Version 5.2: diff --git a/avrdude.1 b/avrdude.1 index 9491ae63..2abb1ca6 100644 --- a/avrdude.1 +++ b/avrdude.1 @@ -412,6 +412,12 @@ from the serial number need to be given. As the AVRISP mkII device can only be talked to over USB, the very same method of specifying the port is required there. .Pp +For the USB programmer "AVR-Doper" running in HID mode, the port must +be specified as +.Ar avrdoper. +Libusb support is required on Unix but not on Windows. For more +information about AVR-Doper see http://www.obdev.at/avrusb/avrdoper.html. +.Pp For programmers that attach to a serial port using some kind of higher level protocol (as opposed to bit-bang style programmers), .Ar port diff --git a/configure.ac b/configure.ac index 555f7d18..7696d5c4 100644 --- a/configure.ac +++ b/configure.ac @@ -60,9 +60,49 @@ if test x$have_libusb = xyes; then fi AC_SUBST(LIBUSB, $LIBUSB) +AC_MSG_CHECKING([for a Win32 HID libray]) +SAVED_LIBS="${LIBS}" +case $target in + *-*-mingw32*) + LIBHID="-lhid -lsetupapi" + HIDINCLUDE="#include " + ;; + *) + LIBHID="-lhid" + HIDINCLUDE='#include "my_ddk_hidsdi.h"' + ;; +esac +LIBS="${LIBS} ${LIBHID}" + +AH_TEMPLATE([HAVE_LIBHID], + [Define if HID support is enabled via the Win32 DDK]) +AC_TRY_RUN([#include +#include +$HIDINCLUDE + +int +main(void) +{ + GUID hidGuid; + HidD_GetHidGuid(&hidGuid); + + return 0; +} +], [have_libhid=yes], [have_libhid=no], [have_libhid=no]) +AC_MSG_RESULT([$have_libhid]) +if test x$have_libhid = xyes; then + AC_DEFINE([HAVE_LIBHID]) +else + LIBHID="" +fi +LIBS="${SAVED_LIBS}" + # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h limits.h stdlib.h string.h sys/ioctl.h sys/time.h termios.h unistd.h]) +AC_CHECK_HEADERS([limits.h stdlib.h string.h]) +AC_CHECK_HEADERS([fcntl.h sys/ioctl.h sys/time.h termios.h unistd.h]) +AC_CHECK_HEADERS([ddk/hidsdi.h],,,[#include +#include ]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/doc/avrdude.texi b/doc/avrdude.texi index 18c69c15..4bb7d7bc 100644 --- a/doc/avrdude.texi +++ b/doc/avrdude.texi @@ -559,6 +559,11 @@ attached to USB, see @ref{Example Command Line Invocations}. As the AVRISP mkII device can only be talked to over USB, the very same method of specifying the port is required there. +For the USB programmer "AVR-Doper" running in HID mode, the port must +be specified as @var{avrdoper}. Libusb support is required on Unix +but not on Windows. For more information about AVR-Doper see +@url{http://www.obdev.at/avrusb/avrdoper.html}. + For programmers that attach to a serial port using some kind of higher level protocol (as opposed to bit-bang style programmers), @var{port} can be specified as @code{net}:@var{host}:@var{port}. diff --git a/my_ddk_hidsdi.h b/my_ddk_hidsdi.h new file mode 100644 index 00000000..99859d06 --- /dev/null +++ b/my_ddk_hidsdi.h @@ -0,0 +1,49 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2006 Christian Starkjohann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* +The following is a replacement for hidsdi.h from the Windows DDK. It defines some +of the types and function prototypes of this header for our project. If you +have the Windows DDK version of this file or a version shipped with MinGW, use +that instead. +*/ +#ifndef MY_DDK_HIDSDI_H +#define MY_DDK_HIDSDI_H +#include +#include +#include +typedef struct{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +}HIDD_ATTRIBUTES; +void __stdcall HidD_GetHidGuid(OUT LPGUID hidGuid); +BOOLEAN __stdcall HidD_GetAttributes(IN HANDLE device, OUT HIDD_ATTRIBUTES *attributes); +BOOLEAN __stdcall HidD_GetManufacturerString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen); +BOOLEAN __stdcall HidD_GetProductString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen); +BOOLEAN __stdcall HidD_GetSerialNumberString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen); +BOOLEAN __stdcall HidD_GetFeature(IN HANDLE device, OUT void *reportBuffer, IN ULONG bufferLen); +BOOLEAN __stdcall HidD_SetFeature(IN HANDLE device, IN void *reportBuffer, IN ULONG bufferLen); +BOOLEAN __stdcall HidD_GetNumInputBuffers(IN HANDLE device, OUT ULONG *numBuffers); +BOOLEAN __stdcall HidD_SetNumInputBuffers(IN HANDLE device, OUT ULONG numBuffers); +#include +#endif /* MY_DDK_HIDSDI_H */ diff --git a/ser_avrdoper.c b/ser_avrdoper.c new file mode 100644 index 00000000..638fb5eb --- /dev/null +++ b/ser_avrdoper.c @@ -0,0 +1,647 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2003-2004 Theodore A. Roth + * Copyright (C) 2006 Joerg Wunsch + * Copyright (C) 2006 Christian Starkjohann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Serial Interface emulation for USB programmer "AVR-Doper" in HID mode. + */ + +#include "ac_cfg.h" + +#if defined(HAVE_LIBUSB) || (defined(WIN32NATIVE) && defined(HAVE_LIBHID)) + +#include +#include + +#include "serial.h" + +/* ------------------------------------------------------------------------ */ + +/* Numeric constants for 'reportType' parameters */ +#define USB_HID_REPORT_TYPE_INPUT 1 +#define USB_HID_REPORT_TYPE_OUTPUT 2 +#define USB_HID_REPORT_TYPE_FEATURE 3 + +/* These are the error codes which can be returned by functions of this + * module. + */ +#define USB_ERROR_NONE 0 +#define USB_ERROR_ACCESS 1 +#define USB_ERROR_NOTFOUND 2 +#define USB_ERROR_BUSY 16 +#define USB_ERROR_IO 5 + +#define USB_VENDOR_ID 0x16c0 +#define USB_PRODUCT_ID 0x05df + +extern char *progname; +extern int verbose; + +static int reportDataSizes[4] = {13, 29, 61, 125}; + +static unsigned char avrdoperRxBuffer[280]; /* buffer for receive data */ +static int avrdoperRxLength = 0; /* amount of valid bytes in rx buffer */ +static int avrdoperRxPosition = 0; /* amount of bytes already consumed in rx buffer */ + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +#ifdef WIN32NATIVE + +#include +#include + +#if defined(HAVE_DDK_HIDSDI_H) +# include +#else +# include "my_ddk_hidsdi.h" +#endif +#include + +#ifdef USB_DEBUG +#define DEBUG_PRINT(arg) printf arg +#else +#define DEBUG_PRINT(arg) +#endif + +/* ------------------------------------------------------------------------ */ + +static void convertUniToAscii(char *buffer) +{ + unsigned short *uni = (void *)buffer; + char *ascii = buffer; + + while(*uni != 0){ + if(*uni >= 256){ + *ascii++ = '?'; + }else{ + *ascii++ = *uni++; + } + } + *ascii++ = 0; +} + +static int usbOpenDevice(union filedescriptor *fdp, int vendor, char *vendorName, + int product, char *productName, int usesReportIDs) +{ + GUID hidGuid; /* GUID for HID driver */ + HDEVINFO deviceInfoList; + SP_DEVICE_INTERFACE_DATA deviceInfo; + SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = NULL; + DWORD size; + int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */ + int errorCode = USB_ERROR_NOTFOUND; + HANDLE handle = INVALID_HANDLE_VALUE; + HIDD_ATTRIBUTES deviceAttributes; + + HidD_GetHidGuid(&hidGuid); + deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, + DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + deviceInfo.cbSize = sizeof(deviceInfo); + for(i=0;;i++){ + if(handle != INVALID_HANDLE_VALUE){ + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + } + if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo)) + break; /* no more entries */ + /* first do a dummy call just to determine the actual size required */ + SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL); + if(deviceDetails != NULL) + free(deviceDetails); + deviceDetails = malloc(size); + deviceDetails->cbSize = sizeof(*deviceDetails); + /* this call is for real: */ + SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, + size, &size, NULL); + DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath)); + /* attempt opening for R/W -- we don't care about devices which can't be accessed */ + handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + openFlag, NULL); + if(handle == INVALID_HANDLE_VALUE){ + DEBUG_PRINT(("opening failed: %d\n", (int)GetLastError())); + /* errorCode = USB_ERROR_ACCESS; opening will always fail for mouse -- ignore */ + continue; + } + deviceAttributes.Size = sizeof(deviceAttributes); + HidD_GetAttributes(handle, &deviceAttributes); + DEBUG_PRINT(("device attributes: vid=%d pid=%d\n", + deviceAttributes.VendorID, deviceAttributes.ProductID)); + if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product) + continue; /* ignore this device */ + errorCode = USB_ERROR_NOTFOUND; + if(vendorName != NULL && productName != NULL){ + char buffer[512]; + if(!HidD_GetManufacturerString(handle, buffer, sizeof(buffer))){ + DEBUG_PRINT(("error obtaining vendor name\n")); + errorCode = USB_ERROR_IO; + continue; + } + convertUniToAscii(buffer); + DEBUG_PRINT(("vendorName = \"%s\"\n", buffer)); + if(strcmp(vendorName, buffer) != 0) + continue; + if(!HidD_GetProductString(handle, buffer, sizeof(buffer))){ + DEBUG_PRINT(("error obtaining product name\n")); + errorCode = USB_ERROR_IO; + continue; + } + convertUniToAscii(buffer); + DEBUG_PRINT(("productName = \"%s\"\n", buffer)); + if(strcmp(productName, buffer) != 0) + continue; + } + break; /* we have found the device we are looking for! */ + } + SetupDiDestroyDeviceInfoList(deviceInfoList); + if(deviceDetails != NULL) + free(deviceDetails); + if(handle != INVALID_HANDLE_VALUE){ + fdp->pfd = (void *)handle; + } + return errorCode; +} + +/* ------------------------------------------------------------------------ */ + +static void usbCloseDevice(union filedescriptor *fdp) +{ + CloseHandle((HANDLE)fdp->pfd); +} + +/* ------------------------------------------------------------------------ */ + +static int usbSetReport(union filedescriptor *fdp, int reportType, char *buffer, int len) +{ + HANDLE handle = (HANDLE)fdp->pfd; + BOOLEAN rval = 0; + DWORD bytesWritten; + + switch(reportType){ + case USB_HID_REPORT_TYPE_INPUT: + break; + case USB_HID_REPORT_TYPE_OUTPUT: + rval = WriteFile(handle, buffer, len, &bytesWritten, NULL); + break; + case USB_HID_REPORT_TYPE_FEATURE: + rval = HidD_SetFeature(handle, buffer, len); + break; + } + return rval == 0 ? USB_ERROR_IO : 0; +} + +/* ------------------------------------------------------------------------ */ + +static int usbGetReport(union filedescriptor *fdp, int reportType, int reportNumber, + char *buffer, int *len) +{ + HANDLE handle = (HANDLE)fdp->pfd; + BOOLEAN rval = 0; + DWORD bytesRead; + + switch(reportType){ + case USB_HID_REPORT_TYPE_INPUT: + buffer[0] = reportNumber; + rval = ReadFile(handle, buffer, *len, &bytesRead, NULL); + if(rval) + *len = bytesRead; + break; + case USB_HID_REPORT_TYPE_OUTPUT: + break; + case USB_HID_REPORT_TYPE_FEATURE: + buffer[0] = reportNumber; + rval = HidD_GetFeature(handle, buffer, *len); + break; + } + return rval == 0 ? USB_ERROR_IO : 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +#else /* !WIN32NATIVE */ + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +#include + +/* ------------------------------------------------------------------------- */ + +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_SET_REPORT 0x09 + +static int usesReportIDs; + +/* ------------------------------------------------------------------------- */ + +static int usbGetStringAscii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen) +{ + char buffer[256]; + int rval, i; + + if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8) + index, langid, buffer, + sizeof(buffer), 1000)) < 0) + return rval; + if(buffer[1] != USB_DT_STRING) + return 0; + if((unsigned char)buffer[0] < rval) + rval = (unsigned char)buffer[0]; + rval /= 2; + /* lossy conversion to ISO Latin1 */ + for(i=1;i buflen) /* destination buffer overflow */ + break; + buf[i-1] = buffer[2 * i]; + if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */ + buf[i-1] = '?'; + } + buf[i-1] = 0; + return i-1; +} + +static int usbOpenDevice(union filedescriptor *fdp, int vendor, char *vendorName, + int product, char *productName, int doReportIDs) +{ + struct usb_bus *bus; + struct usb_device *dev; + usb_dev_handle *handle = NULL; + int errorCode = USB_ERROR_NOTFOUND; + static int didUsbInit = 0; + + if(!didUsbInit){ + usb_init(); + didUsbInit = 1; + } + usb_find_busses(); + usb_find_devices(); + for(bus=usb_get_busses(); bus; bus=bus->next){ + for(dev=bus->devices; dev; dev=dev->next){ + if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){ + char string[256]; + int len; + handle = usb_open(dev); /* we need to open the device in order to query strings */ + if(!handle){ + errorCode = USB_ERROR_ACCESS; + fprintf(stderr, "Warning: cannot open USB device: %s\n", + usb_strerror()); + continue; + } + if(vendorName == NULL && productName == NULL){ /* name does not matter */ + break; + } + /* now check whether the names match: */ + len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, + 0x0409, string, sizeof(string)); + if(len < 0){ + errorCode = USB_ERROR_IO; + fprintf(stderr, + "Warning: cannot query manufacturer for device: %s\n", + usb_strerror()); + }else{ + errorCode = USB_ERROR_NOTFOUND; + /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */ + if(strcmp(string, vendorName) == 0){ + len = usbGetStringAscii(handle, dev->descriptor.iProduct, + 0x0409, string, sizeof(string)); + if(len < 0){ + errorCode = USB_ERROR_IO; + fprintf(stderr, + "Warning: cannot query product for device: %s\n", + usb_strerror()); + }else{ + errorCode = USB_ERROR_NOTFOUND; + /* fprintf(stderr, "seen product ->%s<-\n", string); */ + if(strcmp(string, productName) == 0) + break; + } + } + } + usb_close(handle); + handle = NULL; + } + } + if(handle) + break; + } + if(handle != NULL){ + int rval, retries = 3; + if(usb_set_configuration(handle, 1)){ + fprintf(stderr, "Warning: could not set configuration: %s\n", + usb_strerror()); + } + /* now try to claim the interface and detach the kernel HID driver on + * linux and other operating systems which support the call. + */ + while((rval = usb_claim_interface(handle, 0)) != 0 && retries-- > 0){ +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + if(usb_detach_kernel_driver_np(handle, 0) < 0){ + fprintf(stderr, "Warning: could not detach kernel HID driver: %s\n", + usb_strerror()); + } +#endif + } + if(rval != 0) + fprintf(stderr, "Warning: could not claim interface\n"); +/* Continue anyway, even if we could not claim the interface. Control transfers + * should still work. + */ + errorCode = 0; + fdp->pfd = (void *)handle; + usesReportIDs = doReportIDs; + } + return errorCode; +} + +/* ------------------------------------------------------------------------- */ + +static void usbCloseDevice(union filedescriptor *fdp) +{ + usb_close((usb_dev_handle *)fdp->pfd); +} + +/* ------------------------------------------------------------------------- */ + +static int usbSetReport(union filedescriptor *fdp, int reportType, char *buffer, int len) +{ + int bytesSent; + + if(!usesReportIDs){ + buffer++; /* skip dummy report ID */ + len--; + } + bytesSent = usb_control_msg((usb_dev_handle *)fdp->pfd, USB_TYPE_CLASS | + USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, + reportType << 8 | buffer[0], 0, buffer, len, 5000); + if(bytesSent != len){ + if(bytesSent < 0) + fprintf(stderr, "Error sending message: %s\n", usb_strerror()); + return USB_ERROR_IO; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static int usbGetReport(union filedescriptor *fdp, int reportType, int reportNumber, + char *buffer, int *len) +{ + int bytesReceived, maxLen = *len; + + if(!usesReportIDs){ + buffer++; /* make room for dummy report ID */ + maxLen--; + } + bytesReceived = usb_control_msg((usb_dev_handle *)fdp->pfd, USB_TYPE_CLASS | + USB_RECIP_INTERFACE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, + reportType << 8 | reportNumber, 0, buffer, maxLen, 5000); + if(bytesReceived < 0){ + fprintf(stderr, "Error sending message: %s\n", usb_strerror()); + return USB_ERROR_IO; + } + *len = bytesReceived; + if(!usesReportIDs){ + buffer[-1] = reportNumber; /* add dummy report ID */ + *len++; + } + return 0; +} + +#endif /* WIN32NATIVE */ + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +/* ------------------------------------------------------------------------- */ + +static void dumpBlock(char *prefix, unsigned char *buf, int len) +{ + int i; + + if(len <= 8){ /* more compact format for short blocks */ + fprintf(stderr, "%s: %d bytes: ", prefix, len); + for(i = 0; i < len; i++){ + fprintf(stderr, "%02x ", buf[i]); + } + fprintf(stderr, " \""); + for(i = 0; i < len; i++){ + if(buf[i] >= 0x20 && buf[i] < 0x7f){ + fputc(buf[i], stderr); + }else{ + fputc('.', stderr); + } + } + fprintf(stderr, "\"\n"); + }else{ + fprintf(stderr, "%s: %d bytes:\n", prefix, len); + while(len > 0){ + for(i = 0; i < 16; i++){ + if(i < len){ + fprintf(stderr, "%02x ", buf[i]); + }else{ + fprintf(stderr, " "); + } + if(i == 7) + fputc(' ', stderr); + } + fprintf(stderr, " \""); + for(i = 0; i < 16; i++){ + if(i < len){ + if(buf[i] >= 0x20 && buf[i] < 0x7f){ + fputc(buf[i], stderr); + }else{ + fputc('.', stderr); + } + } + } + fprintf(stderr, "\"\n"); + buf += 16; + len -= 16; + } + } +} + +static char *usbErrorText(int usbErrno) +{ + static char buffer[32]; + + switch(usbErrno){ + case USB_ERROR_NONE: return "Success."; + case USB_ERROR_ACCESS: return "Access denied."; + case USB_ERROR_NOTFOUND:return "Device not found."; + case USB_ERROR_BUSY: return "Device is busy."; + case USB_ERROR_IO: return "I/O Error."; + default: + sprintf(buffer, "Unknown error %d.", usbErrno); + return buffer; + } +} + +/* ------------------------------------------------------------------------- */ + +static void avrdoper_open(char *port, long baud, union filedescriptor *fdp) +{ + int rval; + char *vname = "obdev.at"; + char *devname = "AVR-Doper"; + + rval = usbOpenDevice(fdp, USB_VENDOR_ID, vname, USB_PRODUCT_ID, devname, 1); + if(rval != 0){ + fprintf(stderr, "%s: avrdoper_open(): %s\n", progname, usbErrorText(rval)); + exit(1); + } +} + +/* ------------------------------------------------------------------------- */ + +static void avrdoper_close(union filedescriptor *fdp) +{ + usbCloseDevice(fdp); +} + +/* ------------------------------------------------------------------------- */ + +static int chooseDataSize(int len) +{ + int i; + + for(i = 0; i < sizeof(reportDataSizes)/sizeof(reportDataSizes[0]); i++){ + if(reportDataSizes[i] >= len) + return i; + } + return i - 1; +} + +static int avrdoper_send(union filedescriptor *fdp, unsigned char *buf, size_t buflen) +{ + if(verbose > 3) + dumpBlock("Send", buf, buflen); + while(buflen > 0){ + unsigned char buffer[256]; + int rval, lenIndex = chooseDataSize(buflen); + int thisLen = buflen > reportDataSizes[lenIndex] ? + reportDataSizes[lenIndex] : buflen; + buffer[0] = lenIndex + 1; /* report ID */ + buffer[1] = thisLen; + memcpy(buffer + 2, buf, thisLen); + if(verbose > 3) + fprintf(stderr, "Sending %d bytes data chunk\n", thisLen); + rval = usbSetReport(fdp, USB_HID_REPORT_TYPE_FEATURE, (char *)buffer, + reportDataSizes[lenIndex] + 2); + if(rval != 0){ + fprintf(stderr, "%s: avrdoper_send(): %s\n", progname, usbErrorText(rval)); + exit(1); + } + buflen -= thisLen; + buf += thisLen; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static void avrdoperFillBuffer(union filedescriptor *fdp) +{ + int bytesPending = reportDataSizes[1]; /* guess how much data is buffered in device */ + + avrdoperRxPosition = avrdoperRxLength = 0; + while(bytesPending > 0){ + int len, usbErr, lenIndex = chooseDataSize(bytesPending); + unsigned char buffer[128]; + len = sizeof(avrdoperRxBuffer) - avrdoperRxLength; /* bytes remaining */ + if(reportDataSizes[lenIndex] + 2 > len) /* requested data would not fit into buffer */ + break; + len = reportDataSizes[lenIndex] + 2; + usbErr = usbGetReport(fdp, USB_HID_REPORT_TYPE_FEATURE, lenIndex + 1, + (char *)buffer, &len); + if(usbErr != 0){ + fprintf(stderr, "%s: avrdoperFillBuffer(): %s\n", progname, usbErrorText(usbErr)); + exit(1); + } + if(verbose > 3) + fprintf(stderr, "Received %d bytes data chunk of total %d\n", len - 2, buffer[1]); + len -= 2; /* compensate for report ID and length byte */ + bytesPending = buffer[1] - len; /* amount still buffered */ + if(len > buffer[1]) /* cut away padding */ + len = buffer[1]; + if(avrdoperRxLength + len > sizeof(avrdoperRxBuffer)){ + fprintf(stderr, + "%s: avrdoperFillBuffer(): internal error: buffer overflow\n", + progname); + exit(1); + } + memcpy(avrdoperRxBuffer + avrdoperRxLength, buffer + 2, len); + avrdoperRxLength += len; + } +} + +static int avrdoper_recv(union filedescriptor *fdp, unsigned char *buf, size_t buflen) +{ + unsigned char *p = buf; + int remaining = buflen; + + while(remaining > 0){ + int len, available = avrdoperRxLength - avrdoperRxPosition; + if(available <= 0){ /* buffer is empty */ + avrdoperFillBuffer(fdp); + continue; + } + len = remaining < available ? remaining : available; + memcpy(p, avrdoperRxBuffer + avrdoperRxPosition, len); + p += len; + remaining -= len; + avrdoperRxPosition += len; + } + if(verbose > 3) + dumpBlock("Receive", buf, buflen); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static int avrdoper_drain(union filedescriptor *fdp, int display) +{ + do{ + avrdoperFillBuffer(fdp); + }while(avrdoperRxLength > 0); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +struct serial_device avrdoper_serdev = +{ + .open = avrdoper_open, + .close = avrdoper_close, + .send = avrdoper_send, + .recv = avrdoper_recv, + .drain = avrdoper_drain, + .flags = SERDEV_FL_NONE, +}; + +#endif /* defined(HAVE_LIBUSB) || (defined(WIN32NATIVE) && defined(HAVE_LIBHID)) */ diff --git a/serial.h b/serial.h index 1b19f26a..19ce5b4e 100644 --- a/serial.h +++ b/serial.h @@ -53,7 +53,10 @@ struct serial_device }; extern struct serial_device *serdev; -extern struct serial_device serial_serdev, usb_serdev, usb_serdev_frame; +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; #define serial_open (serdev->open) #define serial_setspeed (serdev->setspeed) diff --git a/stk500v2.c b/stk500v2.c index db80d3fa..94ea11f5 100644 --- a/stk500v2.c +++ b/stk500v2.c @@ -960,6 +960,16 @@ static int stk500v2_open(PROGRAMMER * pgm, char * port) pgmtype = PGMTYPE_UNKNOWN; + if(strcasecmp(port, "avrdoper") == 0){ +#if defined(HAVE_LIBUSB) || defined(WIN32NATIVE) + serdev = &avrdoper_serdev; + pgmtype = PGMTYPE_STK500; +#else + fprintf(stderr, "avrdude was compiled without usb support.\n"); + return -1; +#endif + } + /* * If the port name starts with "usb", divert the serial routines * to the USB ones. The serial_open() function for USB overrides