/* * 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, see . */ /* $Id$ */ /* * Serial Interface emulation for USB programmer "AVR-Doper" in HID mode. */ #include "ac_cfg.h" #if defined(HAVE_LIBHIDAPI) #include #include #include #include #include "avrdude.h" #include "libavrdude.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 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 */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ static int usbOpenDevice(union filedescriptor *fdp, int vendor, const char *vendorName, int product, const char *productName, int doReportIDs) { hid_device *dev; dev = hid_open(vendor, product, NULL); if (dev == NULL) { pmsg_ext_error("no device found\n"); return USB_ERROR_NOTFOUND; } fdp->usb.handle = dev; return USB_ERROR_NONE; } /* ------------------------------------------------------------------------- */ static void usbCloseDevice(union filedescriptor *fdp) { hid_device *udev = (hid_device *)fdp->usb.handle; fdp->usb.handle = NULL; if (udev == NULL) return; hid_close(udev); } /* ------------------------------------------------------------------------- */ static int usbSetReport(const union filedescriptor *fdp, int reportType, char *buffer, int len) { hid_device *udev = (hid_device *)fdp->usb.handle; int bytesSent = -1; switch(reportType){ case USB_HID_REPORT_TYPE_INPUT: break; case USB_HID_REPORT_TYPE_OUTPUT: bytesSent = hid_write(udev, (unsigned char*) buffer, len); break; case USB_HID_REPORT_TYPE_FEATURE: bytesSent = hid_send_feature_report(udev, (unsigned char*) buffer, len); break; } if(bytesSent != len){ if(bytesSent < 0) pmsg_error("unable to send message: %ls\n", hid_error(udev)); return USB_ERROR_IO; } return USB_ERROR_NONE; } /* ------------------------------------------------------------------------- */ static int usbGetReport(const union filedescriptor *fdp, int reportType, int reportNumber, char *buffer, int *len) { hid_device *udev = (hid_device *)fdp->usb.handle; int bytesReceived = -1; switch(reportType){ case USB_HID_REPORT_TYPE_INPUT: bytesReceived = hid_read_timeout(udev, (unsigned char*) buffer, *len, 300); break; case USB_HID_REPORT_TYPE_OUTPUT: break; case USB_HID_REPORT_TYPE_FEATURE: bytesReceived = hid_get_feature_report(udev, (unsigned char*) buffer, *len); break; } if(bytesReceived < 0){ pmsg_error("unable to send message: %ls\n", hid_error(udev)); return USB_ERROR_IO; } *len = bytesReceived; return USB_ERROR_NONE; } /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------- */ static void dumpBlock(const char *prefix, const unsigned char *buf, int len) { int i; if(len <= 8){ /* more compact format for short blocks */ msg_info("%s: %d bytes: ", prefix, len); for(i = 0; i < len; i++){ msg_info("%02x ", buf[i]); } msg_info(" \""); for(i = 0; i < len; i++) msg_info("%c", buf[i] >= 0x20 && buf[i] < 0x7f? buf[i]: '.'); msg_info("\"\n"); }else{ msg_info("%s: %d bytes:\n", prefix, len); while(len > 0){ for(i = 0; i < 16; i++){ if(i < len){ msg_info("%02x ", buf[i]); }else{ msg_info(" "); } if(i == 7) msg_info(" "); } msg_info(" \""); for(i = 0; i < 16 && i < len; i++) msg_info("%c", buf[i] >= 0x20 && buf[i] < 0x7f? buf[i]: '.'); msg_info("\"\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 int avrdoper_open(const char *port, union pinfo pinfo, 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){ pmsg_ext_error("%s\n", usbErrorText(rval)); return -1; } return 0; } /* ------------------------------------------------------------------------- */ 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(const union filedescriptor *fdp, const 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); msg_trace("Sending %d bytes data chunk\n", thisLen); rval = usbSetReport(fdp, USB_HID_REPORT_TYPE_FEATURE, (char *)buffer, reportDataSizes[lenIndex] + 2); if(rval != 0){ pmsg_error("%s\n", usbErrorText(rval)); return -1; } buflen -= thisLen; buf += thisLen; } return 0; } /* ------------------------------------------------------------------------- */ static int avrdoperFillBuffer(const 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){ pmsg_error("%s\n", usbErrorText(usbErr)); return -1; } msg_trace("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)){ pmsg_error("buffer overflow\n"); return -1; } memcpy(avrdoperRxBuffer + avrdoperRxLength, buffer + 2, len); avrdoperRxLength += len; } return 0; } static int avrdoper_recv(const 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 */ if (avrdoperFillBuffer(fdp) < 0) return -1; 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(const union filedescriptor *fdp, int display) { do{ if (avrdoperFillBuffer(fdp) < 0) return -1; }while(avrdoperRxLength > 0); return 0; } /* ------------------------------------------------------------------------- */ static int avrdoper_set_dtr_rts(const union filedescriptor *fdp, int is_on) { pmsg_error("AVR-Doper does not support DTR/RTS setting\n"); return -1; } /* ------------------------------------------------------------------------- */ struct serial_device avrdoper_serdev = { .open = avrdoper_open, .close = avrdoper_close, .send = avrdoper_send, .recv = avrdoper_recv, .drain = avrdoper_drain, .set_dtr_rts = avrdoper_set_dtr_rts, .flags = SERDEV_FL_NONE, }; #endif /* defined(HAVE_LIBHIDAPI) */