Submitted by Kirill Levchenko:

patch #7896: DFU FLIPv2 programming support
* pgm_type.c: Add the flip2 programmer type.
* config_gram.y: Allow for the usbid keyword in a device definition.
* avrdude.conf.in: Add usbpid values to those Xmega devices where
applicable.
* avrpart.h: Add usbpid device field.
* dfu.c: (New file.)
* dfu.h: (New file.)
* flip.c: (New file.)
* flip.h: (New file.)
* Makefile.am: Add new files.
* doc/avrdude.texi: Document the changes.
* avrdude.1: (Dito.)



git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1258 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
Joerg Wunsch 2014-01-15 13:34:49 +00:00
parent a323e4b9ec
commit efc87b6d2e
13 changed files with 1623 additions and 3 deletions

View File

@ -0,0 +1,20 @@
2014-01-15 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
Submitted by Kirill Levchenko:
patch #7896: DFU FLIPv2 programming support
* pgm_type.c: Add the flip2 programmer type.
* config_gram.y: Allow for the usbid keyword in a device definition.
* avrdude.conf.in: Add usbpid values to those Xmega devices where
applicable.
* avrpart.h: Add usbpid device field.
* dfu.c: (New file.)
* dfu.h: (New file.)
* flip.c: (New file.)
* flip.h: (New file.)
* Makefile.am: Add new files.
* doc/avrdude.texi: Document the changes.
* avrdude.1: (Dito.)
2014-01-15 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
* ChangeLog-2013: Annual changelog rotation.

View File

@ -113,8 +113,12 @@ libavrdude_a_SOURCES = \
confwin.h \
crc16.c \
crc16.h \
dfu.c \
dfu.h \
fileio.c \
fileio.h \
flip.c \
flip.h \
freebsd_ppi.h \
ft245r.c \
ft245r.h \

3
NEWS
View File

@ -14,12 +14,13 @@ Current:
- ...
* New programmers supported:
- ...
- FLIP v2 (Atmel DFU protocol)
* Bugfixes
- bug #40055: AVRDUDE segfaults when writing eeprom
- bug #40085: Typo fix in fuses report (for 6.1-svn-20130917)
- bug #40817: Elf file support (possibly) not working on 6.0.1 windows build
- patch #7896: DFU FLIPv2 programming support
- patch #XXXX: xxx
* Internals:

View File

@ -1,7 +1,7 @@
.\"
.\" avrdude - A Downloader/Uploader for AVR device programmers
.\" Copyright (C) 2001, 2002, 2003, 2005 - 2013 Joerg Wunsch
.\"
.\" Copyright (C) 2001, 2002, 2003, 2005 - 2014 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
@ -175,6 +175,8 @@ the avrftdi device adds support for many programmers using FTDI's 2232C/D/H
and 4232H parts running in MPSSE mode, which hard-codes (in the chip)
SCK to bit 1, MOSI to bit 2, and MISO to bit 3. Reset is usually bit 4.
.Pp
The Atmel DFU bootloader is supported in version 2 (Xmega devices).
.Pp
Input files can be provided, and output files can be written in
different file formats, such as raw binary files containing the data
to download to the chip, Intel hex format, or Motorola S-record

View File

@ -54,6 +54,7 @@
# stk500_devcode = <num> ; # numeric
# avr910_devcode = <num> ; # numeric
# signature = <num> <num> <num> ; # signature bytes
# usbpid = <num> ; # DFU USB PID
# chip_erase_delay = <num> ; # micro-seconds
# reset = dedicated | io;
# retry_pulse = reset | sck;
@ -1031,6 +1032,13 @@ programmer
connection_type = usb;
;
programmer
id = "flip2";
desc = "FLIPv2 USB DFU protocol (AVR4023)";
type = "flip2";
connection_type = usb;
;
@HAVE_PARPORT_BEGIN@ Inclusion of the following depends on --enable-parport
# Parallel port programmers.
@ -13132,6 +13140,7 @@ part parent ".xmega"
id = "x16a4u";
desc = "ATxmega16A4U";
signature = 0x1e 0x94 0x41;
usbpid = 0x2fe3;
memory "eeprom"
size = 0x400;
@ -13220,6 +13229,7 @@ part parent ".xmega"
id = "x32a4u";
desc = "ATxmega32A4U";
signature = 0x1e 0x95 0x41;
usbpid = 0x2fe4;
memory "eeprom"
size = 0x400;
@ -13308,6 +13318,7 @@ part parent ".xmega"
id = "x64a4u";
desc = "ATxmega64A4U";
signature = 0x1e 0x96 0x46;
usbpid = 0x2fe5;
memory "eeprom"
size = 0x800;
@ -13360,6 +13371,7 @@ part parent "x64a4u"
id = "x64c3";
desc = "ATxmega64C3";
signature = 0x1e 0x96 0x49;
usbpid = 0x2fd6;
;
#------------------------------------------------------------
@ -13406,6 +13418,7 @@ part parent "x64a1"
id = "x64a1u";
desc = "ATxmega64A1U";
signature = 0x1e 0x96 0x4e;
usbpid = 0x2fe8;
;
#------------------------------------------------------------
@ -13426,6 +13439,7 @@ part parent "x64a1"
id = "x64a3u";
desc = "ATxmega64A3U";
signature = 0x1e 0x96 0x42;
usbpid = 0x2fe5;
;
#------------------------------------------------------------
@ -13446,6 +13460,7 @@ part parent "x64a1"
id = "x64b1";
desc = "ATxmega64B1";
signature = 0x1e 0x96 0x52;
usbpid = 0x2fe1;
;
#------------------------------------------------------------
@ -13456,6 +13471,7 @@ part parent "x64a1"
id = "x64b3";
desc = "ATxmega64B3";
signature = 0x1e 0x96 0x51;
usbpid = 0x2fdf;
;
#------------------------------------------------------------
@ -13466,6 +13482,7 @@ part parent ".xmega"
id = "x128c3";
desc = "ATxmega128C3";
signature = 0x1e 0x97 0x52;
usbpid = 0x2fd7;
memory "eeprom"
size = 0x800;
@ -13564,6 +13581,7 @@ part parent "x128a1"
id = "x128a1u";
desc = "ATxmega128A1U";
signature = 0x1e 0x97 0x4c;
usbpid = 0x2fed;
;
#------------------------------------------------------------
@ -13584,6 +13602,7 @@ part parent "x128a1"
id = "x128a3u";
desc = "ATxmega128A3U";
signature = 0x1e 0x97 0x42;
usbpid = 0x2fe6;
;
#------------------------------------------------------------
@ -13652,6 +13671,7 @@ part parent ".xmega"
id = "x128a4u";
desc = "ATxmega128A4U";
signature = 0x1e 0x97 0x46;
usbpid = 0x2fde;
memory "eeprom"
size = 0x800;
@ -13704,6 +13724,7 @@ part parent ".xmega"
id = "x128b1";
desc = "ATxmega128B1";
signature = 0x1e 0x97 0x4d;
usbpid = 0x2fea;
has_jtag = yes;
memory "eeprom"
@ -13762,6 +13783,7 @@ part parent "x128b1"
id = "x128b3";
desc = "ATxmega128B3";
signature = 0x1e 0x97 0x4b;
usbpid = 0x2fe0;
;
#------------------------------------------------------------
@ -13772,6 +13794,7 @@ part parent ".xmega"
id = "x192c3";
desc = "ATxmega192C3";
signature = 0x1e 0x97 0x51;
# usbpid = 0x2f??;
memory "eeprom"
size = 0x800;
@ -13860,6 +13883,7 @@ part parent "x192a1"
id = "x192a3u";
desc = "ATxmega192A3U";
signature = 0x1e 0x97 0x44;
usbpid = 0x2fe7;
;
#------------------------------------------------------------
@ -13870,6 +13894,7 @@ part parent ".xmega"
id = "x256c3";
desc = "ATxmega256C3";
signature = 0x1e 0x98 0x46;
usbpid = 0x2fda;
memory "eeprom"
size = 0x1000;
@ -13958,6 +13983,7 @@ part parent "x256a1"
id = "x256a3u";
desc = "ATxmega256A3U";
signature = 0x1e 0x98 0x42;
usbpid = 0x2fec;
;
#------------------------------------------------------------
@ -13978,6 +14004,7 @@ part parent "x256a1"
id = "x256a3bu";
desc = "ATxmega256A3BU";
signature = 0x1e 0x98 0x43;
usbpid = 0x2fe2;
;
#------------------------------------------------------------
@ -13988,6 +14015,7 @@ part parent ".xmega"
id = "x384c3";
desc = "ATxmega384C3";
signature = 0x1e 0x98 0x45;
usbpid = 0x2fdb;
memory "eeprom"
size = 0x1000;

View File

@ -111,6 +111,7 @@ typedef struct avrpart {
unsigned char pagel; /* for parallel programming */
unsigned char bs2; /* for parallel programming */
unsigned char signature[3]; /* expected value of signature bytes */
unsigned short usbpid; /* USB DFU product ID (0 = none) */
int reset_disposition; /* see RESET_ enums */
int retry_pulse; /* retry program enable by pulsing
this pin (PIN_AVR_*) */

View File

@ -672,6 +672,13 @@ part_parm :
}
} |
K_USBPID TKN_EQUAL TKN_NUMBER {
{
current_part->usbpid = $3->value.number;
free_token($3);
}
} |
K_PP_CONTROLSTACK TKN_EQUAL num_list {
{
TOKEN * t;

515
dfu.c Normal file
View File

@ -0,0 +1,515 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2012 Kirill Levchenko
*
* 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$ */
#include "ac_cfg.h"
#include "dfu.h"
#include "avrdude.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include "usbdevs.h" /* for USB_VENDOR_ATMEL */
/* If we don't have LibUSB, define dummy functions that report an error. */
#ifndef HAVE_LIBUSB
int dfu_open(struct dfu_dev *dfu, char *port_name) {
fprintf(stderr, "%s: Error: No USB support in this compile of avrdude\n",
progname);
return -1;
}
int dfu_init(struct dfu_dev *dfu, unsigned short usb_pid) {
return -1;
}
void dfu_close(struct dfu_dev *dfu) {
/* nothing */
}
int dfu_getstatus(struct dfu_dev *dfu, struct dfu_status *status)
{
return -1;
}
int dfu_clrstatus(struct dfu_dev *dfu) {
return -1;
}
int dfu_download(struct dfu_dev *dfu, void * ptr, int size) {
return -1;
}
int dfu_upload(struct dfu_dev *dfu, void * ptr, int size) {
return -1;
}
#else
/* If we DO have LibUSB, we can define the real functions. */
#include <usb.h>
/* DFU data structures and constants.
*/
#define DFU_TIMEOUT 200 /* ms */
#define DFU_DNLOAD 1
#define DFU_UPLOAD 2
#define DFU_GETSTATUS 3
#define DFU_CLRSTATUS 4
/* Block counter global variable. Incremented each time a DFU_DNLOAD command
* is sent to the device.
*/
static u_int16_t wIndex = 0;
/* INTERNAL FUNCTION PROTOTYPES
*/
static char * get_usb_string(usb_dev_handle * dev_handle, int index);
/* EXPORTED FUNCTION DEFINITIONS
*/
struct dfu_dev * dfu_open(char *port_spec)
{
struct dfu_dev *dfu;
char *bus_name = NULL;
char *dev_name = NULL;
/* The following USB device spec parsing code was copied from usbtiny.c. The
* expected format is "usb:BUS:DEV" where BUS and DEV are the bus and device
* names. We stash these away in the dfu_dev structure for the dfu_init()
* function, where we actually open the device.
*/
if (strncmp(port_spec, "usb", 3) != 0) {
fprintf(stderr, "%s: Error: " \
"Invalid port specification \"%s\" for USB device\n",
progname, port_spec);
return NULL;
}
if(':' == port_spec[3]) {
bus_name = strdup(port_spec + 3 + 1);
if (bus_name == NULL) {
perror(progname);
exit(1);
}
dev_name = strchr(bus_name, ':');
if(NULL != dev_name)
*dev_name++ = '\0';
}
/* Allocate the dfu_dev structure and save the bus_name and dev_name
* strings for use in dfu_initialize().
*/
dfu = calloc(1, sizeof(struct dfu_dev));
if (dfu == NULL)
{
perror(progname);
exit(1);
}
dfu->bus_name = bus_name;
dfu->dev_name = dev_name;
/* LibUSB initialization. */
usb_init();
usb_find_busses();
usb_find_devices();
return dfu;
}
int dfu_init(struct dfu_dev *dfu, unsigned short vid, unsigned short pid)
{
struct usb_device *found = NULL;
struct usb_device *dev;
struct usb_bus *bus;
/* At last, we reach out through the USB bus to the part. There are three
* ways to specify the part: by USB address, by USB vendor and product id,
* and by part name. To specify the part by USB address, the user specifies
* a port parameter in the form "usb:BUS:DEV" (see dfu_open()). To specify
* the part by vendor and product, the user must specify a usbvid and usbpid
* in the configuration file. Finally, if the user specifies the part only,
* we use the default vendor and product id.
*/
if (pid == 0 && dfu->dev_name == NULL) {
fprintf(stderr, "%s: Error: No DFU support for part; " \
"specify PID in config or USB address (via -P) to override.\n",
progname);
return -1;
}
/* Scan through all the devices for the part. The matching rules are:
*
* 1. If the user specified a USB bus name, it must match.
* 2. If the user specified a USB device name, it must match.
* 3. If the user didn't specify a USB device name and specified a vendor
* id, the vendor id must match.
* 4. If the user didn't specify a USB device name and specified a product
* id, the product id must match.
*/
for (bus = usb_busses; !found && bus != NULL; bus = bus->next) {
for (dev = bus->devices; !found && dev != NULL; dev = dev->next) {
if (dfu->bus_name != NULL && strcmp(bus->dirname, dfu->bus_name))
continue;
if (dfu->dev_name != NULL) {
if (strcmp(dev->filename, dfu->dev_name))
continue;
} else if (vid != dev->descriptor.idVendor)
continue;
else if (pid != 0 && pid != dev->descriptor.idProduct)
continue;
found = dev;
}
}
if (found == NULL) {
/* We could try to be more informative here. For example, we could report
* why the match failed, and if we came across another DFU-capable part.
*/
fprintf(stderr, "%s: Error: No matching USB device found\n", progname);
return -1;
}
if(verbose)
fprintf(stderr,
"%s: Found VID=0x%04x PID=0x%04x at %s:%s\n",
progname, found->descriptor.idVendor, found->descriptor.idProduct,
found->bus->dirname, found->filename);
dfu->dev_handle = usb_open(found);
if (dfu->dev_handle == NULL) {
fprintf(stderr, "%s: Error: USB device at %s:%s: %s\n",
progname, found->bus->dirname, found->filename, usb_strerror());
return -1;
}
/* Save device, configuration, interface and endpoint descriptors. */
memcpy(&dfu->dev_desc, &found->descriptor, sizeof(dfu->dev_desc));
memcpy(&dfu->conf_desc, found->config, sizeof(dfu->conf_desc));
dfu->conf_desc.interface = NULL;
memcpy(&dfu->intf_desc, found->config->interface->altsetting,
sizeof(dfu->intf_desc));
dfu->intf_desc.endpoint = &dfu->endp_desc;
if (found->config->interface->altsetting->endpoint != 0)
memcpy(&dfu->endp_desc, found->config->interface->altsetting->endpoint,
sizeof(dfu->endp_desc));
/* Check if descriptor values are what we expect. */
if (dfu->dev_desc.idVendor != vid)
fprintf( stderr, "%s: Warning: USB idVendor = 0x%04X (expected 0x%04X)\n",
progname, dfu->dev_desc.idVendor, vid);
if (pid != 0 && dfu->dev_desc.idProduct != pid)
fprintf( stderr, "%s: Warning: USB idProduct = 0x%04X (expected 0x%04X)\n",
progname, dfu->dev_desc.idProduct, pid);
if (dfu->dev_desc.bNumConfigurations != 1)
fprintf( stderr, "%s: Warning: USB bNumConfigurations = %d (expected 1)\n",
progname, (int) dfu->dev_desc.bNumConfigurations);
if (dfu->conf_desc.bNumInterfaces != 1)
fprintf( stderr, "%s: Warning: USB bNumInterfaces = %d (expected 1)\n",
progname, (int) dfu->conf_desc.bNumInterfaces);
if (dfu->dev_desc.bDeviceClass != 0)
fprintf( stderr, "%s: Warning: USB bDeviceClass = %d (expected 0)\n",
progname, (int) dfu->dev_desc.bDeviceClass);
if (dfu->dev_desc.bDeviceSubClass != 0)
fprintf( stderr, "%s: Warning: USB bDeviceSubClass = %d (expected 0)\n",
progname, (int) dfu->dev_desc.bDeviceSubClass);
if (dfu->dev_desc.bDeviceProtocol != 0)
fprintf( stderr, "%s: Warning: USB bDeviceProtocol = %d (expected 0)\n",
progname, (int) dfu->dev_desc.bDeviceProtocol);
if (dfu->intf_desc.bInterfaceClass != 0xFF)
fprintf( stderr, "%s: Warning: USB bInterfaceClass = %d (expected 255)\n",
progname, (int) dfu->intf_desc.bInterfaceClass);
if (dfu->intf_desc.bInterfaceSubClass != 0)
fprintf( stderr, "%s: Warning: USB bInterfaceSubClass = %d (expected 0)\n",
progname, (int) dfu->intf_desc.bInterfaceSubClass);
if (dfu->intf_desc.bInterfaceProtocol != 0)
fprintf( stderr, "%s: Warning: USB bInterfaceSubClass = %d (expected 0)\n",
progname, (int) dfu->intf_desc.bInterfaceProtocol);
/* Get strings. */
dfu->manf_str = get_usb_string(dfu->dev_handle,
dfu->dev_desc.iManufacturer);
dfu->prod_str = get_usb_string(dfu->dev_handle,
dfu->dev_desc.iProduct);
dfu->serno_str = get_usb_string(dfu->dev_handle,
dfu->dev_desc.iSerialNumber);
return 0;
}
void dfu_close(struct dfu_dev *dfu)
{
if (dfu->dev_handle != NULL)
usb_close(dfu->dev_handle);
if (dfu->bus_name != NULL)
free(dfu->bus_name);
if (dfu->manf_str != NULL)
free(dfu->manf_str);
if (dfu->prod_str != NULL)
free(dfu->prod_str);
if (dfu->serno_str != NULL)
free(dfu->serno_str);
}
int dfu_getstatus(struct dfu_dev *dfu, struct dfu_status *status)
{
int result;
result = usb_control_msg(dfu->dev_handle,
0x80 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, DFU_GETSTATUS, 0, 0,
(char*) status, sizeof(struct dfu_status), DFU_TIMEOUT);
if (result < 0) {
fprintf(stderr, "%s: Error: Failed to get DFU status: %s\n",
progname, usb_strerror());
return -1;
}
if (result < sizeof(struct dfu_status)) {
fprintf(stderr, "%s: Error: Failed to get DFU status: %s\n",
progname, "short read");
return -1;
}
if (result > sizeof(struct dfu_status)) {
fprintf(stderr, "%s: Error: Oversize read (should not happen); " \
"exiting\n", progname);
exit(1);
}
return 0;
}
int dfu_clrstatus(struct dfu_dev *dfu)
{
int result;
result = usb_control_msg(dfu->dev_handle,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, DFU_CLRSTATUS, 0, 0,
NULL, 0, DFU_TIMEOUT);
if (result < 0) {
fprintf(stderr, "%s: Error: Failed to clear DFU status: %s\n",
progname, usb_strerror());
return -1;
}
return 0;
}
int dfu_dnload(struct dfu_dev *dfu, void *ptr, int size)
{
int result;
result = usb_control_msg(dfu->dev_handle,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, DFU_DNLOAD, wIndex++, 0,
ptr, size, DFU_TIMEOUT);
if (result < 0) {
fprintf(stderr, "%s: Error: DFU_DNLOAD failed: %s\n",
progname, usb_strerror());
return -1;
}
if (result < size) {
fprintf(stderr, "%s: Error: DFU_DNLOAD failed: %s\n",
progname, "short write");
return -1;
}
if (result > size) {
fprintf(stderr, "%s: Error: Oversize write (should not happen); " \
"exiting\n", progname);
exit(1);
}
return 0;
}
int dfu_upload(struct dfu_dev *dfu, void *ptr, int size)
{
int result;
result = usb_control_msg(dfu->dev_handle,
0x80 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, DFU_UPLOAD, wIndex++, 0,
ptr, size, DFU_TIMEOUT);
if (result < 0) {
fprintf(stderr, "%s: Error: DFU_UPLOAD failed: %s\n",
progname, usb_strerror());
return -1;
}
if (result < size) {
fprintf(stderr, "%s: Error: DFU_UPLOAD failed: %s\n",
progname, "short read");
return -1;
}
if (result > size) {
fprintf(stderr, "%s: Error: Oversize read (should not happen); " \
"exiting\n", progname);
exit(1);
}
return 0;
}
void dfu_show_info(struct dfu_dev *dfu)
{
if (dfu->manf_str != NULL)
fprintf(stderr, " USB Vendor : %s (0x%04hX)\n",
dfu->manf_str, (unsigned short) dfu->dev_desc.idVendor);
else
fprintf(stderr, " USB Vendor : 0x%04hX\n",
(unsigned short) dfu->dev_desc.idVendor);
if (dfu->prod_str != NULL)
fprintf(stderr, " USB Product : %s (0x%04hX)\n",
dfu->prod_str, (unsigned short) dfu->dev_desc.idProduct);
else
fprintf(stderr, " USB Product : 0x%04hX\n",
(unsigned short) dfu->dev_desc.idProduct);
fprintf(stderr, " USB Release : %hu.%hu.%hu\n",
((unsigned short) dfu->dev_desc.bcdDevice >> 8) & 0xFF,
((unsigned short) dfu->dev_desc.bcdDevice >> 4) & 0xF,
((unsigned short) dfu->dev_desc.bcdDevice >> 0) & 0xF);
if (dfu->serno_str != NULL)
fprintf(stderr, " USB Serial No : %s\n", dfu->serno_str);
}
/* INTERNAL FUNCTION DEFINITIONS
*/
char * get_usb_string(usb_dev_handle * dev_handle, int index) {
char buffer[256];
char * str;
int result;
if (index == 0)
return NULL;
result = usb_get_string_simple(dev_handle, index, buffer, sizeof(buffer)-1);
if (result < 0) {
fprintf( stderr, "%s: Warning: Failed to read USB device string %d: %s\n",
progname, index, usb_strerror());
return NULL;
}
str = malloc(result+1);
if (str == NULL) {
perror(progname);
exit(1);
}
memcpy(str, buffer, result);
str[result] = '\0';
return str;
}
#endif /* defined(HAVE_LIBUSB) */
/* EXPORTED FUNCTIONS THAT DO NO REQUIRE LIBUSB
*/
const char * dfu_status_str(int bStatus)
{
switch (bStatus) {
case DFU_STATUS_OK: return "OK";
case DFU_STATUS_ERR_TARGET: return "ERR_TARGET";
case DFU_STATUS_ERR_FILE: return "ERR_FILE";
case DFU_STATUS_ERR_WRITE: return "ERR_WRITE";
case DFU_STATUS_ERR_ERASE: return "ERR_ERASE";
case DFU_STATUS_ERR_CHECK_ERASED: return "ERR_CHECK_ERASED";
case DFU_STATUS_ERR_PROG: return "ERR_PROG";
case DFU_STATUS_ERR_VERIFY: return "ERR_VERIFY";
case DFU_STATUS_ERR_ADDRESS: return "ERR_ADDRESS";
case DFU_STATUS_ERR_NOTDONE: return "ERR_NOTDONE";
case DFU_STATUS_ERR_FIRMWARE: return "ERR_FIRMWARE";
case DFU_STATUS_ERR_VENDOR: return "ERR_VENDOR";
case DFU_STATUS_ERR_USBR: return "ERR_USBR";
case DFU_STATUS_ERR_POR: return "ERR_POR";
case DFU_STATUS_ERR_UNKNOWN: return "ERR_UNKNOWN";
case DFU_STATUS_ERR_STALLEDPKT: return "ERR_STALLEDPKT";
default: return "Unknown";
}
}
const char * dfu_state_str(int bState)
{
switch (bState) {
case DFU_STATE_APP_IDLE: return "APP_IDLE";
case DFU_STATE_APP_DETACH: return "APP_DETACH";
case DFU_STATE_DFU_IDLE: return "DFU_IDLE";
case DFU_STATE_DFU_DLOAD_SYNC: return "DFU_DLOAD_SYNC";
case DFU_STATE_DFU_DNBUSY: return "DFU_DNBUSY";
case DFU_STATE_DFU_DNLOAD_IDLE: return "DFU_DNLOAD_IDLE";
case DFU_STATE_DFU_MANIFEST_SYNC: return "DFU_MANIFEST_SYNC";
case DFU_STATE_DFU_MANIFEST: return "DFU_MANIFEST";
case DFU_STATE_DFU_MANIFEST_WAIT_RESET: return "DFU_MANIFEST_WAIT_RESET";
case DFU_STATE_DFU_UPLOAD_IDLE: return "DFU_UPLOAD_IDLE";
case DFU_STATE_DFU_ERROR: return "DFU_ERROR";
default: return "Unknown";
}
}

130
dfu.h Normal file
View File

@ -0,0 +1,130 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2012 Kirill Levchenko
*
* 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$ */
#ifndef dfu_h
#define dfu_h
#include "ac_cfg.h"
#ifdef HAVE_LIBUSB
#include <usb.h>
#endif
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif
/* If we have LIBUSB, define the dfu_dev struct normally. Otherwise, declare
* it as an empty struct so that code compiles, but we generate an error at
* run time.
*/
#ifdef HAVE_LIBUSB
struct dfu_dev
{
char *bus_name, *dev_name;
usb_dev_handle *dev_handle;
struct usb_device_descriptor dev_desc;
struct usb_config_descriptor conf_desc;
struct usb_interface_descriptor intf_desc;
struct usb_endpoint_descriptor endp_desc;
char *manf_str, *prod_str, *serno_str;
};
#else
struct dfu_dev {
// empty
};
#endif
/* We assume unsigned char is 1 byte. */
#if UCHAR_MAX != 255
#error UCHAR_MAX != 255
#endif
struct dfu_status {
unsigned char bStatus;
unsigned char bwPollTimeout[3];
unsigned char bState;
unsigned char iString;
};
// Values of bStatus field.
#define DFU_STATUS_OK 0x0
#define DFU_STATUS_ERR_TARGET 0x1
#define DFU_STATUS_ERR_FILE 0x2
#define DFU_STATUS_ERR_WRITE 0x3
#define DFU_STATUS_ERR_ERASE 0x4
#define DFU_STATUS_ERR_CHECK_ERASED 0x5
#define DFU_STATUS_ERR_PROG 0x6
#define DFU_STATUS_ERR_VERIFY 0x7
#define DFU_STATUS_ERR_ADDRESS 0x8
#define DFU_STATUS_ERR_NOTDONE 0x9
#define DFU_STATUS_ERR_FIRMWARE 0xA
#define DFU_STATUS_ERR_VENDOR 0xB
#define DFU_STATUS_ERR_USBR 0xC
#define DFU_STATUS_ERR_POR 0xD
#define DFU_STATUS_ERR_UNKNOWN 0xE
#define DFU_STATUS_ERR_STALLEDPKT 0xF
// Values of bState field.
#define DFU_STATE_APP_IDLE 0
#define DFU_STATE_APP_DETACH 1
#define DFU_STATE_DFU_IDLE 2
#define DFU_STATE_DFU_DLOAD_SYNC 3
#define DFU_STATE_DFU_DNBUSY 4
#define DFU_STATE_DFU_DNLOAD_IDLE 5
#define DFU_STATE_DFU_MANIFEST_SYNC 6
#define DFU_STATE_DFU_MANIFEST 7
#define DFU_STATE_DFU_MANIFEST_WAIT_RESET 8
#define DFU_STATE_DFU_UPLOAD_IDLE 9
#define DFU_STATE_DFU_ERROR 10
// FUNCTIONS
extern struct dfu_dev * dfu_open(char *port_spec);
extern int dfu_init(struct dfu_dev *dfu,
unsigned short vid, unsigned short pid);
extern void dfu_close(struct dfu_dev *dfu);
extern int dfu_getstatus(struct dfu_dev *dfu, struct dfu_status *status);
extern int dfu_clrstatus(struct dfu_dev *dfu);
extern int dfu_dnload(struct dfu_dev *dfu, void *ptr, int size);
extern int dfu_upload(struct dfu_dev *dfu, void *ptr, int size);
extern void dfu_show_info(struct dfu_dev *dfu);
extern const char * dfu_status_str(int bStatus);
extern const char * dfu_state_str(int bState);
#ifdef __cplusplus
}
#endif
#endif /* dfu_h */

View File

@ -244,6 +244,8 @@ has been compiled with libusb support.
They both feature simple firmware-only USB implementations, running on
an ATmega8 (or ATmega88), or ATtiny2313, respectively.
The Atmel DFU bootloader is supported in version 2 (Xmega devices).
@menu
* History::
@ -1428,6 +1430,7 @@ part
stk500_devcode = <num> ; # numeric
avr910_devcode = <num> ; # numeric
signature = <num> <num> <num> ; # signature bytes
usbpid = <num> ; # DFU USB PID
reset = dedicated | io;
retry_pulse = reset | sck;
pgm_enable = <instruction format> ;

869
flip.c Normal file
View File

@ -0,0 +1,869 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2012 Kirill Levchenko
*
* 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$ */
#include "ac_cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#if HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include "flip.h"
#include "dfu.h"
#include "avrdude.h"
#include "avr.h"
#include "pgm.h"
#include "usbdevs.h" /* for USB_VENDOR_ATMEL */
/* There are three versions of the FLIP protocol:
*
* Version 0: C51 parts
* Version 1: megaAVR parts ("USB DFU Bootloader Datasheet" [doc7618])
* Version 2: XMEGA parts (AVR4023 [doc8457])
*
* We currently only support Version 2, as documented in AVR4023.
*
* Additional references:
* flip_protocol.h from the Atmel Software Framework.
* udi_dfu_atmel.c from XMEGA bootloaders archive.
*/
/* EXPORTED CONSTANT STRINGS */
const char flip2_desc[] = "FLIPv2 USB DFU protocol (AVR4023)";
/* PRIVATE DATA STRUCTURES */
struct flip2
{
struct dfu_dev *dfu;
unsigned char part_sig[3];
unsigned char part_rev;
unsigned char boot_ver;
};
#define FLIP2(pgm) ((struct flip2 *)(pgm->cookie))
/* The FLIP2 protocol assigns specific meaning to certain combinations of
* status and state bytes in the DFU_GETSTATUS response. These constants en-
* code these combinations as a 16-bit value: the high order byte is the
* status and the low order byte is the state of the status-state pairing.
*/
#define FLIP2_STATUS_OK 0x0000
#define FLIP2_STATUS_STALL 0x0F0A
#define FLIP2_STATUS_MEM_UKNOWN 0x030A
#define FLIP2_STATUS_MEM_PROTECTED 0x0300
#define FLIP2_STATUS_OUTOFRANGE 0x080A
#define FLIP2_STATUS_BLANK_FAIL 0x0500
#define FLIP2_STATUS_ERASE_ONGOING 0x0904
/* FLIP2 data structures and constants. */
struct flip2_cmd {
unsigned char group_id;
unsigned char cmd_id;
unsigned char args[4];
};
#define FLIP2_CMD_GROUP_DOWNLOAD 0x01
#define FLIP2_CMD_GROUP_UPLOAD 0x03
#define FLIP2_CMD_GROUP_EXEC 0x04
#define FLIP2_CMD_GROUP_SELECT 0x06
#define FLIP2_CMD_PROG_START 0x00
#define FLIP2_CMD_READ_MEMORY 0x00
#define FLIP2_CMD_SELECT_MEMORY 0x03
#define FLIP2_CMD_CHIP_ERASE 0x00
#define FLIP2_CMD_START_APP 0x03
#define FLIP2_SELECT_MEMORY_UNIT 0x00
#define FLIP2_SELECT_MEMORY_PAGE 0x01
enum flip2_mem_unit {
FLIP2_MEM_UNIT_UNKNOWN = -1,
FLIP2_MEM_UNIT_FLASH = 0x00,
FLIP2_MEM_UNIT_EEPROM = 0x01,
FLIP2_MEM_UNIT_SECURITY = 0x02,
FLIP2_MEM_UNIT_CONFIGURATION = 0x03,
FLIP2_MEM_UNIT_BOOTLOADER = 0x04,
FLIP2_MEM_UNIT_SIGNATURE = 0x05,
FLIP2_MEM_UNIT_USER = 0x06,
FLIP2_MEM_UNIT_INT_RAM = 0x07,
FLIP2_MEM_UNIT_EXT_MEM_CS0 = 0x08,
FLIP2_MEM_UNIT_EXT_MEM_CS1 = 0x09,
FLIP2_MEM_UNIT_EXT_MEM_CS2 = 0x0A,
FLIP2_MEM_UNIT_EXT_MEM_CS3 = 0x0B,
FLIP2_MEM_UNIT_EXT_MEM_CS4 = 0x0C,
FLIP2_MEM_UNIT_EXT_MEM_CS5 = 0x0D,
FLIP2_MEM_UNIT_EXT_MEM_CS6 = 0x0E,
FLIP2_MEM_UNIT_EXT_MEM_CS7 = 0x0F,
FLIP2_MEM_UNIT_EXT_MEM_DF = 0x10
};
/* EXPORTED PROGRAMMER FUNCTION PROTOTYPES */
static int flip2_open(PROGRAMMER *pgm, char *port_spec);
static int flip2_initialize(PROGRAMMER* pgm, AVRPART *part);
static void flip2_close(PROGRAMMER* pgm);
static void flip2_enable(PROGRAMMER* pgm);
static void flip2_disable(PROGRAMMER* pgm);
static void flip2_display(PROGRAMMER* pgm, const char *prefix);
static int flip2_program_enable(PROGRAMMER* pgm, AVRPART *part);
static int flip2_chip_erase(PROGRAMMER* pgm, AVRPART *part);
static int flip2_read_byte(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned long addr, unsigned char *value);
static int flip2_write_byte(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned long addr, unsigned char value);
static int flip2_page_erase(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int base_addr);
static int flip2_paged_load(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int page_size, unsigned int addr, unsigned int n_bytes);
static int flip2_paged_write(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int page_size, unsigned int addr, unsigned int n_bytes);
static int flip2_read_sig_bytes(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem);
static void flip2_setup(PROGRAMMER * pgm);
static void flip2_teardown(PROGRAMMER * pgm);
/* INTERNAL PROGRAMMER FUNCTION PROTOTYPES */
static void flip2_show_info(struct flip2 *flip2);
static int flip2_read_memory(struct dfu_dev *dfu,
enum flip2_mem_unit mem_unit, uint32_t addr, void *ptr, int size);
static int flip2_write_memory(struct dfu_dev *dfu,
enum flip2_mem_unit mem_unit, uint32_t addr, const void *ptr, int size);
static int flip2_set_mem_unit(struct dfu_dev *dfu,
enum flip2_mem_unit mem_unit);
static int flip2_set_mem_page(struct dfu_dev *dfu, unsigned short page_addr);
static int flip2_read_max1k(struct dfu_dev *dfu,
unsigned short offset, void *ptr, unsigned short size);
static int flip2_write_max1k(struct dfu_dev *dfu,
unsigned short offset, const void *ptr, unsigned short size);
static const char * flip2_status_str(const struct dfu_status *status);
static const char * flip2_mem_unit_str(enum flip2_mem_unit mem_unit);
static enum flip2_mem_unit flip2_mem_unit(const char *name);
/* THE INITPGM FUNCTION DEFINITIONS */
void flip2_initpgm(PROGRAMMER *pgm)
{
strcpy(pgm->type, "flip2");
/* Mandatory Functions */
pgm->initialize = flip2_initialize;
pgm->enable = flip2_enable;
pgm->disable = flip2_disable;
pgm->display = flip2_display;
pgm->program_enable = flip2_program_enable;
pgm->chip_erase = flip2_chip_erase;
pgm->open = flip2_open;
pgm->close = flip2_close;
pgm->page_erase = flip2_page_erase;
pgm->paged_load = flip2_paged_load;
pgm->paged_write = flip2_paged_write;
pgm->read_byte = flip2_read_byte;
pgm->write_byte = flip2_write_byte;
pgm->read_sig_bytes = flip2_read_sig_bytes;
pgm->setup = flip2_setup;
pgm->teardown = flip2_teardown;
}
/* EXPORTED PROGRAMMER FUNCTION DEFINITIONS */
int flip2_open(PROGRAMMER *pgm, char *port_spec)
{
FLIP2(pgm)->dfu = dfu_open(port_spec);
return (FLIP2(pgm)->dfu != NULL) ? 0 : -1;
}
int flip2_initialize(PROGRAMMER* pgm, AVRPART *part)
{
unsigned short vid, pid;
int result;
/* A note about return values. Negative return values from this function are
* interpreted as failure by main(), from where this function is called.
* However such failures are interpreted as a device signature check failure
* and the user is adviced to use the -F option to override this check. In
* our case, this is misleading, so we defer reporting an error until another
* function is called. Thus, we always return 0 (success) from initialize().
* I don't like this, but I don't want to mess with main().
*/
/* The dfu_init() function will try to find the target part either based on
* a USB address provided by the user with the -P option or by matching the
* VID and PID of the device. The VID may be specified in the programmer
* definition; if not specified, it defaults to USB_VENDOR_ATMEL (defined
* in usbdevs.h). The PID may be specified either in the programmer
* definition or the part definition; the programmer definition takes
* priority. The default PID value is 0, which causes dfu_init() to ignore
* the PID when matching a target device.
*/
vid = (pgm->usbvid != 0) ? pgm->usbvid : USB_VENDOR_ATMEL;
pid = (pgm->usbpid != 0) ? pgm->usbpid : part->usbpid;
result = dfu_init(FLIP2(pgm)->dfu, vid, pid);
if (result != 0)
goto flip2_initialize_fail;
result = flip2_read_memory(FLIP2(pgm)->dfu,
FLIP2_MEM_UNIT_SIGNATURE, 0, FLIP2(pgm)->part_sig, 4);
if (result != 0)
goto flip2_initialize_fail;
result = flip2_read_memory(FLIP2(pgm)->dfu,
FLIP2_MEM_UNIT_BOOTLOADER, 0, &FLIP2(pgm)->boot_ver, 1);
if (result != 0)
goto flip2_initialize_fail;
if (verbose)
flip2_show_info(FLIP2(pgm));
return 0;
flip2_initialize_fail:
dfu_close(FLIP2(pgm)->dfu);
FLIP2(pgm)->dfu = NULL;
return 0;
}
void flip2_close(PROGRAMMER* pgm)
{
if (FLIP2(pgm)->dfu != NULL) {
dfu_close(FLIP2(pgm)->dfu);
FLIP2(pgm)->dfu = NULL;
}
}
void flip2_enable(PROGRAMMER* pgm)
{
/* Nothing to do. */
}
void flip2_disable(PROGRAMMER* pgm)
{
/* Nothing to do. */
}
void flip2_display(PROGRAMMER* pgm, const char *prefix)
{
/* Nothing to do. */
}
int flip2_program_enable(PROGRAMMER* pgm, AVRPART *part)
{
/* I couldn't find anything that uses this function, although it is marked
* as "mandatory" in pgm.c. In case anyone does use it, we'll report an
* error if we failed to initialize.
*/
return (FLIP2(pgm)->dfu != NULL) ? 0 : -1;
}
int flip2_chip_erase(PROGRAMMER* pgm, AVRPART *part)
{
struct dfu_status status;
int cmd_result = 0;
int aux_result;
struct flip2_cmd cmd = {
FLIP2_CMD_GROUP_EXEC, FLIP2_CMD_CHIP_ERASE, { 0xFF, 0, 0, 0 }
};
for (;;) {
cmd_result = dfu_dnload(FLIP2(pgm)->dfu, &cmd, sizeof(cmd));
aux_result = dfu_getstatus(FLIP2(pgm)->dfu, &status);
if (aux_result != 0)
return aux_result;
if (status.bStatus != DFU_STATUS_OK) {
if (status.bStatus == ((FLIP2_STATUS_ERASE_ONGOING >> 8) & 0xFF) &&
status.bState == ((FLIP2_STATUS_ERASE_ONGOING >> 0) & 0xFF))
{
continue;
} else
fprintf(stderr, "%s: Error: DFU status %s\n", progname,
flip2_status_str(&status));
dfu_clrstatus(FLIP2(pgm)->dfu);
} else
break;
}
return cmd_result;
}
int flip2_read_byte(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned long addr, unsigned char *value)
{
enum flip2_mem_unit mem_unit;
if (FLIP2(pgm)->dfu == NULL)
return -1;
mem_unit = flip2_mem_unit(mem->desc);
if (mem_unit == FLIP2_MEM_UNIT_UNKNOWN) {
fprintf(stderr, "%s: Error: " \
"\"%s\" memory not accessible using FLIP",
progname, mem->desc);
if (strcmp(mem->desc, "flash") == 0)
fprintf(stderr, " (did you mean \"application\"?)");
fprintf(stderr, "\n");
return -1;
}
return flip2_read_memory(FLIP2(pgm)->dfu, mem_unit, addr, value, 1);
}
int flip2_write_byte(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned long addr, unsigned char value)
{
enum flip2_mem_unit mem_unit;
if (FLIP2(pgm)->dfu == NULL)
return -1;
mem_unit = flip2_mem_unit(mem->desc);
if (mem_unit == FLIP2_MEM_UNIT_UNKNOWN) {
fprintf(stderr, "%s: Error: " \
"\"%s\" memory not accessible using FLIP",
progname, mem->desc);
if (strcmp(mem->desc, "flash") == 0)
fprintf(stderr, " (did you mean \"application\"?)");
fprintf(stderr, "\n");
return -1;
}
return flip2_write_memory(FLIP2(pgm)->dfu, mem_unit, addr, &value, 1);
}
int flip2_page_erase(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int base_addr)
{
return 0;
}
int flip2_paged_load(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int page_size, unsigned int addr, unsigned int n_bytes)
{
enum flip2_mem_unit mem_unit;
int result;
if (FLIP2(pgm)->dfu == NULL)
return -1;
mem_unit = flip2_mem_unit(mem->desc);
if (mem_unit == FLIP2_MEM_UNIT_UNKNOWN) {
fprintf(stderr, "%s: Error: " \
"\"%s\" memory not accessible using FLIP",
progname, mem->desc);
if (strcmp(mem->desc, "flash") == 0)
fprintf(stderr, " (did you mean \"application\"?)");
fprintf(stderr, "\n");
return -1;
}
if (n_bytes > INT_MAX) {
/* This should never happen, unless the int type is only 16 bits. */
fprintf(stderr, "%s: Error: Attempting to read more than %d bytes\n",
progname, INT_MAX);
exit(1);
}
result = flip2_read_memory(FLIP2(pgm)->dfu, mem_unit, addr,
mem->buf + addr, n_bytes);
#if 0 /* DEBUG */
fprintf(stderr, "%s: Debug: flip2_read_memory(%s,0x%04X,0x%04X) = %d\n",
progname, mem->desc, addr, n_bytes, result);
#endif
return (result == 0) ? n_bytes : -1;
}
int flip2_paged_write(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem,
unsigned int page_size, unsigned int addr, unsigned int n_bytes)
{
enum flip2_mem_unit mem_unit;
int result;
if (FLIP2(pgm)->dfu == NULL)
return -1;
mem_unit = flip2_mem_unit(mem->desc);
if (mem_unit == FLIP2_MEM_UNIT_UNKNOWN) {
fprintf(stderr, "%s: Error: " \
"\"%s\" memory not accessible using FLIP",
progname, mem->desc);
if (strcmp(mem->desc, "flash") == 0)
fprintf(stderr, " (did you mean \"application\"?)");
fprintf(stderr, "\n");
return -1;
}
if (n_bytes > INT_MAX) {
/* This should never happen, unless the int type is only 16 bits. */
fprintf(stderr, "%s: Error: Attempting to read more than %d bytes\n",
progname, INT_MAX);
exit(1);
}
result = flip2_write_memory(FLIP2(pgm)->dfu, mem_unit, addr,
mem->buf + addr, n_bytes);
#if 0 /* DEBUG */
fprintf(stderr, "%s: Debug: flip2_write_memory(%s,0x%04X,0x%04X) = %d\n",
progname, mem->desc, addr, n_bytes, result);
#endif
return 0;
}
int flip2_read_sig_bytes(PROGRAMMER* pgm, AVRPART *part, AVRMEM *mem)
{
if (FLIP2(pgm)->dfu == NULL)
return -1;
if (mem->size < sizeof(FLIP2(pgm)->part_sig)) {
fprintf(stderr, "%s: Error: Signature read must be at least %u bytes\n",
progname, (unsigned int) sizeof(FLIP2(pgm)->part_sig));
return -1;
}
memcpy(mem->buf, FLIP2(pgm)->part_sig, sizeof(FLIP2(pgm)->part_sig));
return 0;
}
void flip2_setup(PROGRAMMER * pgm)
{
pgm->cookie = calloc(1, sizeof(struct flip2));
if (pgm->cookie == NULL) {
perror(progname);
exit(1);
}
}
void flip2_teardown(PROGRAMMER * pgm)
{
free(pgm->cookie);
pgm->cookie = NULL;
}
/* INTERNAL FUNCTION DEFINITIONS
*/
void flip2_show_info(struct flip2 *flip2)
{
dfu_show_info(flip2->dfu);
fprintf(stderr, " Part signature : 0x%02X%02X%02X\n",
(int) flip2->part_sig[0],
(int) flip2->part_sig[1],
(int) flip2->part_sig[2]);
if (flip2->part_rev < 26)
fprintf(stderr, " Part revision : %c\n",
(char) (flip2->part_rev + 'A'));
else
fprintf(stderr, " Part revision : %c%c\n",
(char) (flip2->part_rev / 26 - 1 + 'A'),
(char) (flip2->part_rev % 26 + 'A'));
fprintf(stderr, " Bootloader version : 2.%hu.%hu\n",
((unsigned short) flip2->boot_ver >> 4) & 0xF,
((unsigned short) flip2->boot_ver >> 0) & 0xF);
fprintf(stderr, " USB max packet size : %hu\n",
(unsigned short) flip2->dfu->dev_desc.bMaxPacketSize0);
}
int flip2_read_memory(struct dfu_dev *dfu,
enum flip2_mem_unit mem_unit, uint32_t addr, void *ptr, int size)
{
unsigned short prev_page_addr;
unsigned short page_addr;
const char * mem_name;
int read_size;
int result;
result = flip2_set_mem_unit(dfu, mem_unit);
if (result != 0) {
if ((mem_name = flip2_mem_unit_str(mem_unit)) != NULL)
fprintf(stderr, "%s: Error: Failed to set memory unit 0x%02X (%s)\n",
progname, (int) mem_unit, mem_name);
else
fprintf(stderr, "%s: Error: Failed to set memory unit 0x%02X\n",
progname, (int) mem_unit);
return -1;
}
page_addr = addr >> 16;
result = flip2_set_mem_page(dfu, page_addr);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to set memory page 0x%04hX\n",
progname, page_addr);
return -1;
}
while (size > 0) {
prev_page_addr = page_addr;
page_addr = addr >> 16;
if (page_addr != prev_page_addr) {
result = flip2_set_mem_page(dfu, page_addr);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to set memory page 0x%04hX\n",
progname, page_addr);
return -1;
}
}
read_size = (size > 0x400) ? 0x400 : size;
result = flip2_read_max1k(dfu, addr & 0xFFFF, ptr, read_size);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to read 0x%04X bytes at 0x%04lX\n",
progname, read_size, (unsigned long) addr);
return -1;
}
ptr += read_size;
addr += read_size;
size -= read_size;
}
return 0;
}
int flip2_write_memory(struct dfu_dev *dfu,
enum flip2_mem_unit mem_unit, uint32_t addr, const void *ptr, int size)
{
unsigned short prev_page_addr;
unsigned short page_addr;
const char * mem_name;
int write_size;
int result;
result = flip2_set_mem_unit(dfu, mem_unit);
if (result != 0) {
if ((mem_name = flip2_mem_unit_str(mem_unit)) != NULL)
fprintf(stderr, "%s: Error: Failed to set memory unit 0x%02X (%s)\n",
progname, (int) mem_unit, mem_name);
else
fprintf(stderr, "%s: Error: Failed to set memory unit 0x%02X\n",
progname, (int) mem_unit);
return -1;
}
page_addr = addr >> 16;
result = flip2_set_mem_page(dfu, page_addr);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to set memory page 0x%04hX\n",
progname, page_addr);
return -1;
}
while (size > 0) {
prev_page_addr = page_addr;
page_addr = addr >> 16;
if (page_addr != prev_page_addr) {
result = flip2_set_mem_page(dfu, page_addr);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to set memory page 0x%04hX\n",
progname, page_addr);
return -1;
}
}
write_size = (size > 0x800) ? 0x800 : size;
result = flip2_write_max1k(dfu, addr & 0xFFFF, ptr, write_size);
if (result != 0) {
fprintf(stderr, "%s: Error: Failed to write 0x%04X bytes at 0x%04lX\n",
progname, write_size, (unsigned long) addr);
return -1;
}
ptr += write_size;
addr += write_size;
size -= write_size;
}
return 0;
}
int flip2_set_mem_unit(struct dfu_dev *dfu, enum flip2_mem_unit mem_unit)
{
struct dfu_status status;
int cmd_result = 0;
int aux_result;
struct flip2_cmd cmd = {
FLIP2_CMD_GROUP_SELECT, FLIP2_CMD_SELECT_MEMORY, { 0, 0, 0, 0 }
};
cmd.args[0] = FLIP2_SELECT_MEMORY_UNIT;
cmd.args[1] = mem_unit;
cmd_result = dfu_dnload(dfu, &cmd, sizeof(cmd));
aux_result = dfu_getstatus(dfu, &status);
if (aux_result != 0)
return aux_result;
if (status.bStatus != DFU_STATUS_OK) {
if (status.bStatus == ((FLIP2_STATUS_OUTOFRANGE >> 8) & 0xFF) &&
status.bState == ((FLIP2_STATUS_OUTOFRANGE >> 0) & 0xFF))
{
fprintf(stderr, "%s: Error: Unknown memory unit (0x%02x)\n",
progname, (unsigned int) mem_unit);
} else
fprintf(stderr, "%s: Error: DFU status %s\n", progname,
flip2_status_str(&status));
dfu_clrstatus(dfu);
}
return cmd_result;
}
int flip2_set_mem_page(struct dfu_dev *dfu,
unsigned short page_addr)
{
struct dfu_status status;
int cmd_result = 0;
int aux_result;
struct flip2_cmd cmd = {
FLIP2_CMD_GROUP_SELECT, FLIP2_CMD_SELECT_MEMORY, { 0, 0, 0, 0 }
};
cmd.args[0] = FLIP2_SELECT_MEMORY_PAGE;
cmd.args[1] = (page_addr >> 8) & 0xFF;
cmd.args[2] = (page_addr >> 0) & 0xFF;
cmd_result = dfu_dnload(dfu, &cmd, sizeof(cmd));
aux_result = dfu_getstatus(dfu, &status);
if (aux_result != 0)
return aux_result;
if (status.bStatus != DFU_STATUS_OK) {
if (status.bStatus == ((FLIP2_STATUS_OUTOFRANGE >> 8) & 0xFF) &&
status.bState == ((FLIP2_STATUS_OUTOFRANGE >> 0) & 0xFF))
{
fprintf(stderr, "%s: Error: Page address out of range (0x%04hx)\n",
progname, page_addr);
} else
fprintf(stderr, "%s: Error: DFU status %s\n", progname,
flip2_status_str(&status));
dfu_clrstatus(dfu);
}
return cmd_result;
}
int flip2_read_max1k(struct dfu_dev *dfu,
unsigned short offset, void *ptr, unsigned short size)
{
struct dfu_status status;
int cmd_result = 0;
int aux_result;
struct flip2_cmd cmd = {
FLIP2_CMD_GROUP_UPLOAD, FLIP2_CMD_READ_MEMORY, { 0, 0, 0, 0 }
};
cmd.args[0] = (offset >> 8) & 0xFF;
cmd.args[1] = (offset >> 0) & 0xFF;
cmd.args[2] = ((offset+size-1) >> 8) & 0xFF;
cmd.args[3] = ((offset+size-1) >> 0) & 0xFF;
cmd_result = dfu_dnload(dfu, &cmd, sizeof(cmd));
if (cmd_result != 0)
goto flip2_read_max1k_status;
cmd_result = dfu_upload(dfu, (char*) ptr, size);
flip2_read_max1k_status:
aux_result = dfu_getstatus(dfu, &status);
if (aux_result != 0)
return aux_result;
if (status.bStatus != DFU_STATUS_OK) {
if (status.bStatus == ((FLIP2_STATUS_OUTOFRANGE >> 8) & 0xFF) &&
status.bState == ((FLIP2_STATUS_OUTOFRANGE >> 0) & 0xFF))
{
fprintf(stderr, "%s: Error: Address out of range [0x%04hX,0x%04hX]\n",
progname, offset, offset+size-1);
} else
fprintf(stderr, "%s: Error: DFU status %s\n", progname,
flip2_status_str(&status));
dfu_clrstatus(dfu);
}
return cmd_result;
}
int flip2_write_max1k(struct dfu_dev *dfu,
unsigned short offset, const void *ptr, unsigned short size)
{
char buffer[64+64+0x400];
unsigned short data_offset;
struct dfu_status status;
int cmd_result = 0;
int aux_result;
struct flip2_cmd cmd = {
FLIP2_CMD_GROUP_DOWNLOAD, FLIP2_CMD_PROG_START, { 0, 0, 0, 0 }
};
cmd.args[0] = (offset >> 8) & 0xFF;
cmd.args[1] = (offset >> 0) & 0xFF;
cmd.args[2] = ((offset+size-1) >> 8) & 0xFF;
cmd.args[3] = ((offset+size-1) >> 0) & 0xFF;
if (size > 0x400) {
fprintf(stderr, "%s: Error: Write block too large (%hu > 1024)\n",
progname, size);
exit(1);
}
/* There are some special padding requirements for writes. The first packet
* must consist only of the FLIP2 command data, which must be padded to
* fill out the USB packet (the packet size is given by bMaxPacketSize0 in
* the device descriptor). In addition, the data must be padded so that the
* first byte of data to be written is at located at position (offset mod
* bMaxPacketSize0) within the packet.
*/
data_offset = dfu->dev_desc.bMaxPacketSize0;
data_offset += offset % dfu->dev_desc.bMaxPacketSize0;
memcpy(buffer, &cmd, sizeof(cmd));
memset(buffer + sizeof(cmd), 0, data_offset - sizeof(cmd));
memcpy(buffer + data_offset, ptr, size);
cmd_result = dfu_dnload(dfu, buffer, data_offset + size);
aux_result = dfu_getstatus(dfu, &status);
if (aux_result != 0)
return aux_result;
if (status.bStatus != DFU_STATUS_OK) {
if (status.bStatus == ((FLIP2_STATUS_OUTOFRANGE >> 8) & 0xFF) &&
status.bState == ((FLIP2_STATUS_OUTOFRANGE >> 0) & 0xFF))
{
fprintf(stderr, "%s: Error: Address out of range [0x%04hX,0x%04hX]\n",
progname, offset, offset+size-1);
} else
fprintf(stderr, "%s: Error: DFU status %s\n", progname,
flip2_status_str(&status));
dfu_clrstatus(dfu);
}
return cmd_result;
}
const char * flip2_status_str(const struct dfu_status *status)
{
unsigned short selector;
selector = (unsigned short) status->bStatus << 8;
selector |= status->bState;
switch (selector) {
case FLIP2_STATUS_OK: return "OK";
case FLIP2_STATUS_STALL: return "STALL";
case FLIP2_STATUS_MEM_UKNOWN: return "MEM_UKNOWN";
case FLIP2_STATUS_MEM_PROTECTED: return "MEM_PROTECTED";
case FLIP2_STATUS_OUTOFRANGE: return "OUTOFRANGE";
case FLIP2_STATUS_BLANK_FAIL: return "BLANK_FAIL";
case FLIP2_STATUS_ERASE_ONGOING: return "ERASE_ONGOING";
default: return dfu_status_str(status->bStatus);
}
}
const char * flip2_mem_unit_str(enum flip2_mem_unit mem_unit)
{
switch (mem_unit) {
case FLIP2_MEM_UNIT_FLASH: return "Flash";
case FLIP2_MEM_UNIT_EEPROM: return "EEPROM";
case FLIP2_MEM_UNIT_SECURITY: return "security";
case FLIP2_MEM_UNIT_CONFIGURATION: return "configuration";
case FLIP2_MEM_UNIT_BOOTLOADER: return "bootloader version";
case FLIP2_MEM_UNIT_SIGNATURE: return "signature";
case FLIP2_MEM_UNIT_USER: return "user";
case FLIP2_MEM_UNIT_INT_RAM: return "internal RAM";
case FLIP2_MEM_UNIT_EXT_MEM_CS0: return "EXT_MEM_CS0";
case FLIP2_MEM_UNIT_EXT_MEM_CS1: return "EXT_MEM_CS1";
case FLIP2_MEM_UNIT_EXT_MEM_CS2: return "EXT_MEM_CS2";
case FLIP2_MEM_UNIT_EXT_MEM_CS3: return "EXT_MEM_CS3";
case FLIP2_MEM_UNIT_EXT_MEM_CS4: return "EXT_MEM_CS4";
case FLIP2_MEM_UNIT_EXT_MEM_CS5: return "EXT_MEM_CS5";
case FLIP2_MEM_UNIT_EXT_MEM_CS6: return "EXT_MEM_CS6";
case FLIP2_MEM_UNIT_EXT_MEM_CS7: return "EXT_MEM_CS7";
case FLIP2_MEM_UNIT_EXT_MEM_DF: return "EXT_MEM_DF";
default: return "unknown";
}
}
enum flip2_mem_unit flip2_mem_unit(const char *name) {
if (strcasecmp(name, "application") == 0)
return FLIP2_MEM_UNIT_FLASH;
if (strcasecmp(name, "eeprom") == 0)
return FLIP2_MEM_UNIT_EEPROM;
if (strcasecmp(name, "signature") == 0)
return FLIP2_MEM_UNIT_SIGNATURE;
return FLIP2_MEM_UNIT_UNKNOWN;
}

38
flip.h Normal file
View File

@ -0,0 +1,38 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2012 Kirill Levchenko
*
* 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$ */
#ifndef flip_h
#define flip_h
#include "pgm.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const char flip2_desc[];
extern void flip2_initpgm(PROGRAMMER * pgm);
#ifdef __cplusplus
}
#endif
#endif /* flip_h */

View File

@ -34,6 +34,7 @@
#include "avrftdi.h"
#include "buspirate.h"
#include "butterfly.h"
#include "flip.h"
#include "ft245r.h"
#include "jtagmkI.h"
#include "jtagmkII.h"
@ -65,6 +66,7 @@ const PROGRAMMER_TYPE const programmers_types[] = {
{"dragon_jtag", jtagmkII_dragon_initpgm, jtagmkII_dragon_desc},
{"dragon_pdi", jtagmkII_dragon_pdi_initpgm, jtagmkII_dragon_pdi_desc},
{"dragon_pp", stk500v2_dragon_pp_initpgm, stk500v2_dragon_pp_desc},
{"flip2", flip2_initpgm, flip2_desc},
{"ftdi_syncbb", ft245r_initpgm, ft245r_desc},
{"jtagmki", jtagmkI_initpgm, jtagmkI_desc},
{"jtagmkii", jtagmkII_initpgm, jtagmkII_desc},