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:
parent
a323e4b9ec
commit
efc87b6d2e
20
ChangeLog
20
ChangeLog
|
@ -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.
|
|
@ -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
3
NEWS
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_*) */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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> ;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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},
|
||||
|
|
Loading…
Reference in New Issue