Merge pull request #772 from dbuchwald/main
SerialUPDI implementation - release candidate 1
This commit is contained in:
commit
2c0ccfed3a
|
@ -166,6 +166,17 @@ libavrdude_a_SOURCES = \
|
||||||
tpi.h \
|
tpi.h \
|
||||||
usbasp.c \
|
usbasp.c \
|
||||||
usbasp.h \
|
usbasp.h \
|
||||||
|
serialupdi.c \
|
||||||
|
serialupdi.h \
|
||||||
|
updi_constants.h \
|
||||||
|
updi_link.c \
|
||||||
|
updi_link.h \
|
||||||
|
updi_state.c \
|
||||||
|
updi_state.h \
|
||||||
|
updi_readwrite.c \
|
||||||
|
updi_readwrite.h \
|
||||||
|
updi_nvm.c \
|
||||||
|
updi_nvm.h \
|
||||||
usbdevs.h \
|
usbdevs.h \
|
||||||
usb_hidapi.c \
|
usb_hidapi.c \
|
||||||
usb_libusb.c \
|
usb_libusb.c \
|
||||||
|
|
|
@ -84,7 +84,8 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
|
||||||
{
|
{
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,7 +370,8 @@ static int avr910_open(PROGRAMMER * pgm, char * port)
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -613,6 +613,13 @@ programmer
|
||||||
reset = 3; # TMS 7
|
reset = 3; # TMS 7
|
||||||
;
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "serialupdi";
|
||||||
|
desc = "SerialUPDI";
|
||||||
|
type = "serialupdi";
|
||||||
|
connection_type = serial;
|
||||||
|
;
|
||||||
|
|
||||||
programmer
|
programmer
|
||||||
id = "avrisp";
|
id = "avrisp";
|
||||||
desc = "Atmel AVR ISP";
|
desc = "Atmel AVR ISP";
|
||||||
|
@ -16555,6 +16562,13 @@ part
|
||||||
readsize = 0x4;
|
readsize = 0x4;
|
||||||
;
|
;
|
||||||
|
|
||||||
|
memory "userrow"
|
||||||
|
size = 0x20;
|
||||||
|
offset = 0x1080;
|
||||||
|
page_size = 0x20;
|
||||||
|
readsize = 0x20;
|
||||||
|
;
|
||||||
|
|
||||||
memory "data"
|
memory "data"
|
||||||
# SRAM, only used to supply the offset
|
# SRAM, only used to supply the offset
|
||||||
offset = 0x1000000;
|
offset = 0x1000000;
|
||||||
|
|
|
@ -427,7 +427,8 @@ static int buspirate_open(struct programmer_t *pgm, char * port)
|
||||||
if(pgm->baudrate == 0)
|
if(pgm->baudrate == 0)
|
||||||
pgm->baudrate = 115200;
|
pgm->baudrate = 115200;
|
||||||
|
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -391,7 +391,8 @@ static int butterfly_open(PROGRAMMER * pgm, char * port)
|
||||||
if(pgm->baudrate == 0) {
|
if(pgm->baudrate == 0) {
|
||||||
pgm->baudrate = 19200;
|
pgm->baudrate = 19200;
|
||||||
}
|
}
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,7 +553,7 @@ static int jtagmkI_initialize(PROGRAMMER * pgm, AVRPART * p)
|
||||||
progname, pgm->baudrate);
|
progname, pgm->baudrate);
|
||||||
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
|
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
|
||||||
PDATA(pgm)->initial_baudrate = pgm->baudrate; /* don't adjust again later */
|
PDATA(pgm)->initial_baudrate = pgm->baudrate; /* don't adjust again later */
|
||||||
serial_setspeed(&pgm->fd, pgm->baudrate);
|
serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,9 +648,10 @@ static int jtagmkI_open(PROGRAMMER * pgm, char * port)
|
||||||
|
|
||||||
for (i = 0; i < sizeof(baudtab) / sizeof(baudtab[0]); i++) {
|
for (i = 0; i < sizeof(baudtab) / sizeof(baudtab[0]); i++) {
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
pinfo.baud = baudtab[i].baud;
|
pinfo.serialinfo.baud = baudtab[i].baud;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open(): trying to sync at baud rate %ld:\n",
|
avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open(): trying to sync at baud rate %ld:\n",
|
||||||
progname, pinfo.baud);
|
progname, pinfo.serialinfo.baud);
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -697,7 +698,7 @@ static void jtagmkI_close(PROGRAMMER * pgm)
|
||||||
"trying to set baudrate to %d\n",
|
"trying to set baudrate to %d\n",
|
||||||
progname, PDATA(pgm)->initial_baudrate);
|
progname, PDATA(pgm)->initial_baudrate);
|
||||||
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
|
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
|
||||||
serial_setspeed(&pgm->fd, pgm->baudrate);
|
serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1312,7 +1312,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p)
|
||||||
"trying to set baudrate to %d\n",
|
"trying to set baudrate to %d\n",
|
||||||
progname, pgm->baudrate);
|
progname, pgm->baudrate);
|
||||||
if (jtagmkII_setparm(pgm, PAR_BAUD_RATE, &b) == 0)
|
if (jtagmkII_setparm(pgm, PAR_BAUD_RATE, &b) == 0)
|
||||||
serial_setspeed(&pgm->fd, pgm->baudrate);
|
serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((pgm->flag & PGM_FL_IS_JTAG) && pgm->bitclock != 0.0) {
|
if ((pgm->flag & PGM_FL_IS_JTAG) && pgm->bitclock != 0.0) {
|
||||||
|
@ -1490,7 +1490,8 @@ static int jtagmkII_open(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -1542,7 +1543,8 @@ static int jtagmkII_open_dw(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -1594,7 +1596,8 @@ static int jtagmkII_open_pdi(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -1647,7 +1650,8 @@ static int jtagmkII_dragon_open(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -1700,7 +1704,8 @@ static int jtagmkII_dragon_open_dw(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -1753,7 +1758,8 @@ static int jtagmkII_dragon_open_pdi(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -3330,7 +3336,8 @@ static int jtagmkII_open32(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
|
|
@ -530,9 +530,34 @@ union filedescriptor
|
||||||
} usb;
|
} usb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SERIAL_CS5 0x0000
|
||||||
|
#define SERIAL_CS6 0x0001
|
||||||
|
#define SERIAL_CS7 0x0002
|
||||||
|
#define SERIAL_CS8 0x0004
|
||||||
|
|
||||||
|
#define SERIAL_NO_CSTOPB 0x0000
|
||||||
|
#define SERIAL_CSTOPB 0x0008
|
||||||
|
|
||||||
|
#define SERIAL_NO_CREAD 0x0000
|
||||||
|
#define SERIAL_CREAD 0x0010
|
||||||
|
|
||||||
|
#define SERIAL_NO_PARITY 0x0000
|
||||||
|
#define SERIAL_PARENB 0x0020
|
||||||
|
#define SERIAL_PARODD 0x0040
|
||||||
|
|
||||||
|
#define SERIAL_NO_CLOCAL 0x0000
|
||||||
|
#define SERIAL_CLOCAL 0x0080
|
||||||
|
|
||||||
|
#define SERIAL_8N1 (SERIAL_CS8 | SERIAL_NO_CSTOPB | SERIAL_CREAD | SERIAL_NO_PARITY | SERIAL_CLOCAL)
|
||||||
|
#define SERIAL_8E1 (SERIAL_CS8 | SERIAL_NO_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL)
|
||||||
|
#define SERIAL_8E2 (SERIAL_CS8 | SERIAL_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL)
|
||||||
|
|
||||||
union pinfo
|
union pinfo
|
||||||
{
|
{
|
||||||
long baud;
|
struct {
|
||||||
|
long baud;
|
||||||
|
unsigned long cflags;
|
||||||
|
} serialinfo;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
unsigned short vid;
|
unsigned short vid;
|
||||||
|
@ -548,7 +573,7 @@ struct serial_device
|
||||||
{
|
{
|
||||||
// open should return -1 on error, other values on success
|
// open should return -1 on error, other values on success
|
||||||
int (*open)(char * port, union pinfo pinfo, union filedescriptor *fd);
|
int (*open)(char * port, union pinfo pinfo, union filedescriptor *fd);
|
||||||
int (*setspeed)(union filedescriptor *fd, long baud);
|
int (*setparams)(union filedescriptor *fd, long baud, unsigned long cflags);
|
||||||
void (*close)(union filedescriptor *fd);
|
void (*close)(union filedescriptor *fd);
|
||||||
|
|
||||||
int (*send)(union filedescriptor *fd, const unsigned char * buf, size_t buflen);
|
int (*send)(union filedescriptor *fd, const unsigned char * buf, size_t buflen);
|
||||||
|
@ -570,7 +595,7 @@ extern struct serial_device avrdoper_serdev;
|
||||||
extern struct serial_device usbhid_serdev;
|
extern struct serial_device usbhid_serdev;
|
||||||
|
|
||||||
#define serial_open (serdev->open)
|
#define serial_open (serdev->open)
|
||||||
#define serial_setspeed (serdev->setspeed)
|
#define serial_setparams (serdev->setparams)
|
||||||
#define serial_close (serdev->close)
|
#define serial_close (serdev->close)
|
||||||
#define serial_send (serdev->send)
|
#define serial_send (serdev->send)
|
||||||
#define serial_recv (serdev->recv)
|
#define serial_recv (serdev->recv)
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "pickit2.h"
|
#include "pickit2.h"
|
||||||
#include "ppi.h"
|
#include "ppi.h"
|
||||||
#include "serbb.h"
|
#include "serbb.h"
|
||||||
|
#include "serialupdi.h"
|
||||||
#include "stk500.h"
|
#include "stk500.h"
|
||||||
#include "stk500generic.h"
|
#include "stk500generic.h"
|
||||||
#include "stk500v2.h"
|
#include "stk500v2.h"
|
||||||
|
@ -87,6 +88,7 @@ const PROGRAMMER_TYPE programmers_types[] = {
|
||||||
{"par", par_initpgm, par_desc},
|
{"par", par_initpgm, par_desc},
|
||||||
{"pickit2", pickit2_initpgm, pickit2_desc},
|
{"pickit2", pickit2_initpgm, pickit2_desc},
|
||||||
{"serbb", serbb_initpgm, serbb_desc},
|
{"serbb", serbb_initpgm, serbb_desc},
|
||||||
|
{"serialupdi", serialupdi_initpgm, serialupdi_desc},
|
||||||
{"stk500", stk500_initpgm, stk500_desc},
|
{"stk500", stk500_initpgm, stk500_desc},
|
||||||
{"stk500generic", stk500generic_initpgm, stk500generic_desc},
|
{"stk500generic", stk500generic_initpgm, stk500generic_desc},
|
||||||
{"stk500v2", stk500v2_initpgm, stk500v2_desc},
|
{"stk500v2", stk500v2_initpgm, stk500v2_desc},
|
||||||
|
|
|
@ -55,6 +55,8 @@ struct baud_mapping {
|
||||||
/* There are a lot more baud rates we could handle, but what's the point? */
|
/* There are a lot more baud rates we could handle, but what's the point? */
|
||||||
|
|
||||||
static struct baud_mapping baud_lookup_table [] = {
|
static struct baud_mapping baud_lookup_table [] = {
|
||||||
|
{ 300, B300 },
|
||||||
|
{ 600, B600 },
|
||||||
{ 1200, B1200 },
|
{ 1200, B1200 },
|
||||||
{ 2400, B2400 },
|
{ 2400, B2400 },
|
||||||
{ 4800, B4800 },
|
{ 4800, B4800 },
|
||||||
|
@ -96,7 +98,20 @@ static speed_t serial_baud_lookup(long baud)
|
||||||
return baud;
|
return baud;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ser_setspeed(union filedescriptor *fd, long baud)
|
static tcflag_t translate_flags(unsigned long cflags)
|
||||||
|
{
|
||||||
|
return ((cflags & SERIAL_CS5) ? CS5 : 0) |
|
||||||
|
((cflags & SERIAL_CS6) ? CS6 : 0) |
|
||||||
|
((cflags & SERIAL_CS7) ? CS7 : 0) |
|
||||||
|
((cflags & SERIAL_CS8) ? CS8 : 0) |
|
||||||
|
((cflags & SERIAL_CSTOPB) ? CSTOPB : 0) |
|
||||||
|
((cflags & SERIAL_CREAD) ? CREAD : 0) |
|
||||||
|
((cflags & (SERIAL_PARENB | SERIAL_PARODD)) ? PARENB : 0) |
|
||||||
|
((cflags & SERIAL_PARODD) ? PARODD : 0) |
|
||||||
|
((cflags & SERIAL_CLOCAL) ? CLOCAL : 0) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cflags)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct termios termios;
|
struct termios termios;
|
||||||
|
@ -110,7 +125,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
||||||
*/
|
*/
|
||||||
rc = tcgetattr(fd->ifd, &termios);
|
rc = tcgetattr(fd->ifd, &termios);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcgetattr() failed",
|
avrdude_message(MSG_INFO, "%s: ser_setparams(): tcgetattr() failed",
|
||||||
progname);
|
progname);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +140,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
||||||
termios.c_iflag = IGNBRK;
|
termios.c_iflag = IGNBRK;
|
||||||
termios.c_oflag = 0;
|
termios.c_oflag = 0;
|
||||||
termios.c_lflag = 0;
|
termios.c_lflag = 0;
|
||||||
termios.c_cflag = (CS8 | CREAD | CLOCAL);
|
termios.c_cflag = translate_flags(cflags);
|
||||||
termios.c_cc[VMIN] = 1;
|
termios.c_cc[VMIN] = 1;
|
||||||
termios.c_cc[VTIME] = 0;
|
termios.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
@ -134,7 +149,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
||||||
|
|
||||||
rc = tcsetattr(fd->ifd, TCSANOW, &termios);
|
rc = tcsetattr(fd->ifd, TCSANOW, &termios);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcsetattr() failed\n",
|
avrdude_message(MSG_INFO, "%s: ser_setparams(): tcsetattr() failed\n",
|
||||||
progname);
|
progname);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +313,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||||
/*
|
/*
|
||||||
* set serial line attributes
|
* set serial line attributes
|
||||||
*/
|
*/
|
||||||
rc = ser_setspeed(fdp, pinfo.baud);
|
rc = ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
avrdude_message(MSG_INFO, "%s: ser_open(): can't set attributes for device \"%s\": %s\n",
|
avrdude_message(MSG_INFO, "%s: ser_open(): can't set attributes for device \"%s\": %s\n",
|
||||||
progname, port, strerror(-rc));
|
progname, port, strerror(-rc));
|
||||||
|
@ -308,7 +323,6 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ser_close(union filedescriptor *fd)
|
static void ser_close(union filedescriptor *fd)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -501,7 +515,7 @@ static int ser_drain(union filedescriptor *fd, int display)
|
||||||
struct serial_device serial_serdev =
|
struct serial_device serial_serdev =
|
||||||
{
|
{
|
||||||
.open = ser_open,
|
.open = ser_open,
|
||||||
.setspeed = ser_setspeed,
|
.setparams = ser_setparams,
|
||||||
.close = ser_close,
|
.close = ser_close,
|
||||||
.send = ser_send,
|
.send = ser_send,
|
||||||
.recv = ser_recv,
|
.recv = ser_recv,
|
||||||
|
|
|
@ -54,6 +54,8 @@ static unsigned char serial_over_ethernet = 0;
|
||||||
/* HANDLE hComPort=INVALID_HANDLE_VALUE; */
|
/* HANDLE hComPort=INVALID_HANDLE_VALUE; */
|
||||||
|
|
||||||
static struct baud_mapping baud_lookup_table [] = {
|
static struct baud_mapping baud_lookup_table [] = {
|
||||||
|
{ 300, CBR_300 },
|
||||||
|
{ 600, CBR_600 },
|
||||||
{ 1200, CBR_1200 },
|
{ 1200, CBR_1200 },
|
||||||
{ 2400, CBR_2400 },
|
{ 2400, CBR_2400 },
|
||||||
{ 4800, CBR_4800 },
|
{ 4800, CBR_4800 },
|
||||||
|
@ -97,7 +99,7 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
|
||||||
return SetCommTimeouts(hComPort, &ctmo);
|
return SetCommTimeouts(hComPort, &ctmo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ser_setspeed(union filedescriptor *fd, long baud)
|
static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cflags)
|
||||||
{
|
{
|
||||||
if (serial_over_ethernet) {
|
if (serial_over_ethernet) {
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
|
@ -111,9 +113,39 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
||||||
dcb.fBinary = 1;
|
dcb.fBinary = 1;
|
||||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||||
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||||
dcb.ByteSize = 8;
|
switch ((cflags & (SERIAL_CS5 | SERIAL_CS6 | SERIAL_CS7 | SERIAL_CS8))) {
|
||||||
dcb.Parity = NOPARITY;
|
case SERIAL_CS5:
|
||||||
dcb.StopBits = ONESTOPBIT;
|
dcb.ByteSize = 5;
|
||||||
|
break;
|
||||||
|
case SERIAL_CS6:
|
||||||
|
dcb.ByteSize = 6;
|
||||||
|
break;
|
||||||
|
case SERIAL_CS7:
|
||||||
|
dcb.ByteSize = 7;
|
||||||
|
break;
|
||||||
|
case SERIAL_CS8:
|
||||||
|
dcb.ByteSize = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ((cflags & (SERIAL_NO_PARITY | SERIAL_PARENB | SERIAL_PARODD))) {
|
||||||
|
case SERIAL_NO_PARITY:
|
||||||
|
dcb.Parity = NOPARITY;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARENB:
|
||||||
|
dcb.Parity = EVENPARITY;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARODD:
|
||||||
|
dcb.Parity = ODDPARITY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ((cflags & (SERIAL_NO_CSTOPB | SERIAL_CSTOPB))) {
|
||||||
|
case SERIAL_NO_CSTOPB:
|
||||||
|
dcb.StopBits = ONESTOPBIT;
|
||||||
|
break;
|
||||||
|
case SERIAL_CSTOPB:
|
||||||
|
dcb.StopBits = TWOSTOPBITS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!SetCommState(hComPort, &dcb))
|
if (!SetCommState(hComPort, &dcb))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -283,7 +315,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fdp->pfd = (void *)hComPort;
|
fdp->pfd = (void *)hComPort;
|
||||||
if (ser_setspeed(fdp, pinfo.baud) != 0)
|
if (ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags) != 0)
|
||||||
{
|
{
|
||||||
CloseHandle(hComPort);
|
CloseHandle(hComPort);
|
||||||
avrdude_message(MSG_INFO, "%s: ser_open(): can't set com-state for \"%s\"\n",
|
avrdude_message(MSG_INFO, "%s: ser_open(): can't set com-state for \"%s\"\n",
|
||||||
|
@ -770,7 +802,7 @@ static int ser_drain(union filedescriptor *fd, int display)
|
||||||
struct serial_device serial_serdev =
|
struct serial_device serial_serdev =
|
||||||
{
|
{
|
||||||
.open = ser_open,
|
.open = ser_open,
|
||||||
.setspeed = ser_setspeed,
|
.setparams = ser_setparams,
|
||||||
.close = ser_close,
|
.close = ser_close,
|
||||||
.send = ser_send,
|
.send = ser_send,
|
||||||
.recv = ser_recv,
|
.recv = ser_recv,
|
||||||
|
|
|
@ -0,0 +1,965 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interface to the SerialUPDI programmer.
|
||||||
|
*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "avrdude.h"
|
||||||
|
#include "libavrdude.h"
|
||||||
|
#include "serialupdi.h"
|
||||||
|
#include "updi_link.h"
|
||||||
|
#include "updi_state.h"
|
||||||
|
#include "updi_readwrite.h"
|
||||||
|
#include "updi_nvm.h"
|
||||||
|
#include "updi_constants.h"
|
||||||
|
|
||||||
|
static int serialupdi_enter_progmode(PROGRAMMER * pgm);
|
||||||
|
static int serialupdi_leave_progmode(PROGRAMMER * pgm);
|
||||||
|
|
||||||
|
static void serialupdi_setup(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
if ((pgm->cookie = malloc(sizeof(updi_state))) == 0) {
|
||||||
|
avrdude_message(MSG_INFO,
|
||||||
|
"%s: serialupdi_setup(): Out of memory allocating private data\n",
|
||||||
|
progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
memset(pgm->cookie, 0, sizeof(updi_state));
|
||||||
|
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialupdi_teardown(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
free(pgm->cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_open(PROGRAMMER * pgm, char * port)
|
||||||
|
{
|
||||||
|
strcpy(pgm->port, port);
|
||||||
|
return updi_link_open(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
APPLY_RESET,
|
||||||
|
RELEASE_RESET
|
||||||
|
} reset_mode;
|
||||||
|
|
||||||
|
static int serialupdi_reset(PROGRAMMER * pgm, reset_mode mode)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def reset(self, apply_reset):
|
||||||
|
"""
|
||||||
|
Applies or releases an UPDI reset condition
|
||||||
|
|
||||||
|
:param apply_reset: True to apply, False to release
|
||||||
|
"""
|
||||||
|
if apply_reset:
|
||||||
|
self.logger.info("Apply reset")
|
||||||
|
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, constants.UPDI_RESET_REQ_VALUE)
|
||||||
|
else:
|
||||||
|
self.logger.info("Release reset")
|
||||||
|
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, 0x00)
|
||||||
|
*/
|
||||||
|
switch (mode) {
|
||||||
|
case APPLY_RESET:
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Sending reset request\n", progname);
|
||||||
|
return updi_write_cs(pgm, UPDI_ASI_RESET_REQ, UPDI_RESET_REQ_VALUE);
|
||||||
|
case RELEASE_RESET:
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Sending release reset request\n", progname);
|
||||||
|
return updi_write_cs(pgm, UPDI_ASI_RESET_REQ, 0x00);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_reset_connection(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updi_link_init(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_decode_sib(PROGRAMMER * pgm, updi_sib_info * sib_info)
|
||||||
|
{
|
||||||
|
char * str_ptr;
|
||||||
|
|
||||||
|
sib_info->sib_string[SIB_INFO_STRING_LENGTH]=0;
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Received SIB: [%s]\n", progname, sib_info->sib_string);
|
||||||
|
memset(sib_info->family_string, 0, SIB_INFO_FAMILY_LENGTH+1);
|
||||||
|
memset(sib_info->nvm_string, 0, SIB_INFO_NVM_LENGTH+1);
|
||||||
|
memset(sib_info->debug_string, 0, SIB_INFO_DEBUG_LENGTH+1);
|
||||||
|
memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1);
|
||||||
|
memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1);
|
||||||
|
memset(sib_info->extra_string, 0, SIB_INFO_EXTRA_LENGTH+1);
|
||||||
|
|
||||||
|
memcpy(sib_info->family_string, sib_info->sib_string, SIB_INFO_FAMILY_LENGTH);
|
||||||
|
memcpy(sib_info->nvm_string, sib_info->sib_string + 8, SIB_INFO_NVM_LENGTH);
|
||||||
|
memcpy(sib_info->debug_string, sib_info->sib_string + 11, SIB_INFO_DEBUG_LENGTH);
|
||||||
|
memcpy(sib_info->pdi_string, sib_info->sib_string + 15, SIB_INFO_PDI_LENGTH);
|
||||||
|
strcpy(sib_info->extra_string, (char *)sib_info->sib_string + 19);
|
||||||
|
|
||||||
|
str_ptr = strstr(sib_info->nvm_string, ":");
|
||||||
|
if (!str_ptr) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Incorrect format of NVM string\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sib_info->nvm_version = *(str_ptr+1);
|
||||||
|
|
||||||
|
str_ptr = strstr(sib_info->debug_string, ":");
|
||||||
|
if (!str_ptr) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Incorrect format of DEBUG string\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sib_info->debug_version = *(str_ptr+1);
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Device family ID: %s\n", progname, sib_info->family_string);
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: NVM interface: %s\n", progname, sib_info->nvm_string);
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Debug interface: %s\n", progname, sib_info->debug_string);
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: PDI oscillator: %s\n", progname, sib_info->pdi_string);
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Extra information: %s\n", progname, sib_info->extra_string);
|
||||||
|
switch (sib_info->nvm_version) {
|
||||||
|
case '0':
|
||||||
|
avrdude_message(MSG_INFO, "%s: NVM type 0: 16-bit, page oriented write\n", progname);
|
||||||
|
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V0);
|
||||||
|
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
avrdude_message(MSG_INFO, "%s: NVM type 2: 24-bit, word oriented write\n", progname);
|
||||||
|
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V2);
|
||||||
|
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_24BIT);
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
avrdude_message(MSG_INFO, "%s: NVM type 3: 16-bit, page oriented\n", progname);
|
||||||
|
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V3);
|
||||||
|
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
avrdude_message(MSG_INFO, "%s: Unsupported NVM type: %c, please update software\n", progname, sib_info->nvm_version);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialupdi_close(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Leaving NVM programming mode\n", progname);
|
||||||
|
|
||||||
|
if (serialupdi_leave_progmode(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Unable to leave NVM programming mode\n", progname);
|
||||||
|
}
|
||||||
|
updi_link_close(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_wait_for_unlock(PROGRAMMER * pgm, unsigned int ms) {
|
||||||
|
/*
|
||||||
|
def wait_unlocked(self, timeout_ms):
|
||||||
|
"""
|
||||||
|
Waits for the device to be unlocked.
|
||||||
|
All devices boot up as locked until proven otherwise
|
||||||
|
|
||||||
|
:param timeout_ms: number of milliseconds to wait
|
||||||
|
"""
|
||||||
|
timeout = Timeout(timeout_ms)
|
||||||
|
|
||||||
|
while not timeout.expired():
|
||||||
|
if not self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (
|
||||||
|
1 << constants.UPDI_ASI_SYS_STATUS_LOCKSTATUS):
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.logger.error("Timeout waiting for device to unlock")
|
||||||
|
return False
|
||||||
|
*/
|
||||||
|
unsigned long start_time;
|
||||||
|
unsigned long current_time;
|
||||||
|
struct timeval tv;
|
||||||
|
uint8_t status;
|
||||||
|
gettimeofday (&tv, NULL);
|
||||||
|
start_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||||
|
do {
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &status) >= 0) {
|
||||||
|
if (!(status & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gettimeofday (&tv, NULL);
|
||||||
|
current_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||||
|
} while ((current_time - start_time) < (ms * 1000));
|
||||||
|
|
||||||
|
avrdude_message(MSG_INFO, "%s: Timeout waiting for device to unlock\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WAIT_FOR_UROW_LOW,
|
||||||
|
WAIT_FOR_UROW_HIGH
|
||||||
|
} urow_wait_mode;
|
||||||
|
|
||||||
|
static int serialupdi_wait_for_urow(PROGRAMMER * pgm, unsigned int ms, urow_wait_mode mode) {
|
||||||
|
/*
|
||||||
|
def wait_urow_prog(self, timeout_ms, wait_for_high):
|
||||||
|
"""
|
||||||
|
Waits for the device to be in user row write mode
|
||||||
|
User row is writeable on a locked device using this mechanism
|
||||||
|
|
||||||
|
:param timeout_ms: number of milliseconds to wait
|
||||||
|
:param wait_for_high: set True to wait for bit to go high; False to wait for low
|
||||||
|
"""
|
||||||
|
timeout = Timeout(timeout_ms)
|
||||||
|
|
||||||
|
while not timeout.expired():
|
||||||
|
status = self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS)
|
||||||
|
if wait_for_high:
|
||||||
|
if status & (1 << constants.UPDI_ASI_SYS_STATUS_UROWPROG):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if not status & (1 << constants.UPDI_ASI_SYS_STATUS_UROWPROG):
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.logger.error("Timeout waiting for device to enter UROW WRITE mode")
|
||||||
|
return False
|
||||||
|
*/
|
||||||
|
unsigned long start_time;
|
||||||
|
unsigned long current_time;
|
||||||
|
struct timeval tv;
|
||||||
|
uint8_t status;
|
||||||
|
gettimeofday (&tv, NULL);
|
||||||
|
start_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||||
|
do {
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &status) >= 0) {
|
||||||
|
if (mode == WAIT_FOR_UROW_HIGH) {
|
||||||
|
if (status & (1 << UPDI_ASI_SYS_STATUS_UROWPROG)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!(status & (1 << UPDI_ASI_SYS_STATUS_UROWPROG))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gettimeofday (&tv, NULL);
|
||||||
|
current_time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
||||||
|
} while ((current_time - start_time) < (ms * 1000));
|
||||||
|
|
||||||
|
avrdude_message(MSG_INFO, "%s: Timeout waiting for device to complete UROW WRITE\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_in_prog_mode(PROGRAMMER * pgm, uint8_t * in_prog_mode)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def in_prog_mode(self):
|
||||||
|
"""
|
||||||
|
Checks whether the NVM PROG flag is up
|
||||||
|
"""
|
||||||
|
if self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (1 << constants.UPDI_ASI_SYS_STATUS_NVMPROG):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
*/
|
||||||
|
uint8_t value;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Read CS operation failed\n", progname);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_NVMPROG)) {
|
||||||
|
*in_prog_mode = 1;
|
||||||
|
} else {
|
||||||
|
*in_prog_mode = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_enter_progmode(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def enter_progmode(self):
|
||||||
|
"""
|
||||||
|
Enters into NVM programming mode
|
||||||
|
"""
|
||||||
|
# First check if NVM is already enabled
|
||||||
|
if self.in_prog_mode():
|
||||||
|
self.logger.info("Already in NVM programming mode")
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.logger.info("Entering NVM programming mode")
|
||||||
|
|
||||||
|
# Put in the key
|
||||||
|
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_NVM)
|
||||||
|
|
||||||
|
# Check key status
|
||||||
|
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
|
||||||
|
self.logger.debug("Key status = 0x%02X", key_status)
|
||||||
|
|
||||||
|
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_NVMPROG):
|
||||||
|
self.logger.error("Key status = 0x%02X", key_status)
|
||||||
|
raise IOError("Key not accepted")
|
||||||
|
|
||||||
|
# Toggle reset
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
|
||||||
|
# And wait for unlock
|
||||||
|
if not self.wait_unlocked(100):
|
||||||
|
raise IOError("Failed to enter NVM programming mode: device is locked")
|
||||||
|
|
||||||
|
# Check for NVMPROG flag
|
||||||
|
if not self.in_prog_mode():
|
||||||
|
raise IOError("Failed to enter NVM programming mode")
|
||||||
|
|
||||||
|
self.logger.debug("Now in NVM programming mode")
|
||||||
|
return True
|
||||||
|
*/
|
||||||
|
uint8_t in_prog_mode;
|
||||||
|
unsigned char buffer[8];
|
||||||
|
uint8_t key_status;
|
||||||
|
|
||||||
|
if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (in_prog_mode) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Already in prog mode\n", progname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, UPDI_KEY_NVM, sizeof(buffer));
|
||||||
|
if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Writing NVM KEY failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_KEY_STATUS, &key_status) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Checking KEY status failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Key status: 0x%02X\n", progname, key_status);
|
||||||
|
|
||||||
|
if (!(key_status & (1 << UPDI_ASI_KEY_STATUS_NVMPROG))) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Key was not accepted\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_wait_for_unlock(pgm, 100) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to enter NVM programming mode: device is locked\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_prog_mode) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to enter NVM programming mode\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Entered NVM programming mode\n", progname);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_leave_progmode(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def leave_progmode(self):
|
||||||
|
"""
|
||||||
|
Disables UPDI which releases any keys enabled
|
||||||
|
"""
|
||||||
|
self.logger.info("Leaving NVM programming mode")
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
self.readwrite.write_cs(constants.UPDI_CS_CTRLB,
|
||||||
|
(1 << constants.UPDI_CTRLB_UPDIDIS_BIT) | (1 << constants.UPDI_CTRLB_CCDETDIS_BIT))
|
||||||
|
*/
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updi_write_cs(pgm, UPDI_CS_CTRLB, (1 << UPDI_CTRLB_UPDIDIS_BIT) | (1 << UPDI_CTRLB_CCDETDIS_BIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_write_userrow(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
|
unsigned int page_size,
|
||||||
|
unsigned int addr, unsigned int n_bytes)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_user_row_locked_device(self, address, data):
|
||||||
|
"""
|
||||||
|
Writes data to the user row when the device is locked, using a key.
|
||||||
|
"""
|
||||||
|
# Put in the key
|
||||||
|
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_UROW)
|
||||||
|
|
||||||
|
# Check key status
|
||||||
|
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
|
||||||
|
self.logger.debug("Key status = 0x%02X", key_status)
|
||||||
|
|
||||||
|
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_UROWWRITE):
|
||||||
|
raise PymcuprogError("Key not accepted")
|
||||||
|
|
||||||
|
# Toggle reset
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
|
||||||
|
# Wait for mode to be entered
|
||||||
|
if not self.wait_urow_prog(500, wait_for_high=True):
|
||||||
|
raise PymcuprogError("Failed to enter urow write mode using key")
|
||||||
|
|
||||||
|
# At this point we can write one 'page' to the device, and have it transfered into the user row
|
||||||
|
# Transfer data
|
||||||
|
self.readwrite.write_data(address, data)
|
||||||
|
|
||||||
|
# Finalize
|
||||||
|
self.readwrite.write_cs(constants.UPDI_ASI_SYS_CTRLA,
|
||||||
|
(1 << constants.UPDI_ASI_SYS_CTRLA_UROW_FINAL) |
|
||||||
|
(1 << constants.UPDI_CTRLB_CCDETDIS_BIT))
|
||||||
|
|
||||||
|
# Wait for mode to be exited
|
||||||
|
if not self.wait_urow_prog(500, wait_for_high=False):
|
||||||
|
# Toggle reset
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
raise PymcuprogError("Failed to exit urow write mode")
|
||||||
|
|
||||||
|
# Clear status
|
||||||
|
self.readwrite.write_cs(constants.UPDI_ASI_KEY_STATUS,
|
||||||
|
(1 << constants.UPDI_ASI_KEY_STATUS_UROWWRITE) |
|
||||||
|
(1 << constants.UPDI_CTRLB_CCDETDIS_BIT))
|
||||||
|
|
||||||
|
# Toggle reset
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
*/
|
||||||
|
unsigned char buffer[8];
|
||||||
|
uint8_t key_status;
|
||||||
|
|
||||||
|
memcpy(buffer, UPDI_KEY_UROW, sizeof(buffer));
|
||||||
|
if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Writing USERROW KEY failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_KEY_STATUS, &key_status) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Checking KEY status failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Key status: 0x%02X\n", progname, key_status);
|
||||||
|
|
||||||
|
if (!(key_status & (1 << UPDI_ASI_KEY_STATUS_UROWWRITE))) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Key was not accepted\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_wait_for_urow(pgm, 500, WAIT_FOR_UROW_HIGH) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to enter USERROW programming mode\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_write_data(pgm, m->offset+addr, m->buf + addr, n_bytes) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Writing USER ROW failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_write_cs(pgm, UPDI_ASI_SYS_CTRLA, (1 << UPDI_ASI_SYS_CTRLA_UROW_FINAL) |
|
||||||
|
(1 << UPDI_CTRLB_CCDETDIS_BIT)) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed trying to commit user row write\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_wait_for_urow(pgm, 500, WAIT_FOR_UROW_LOW) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Failed to exit USERROW programming mode\n", progname);
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_write_cs(pgm, UPDI_ASI_KEY_STATUS, (1 << UPDI_ASI_KEY_STATUS_UROWWRITE) |
|
||||||
|
(1 << UPDI_CTRLB_CCDETDIS_BIT)) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed trying to complete user row write\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialupdi_reset_connection(pgm);
|
||||||
|
|
||||||
|
serialupdi_enter_progmode(pgm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p)
|
||||||
|
{
|
||||||
|
uint8_t value;
|
||||||
|
uint8_t reset_link_required=0;
|
||||||
|
|
||||||
|
if (updi_link_init(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname);
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) {
|
||||||
|
|
||||||
|
/* let's try reset the connection */
|
||||||
|
if (!serialupdi_reset_connection(pgm)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Read CS operation during initialization failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device is locked\n", progname);
|
||||||
|
}
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_UROWPROG)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device in USER ROW programming state, leaving programming mode\n", progname);
|
||||||
|
reset_link_required = 1;
|
||||||
|
}
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_NVMPROG)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device in NVM programming state, leaving programming mode\n", progname);
|
||||||
|
reset_link_required = 1;
|
||||||
|
}
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_INSLEEP)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device is in SLEEP mode\n", progname);
|
||||||
|
}
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_RSTSYS)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device in reset status, trying to release it\n", progname);
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET)<0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reset_link_required) {
|
||||||
|
if (serialupdi_reset_connection(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: UPDI link reset failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updi_sib_info * sib_info = updi_get_sib_info(pgm);
|
||||||
|
|
||||||
|
if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) {
|
||||||
|
/* this should never happen, let's try to reset connection and try again */
|
||||||
|
if (serialupdi_reset_connection(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: SerialUPDI reset connection failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (serialupdi_decode_sib(pgm, sib_info) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_init(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
avrdude_message(MSG_INFO, "%s: Entering NVM programming mode\n", progname);
|
||||||
|
/* try, but ignore failure */
|
||||||
|
serialupdi_enter_progmode(pgm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialupdi_disable(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/* Do nothing. */
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialupdi_enable(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/* Do nothing. */
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialupdi_display(PROGRAMMER * pgm, const char * p)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_cmd(PROGRAMMER * pgm, const unsigned char * cmd,
|
||||||
|
unsigned char * res)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: error: cmd %s[%s] not implemented yet\n",
|
||||||
|
progname, cmd, res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_program_enable(PROGRAMMER * pgm, AVRPART * p)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: error: program enable not implemented yet\n",
|
||||||
|
progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
|
unsigned long addr, unsigned char * value)
|
||||||
|
{
|
||||||
|
return updi_read_byte(pgm, mem->offset + addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
|
unsigned long addr, unsigned char value)
|
||||||
|
{
|
||||||
|
if (strstr(mem->desc, "fuse") != 0) {
|
||||||
|
return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value);
|
||||||
|
}
|
||||||
|
if (strcmp(mem->desc, "lock") == 0) {
|
||||||
|
return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value);
|
||||||
|
}
|
||||||
|
if (strcmp(mem->desc, "eeprom") == 0) {
|
||||||
|
unsigned char buffer[1];
|
||||||
|
buffer[0]=value;
|
||||||
|
return updi_nvm_write_eeprom(pgm, p, mem->offset + addr, buffer, 1);
|
||||||
|
}
|
||||||
|
if (strcmp(mem->desc, "flash") == 0) {
|
||||||
|
unsigned char buffer[1];
|
||||||
|
buffer[0]=value;
|
||||||
|
return updi_nvm_write_flash(pgm, p, mem->offset + addr, buffer, 1);
|
||||||
|
}
|
||||||
|
return updi_write_byte(pgm, mem->offset + addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
|
unsigned int page_size,
|
||||||
|
unsigned int addr, unsigned int n_bytes)
|
||||||
|
{
|
||||||
|
if (n_bytes > m->readsize) {
|
||||||
|
unsigned int read_offset = addr;
|
||||||
|
unsigned int remaining_bytes = n_bytes;
|
||||||
|
int read_bytes = 0;
|
||||||
|
int rc;
|
||||||
|
while (remaining_bytes > 0) {
|
||||||
|
rc = updi_read_data(pgm, m->offset + read_offset, m->buf + read_offset,
|
||||||
|
remaining_bytes > m->readsize ? m->readsize : remaining_bytes);
|
||||||
|
if (rc < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Paged load operation failed\n", progname);
|
||||||
|
return rc;
|
||||||
|
} else {
|
||||||
|
read_bytes+=rc;
|
||||||
|
read_offset+=m->readsize;
|
||||||
|
remaining_bytes-=m->readsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return read_bytes;
|
||||||
|
} else {
|
||||||
|
return updi_read_data(pgm, m->offset + addr, m->buf + addr, n_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
|
unsigned int page_size,
|
||||||
|
unsigned int addr, unsigned int n_bytes)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
if (n_bytes > m->page_size) {
|
||||||
|
unsigned int write_offset = addr;
|
||||||
|
unsigned int remaining_bytes = n_bytes;
|
||||||
|
int write_bytes = 0;
|
||||||
|
while (remaining_bytes > 0) {
|
||||||
|
|
||||||
|
if (strcmp(m->desc, "eeprom")==0) {
|
||||||
|
rc = updi_nvm_write_eeprom(pgm, p, m->offset + write_offset, m->buf + write_offset,
|
||||||
|
remaining_bytes > m->page_size ? m->page_size : remaining_bytes);
|
||||||
|
} else if (strcmp(m->desc, "flash")==0) {
|
||||||
|
rc = updi_nvm_write_flash(pgm, p, m->offset + write_offset, m->buf + write_offset,
|
||||||
|
remaining_bytes > m->page_size ? m->page_size : remaining_bytes);
|
||||||
|
} else if (strcmp(m->desc, "userrow")==0) {
|
||||||
|
rc = serialupdi_write_userrow(pgm, p, m, page_size, write_offset,
|
||||||
|
remaining_bytes > m->page_size ? m->page_size : remaining_bytes);
|
||||||
|
} else if (strcmp(m->desc, "fuses")==0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Page write operation requested for fuses, falling back to byte-level write\n", progname);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes);
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Paged write operation failed\n", progname);
|
||||||
|
return rc;
|
||||||
|
} else {
|
||||||
|
write_bytes+=rc;
|
||||||
|
write_offset+=m->page_size;
|
||||||
|
remaining_bytes-=m->page_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return write_bytes;
|
||||||
|
} else {
|
||||||
|
if (strcmp(m->desc, "eeprom")==0) {
|
||||||
|
rc = updi_nvm_write_eeprom(pgm, p, m->offset+addr, m->buf+addr, n_bytes);
|
||||||
|
} else if (strcmp(m->desc, "flash")==0) {
|
||||||
|
rc = updi_nvm_write_flash(pgm, p, m->offset+addr, m->buf+addr, n_bytes);
|
||||||
|
} else if (strcmp(m->desc, "userrow")==0) {
|
||||||
|
rc = serialupdi_write_userrow(pgm, p, m, page_size, addr, n_bytes);
|
||||||
|
} else if (strcmp(m->desc, "fuses")==0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Page write operation requested for fuses, falling back to byte-level write\n", progname);
|
||||||
|
rc = -1;
|
||||||
|
} else {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes);
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_unlock(PROGRAMMER * pgm, AVRPART * p)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def unlock(self):
|
||||||
|
"""
|
||||||
|
Unlock by chip erase
|
||||||
|
"""
|
||||||
|
# Put in the key
|
||||||
|
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_CHIPERASE)
|
||||||
|
|
||||||
|
# Check key status
|
||||||
|
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
|
||||||
|
self.logger.debug("Key status = 0x%02X", key_status)
|
||||||
|
|
||||||
|
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE):
|
||||||
|
raise PymcuprogError("Key not accepted")
|
||||||
|
|
||||||
|
# Toggle reset
|
||||||
|
self.reset(apply_reset=True)
|
||||||
|
self.reset(apply_reset=False)
|
||||||
|
|
||||||
|
# And wait for unlock
|
||||||
|
if not self.wait_unlocked(500):
|
||||||
|
raise PymcuprogError("Failed to chip erase using key")
|
||||||
|
*/
|
||||||
|
unsigned char buffer[8];
|
||||||
|
uint8_t key_status;
|
||||||
|
|
||||||
|
memcpy(buffer, UPDI_KEY_CHIPERASE, sizeof(buffer));
|
||||||
|
|
||||||
|
if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Writing NVM KEY failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_KEY_STATUS, &key_status) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Checking KEY status failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Key status: 0x%02X\n", progname, key_status);
|
||||||
|
|
||||||
|
if (!(key_status & (1 << UPDI_ASI_KEY_STATUS_CHIPERASE))) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Key not accepted\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialupdi_wait_for_unlock(pgm, 500) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Waiting for unlock failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_init(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: UPDI link reinitialization failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialupdi_enter_progmode(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_chip_erase(PROGRAMMER * pgm, AVRPART * p)
|
||||||
|
{
|
||||||
|
uint8_t value;
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Read CS operation during chip erase failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Device is locked\n", progname);
|
||||||
|
if (ovsigck) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Attempting device erase\n", progname);
|
||||||
|
return serialupdi_unlock(pgm, p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return updi_nvm_chip_erase(pgm, p);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_page_erase(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
|
unsigned int baseaddr)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: error: page erase not implemented yet\n",
|
||||||
|
progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_read_signature(PROGRAMMER * pgm, AVRPART *p, AVRMEM *m) {
|
||||||
|
|
||||||
|
uint8_t value;
|
||||||
|
|
||||||
|
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: Read CS operation during signature read failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) {
|
||||||
|
m->buf[0]=0x00;
|
||||||
|
m->buf[1]=0x00;
|
||||||
|
m->buf[2]=0x00;
|
||||||
|
} else {
|
||||||
|
updi_read_byte(pgm, m->offset + 0, m->buf);
|
||||||
|
updi_read_byte(pgm, m->offset + 1, m->buf+1);
|
||||||
|
updi_read_byte(pgm, m->offset + 2, m->buf+2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serialupdi_read_sib(PROGRAMMER * pgm, AVRPART *p, char *sib) {
|
||||||
|
|
||||||
|
updi_sib_info * sib_info = updi_get_sib_info(pgm);
|
||||||
|
|
||||||
|
memcpy(sib, sib_info->sib_string, 32);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void serialupdi_initpgm(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
strcpy(pgm->type, "serialupdi");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mandatory functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
pgm->initialize = serialupdi_initialize;
|
||||||
|
pgm->display = serialupdi_display;
|
||||||
|
pgm->enable = serialupdi_enable;
|
||||||
|
pgm->disable = serialupdi_disable;
|
||||||
|
pgm->program_enable = serialupdi_program_enable;
|
||||||
|
pgm->chip_erase = serialupdi_chip_erase;
|
||||||
|
pgm->cmd = serialupdi_cmd;
|
||||||
|
pgm->open = serialupdi_open;
|
||||||
|
pgm->close = serialupdi_close;
|
||||||
|
pgm->read_byte = serialupdi_read_byte;
|
||||||
|
pgm->write_byte = serialupdi_write_byte;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* optional functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
pgm->unlock = serialupdi_unlock;
|
||||||
|
pgm->paged_write = serialupdi_paged_write;
|
||||||
|
pgm->read_sig_bytes = serialupdi_read_signature;
|
||||||
|
pgm->read_sib = serialupdi_read_sib;
|
||||||
|
pgm->paged_load = serialupdi_paged_load;
|
||||||
|
pgm->page_erase = serialupdi_page_erase;
|
||||||
|
pgm->setup = serialupdi_setup;
|
||||||
|
pgm->teardown = serialupdi_teardown;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const char serialupdi_desc[] = "Driver for SerialUPDI programmers";
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef serialupdi_h
|
||||||
|
#define serialupdi_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const char serialupdi_desc[];
|
||||||
|
void serialupdi_initpgm (PROGRAMMER * pgm);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* serialupdi_h */
|
|
@ -657,7 +657,8 @@ static int stk500_open(PROGRAMMER * pgm, char * port)
|
||||||
{
|
{
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
if (serial_open(port, pinfo, &pgm->fd)==-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1603,12 +1603,12 @@ static void stk500v2_enable(PROGRAMMER * pgm)
|
||||||
|
|
||||||
static int stk500v2_open(PROGRAMMER * pgm, char * port)
|
static int stk500v2_open(PROGRAMMER * pgm, char * port)
|
||||||
{
|
{
|
||||||
union pinfo pinfo = { .baud = 115200 };
|
union pinfo pinfo = { .serialinfo.baud = 115200, .serialinfo.cflags = SERIAL_8N1 };
|
||||||
|
|
||||||
DEBUG("STK500V2: stk500v2_open()\n");
|
DEBUG("STK500V2: stk500v2_open()\n");
|
||||||
|
|
||||||
if (pgm->baudrate)
|
if (pgm->baudrate)
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
|
||||||
PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;
|
PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;
|
||||||
|
|
||||||
|
@ -1671,12 +1671,12 @@ static int stk500v2_open(PROGRAMMER * pgm, char * port)
|
||||||
|
|
||||||
static int stk600_open(PROGRAMMER * pgm, char * port)
|
static int stk600_open(PROGRAMMER * pgm, char * port)
|
||||||
{
|
{
|
||||||
union pinfo pinfo = { .baud = 115200 };
|
union pinfo pinfo = { .serialinfo.baud = 115200, .serialinfo.cflags = SERIAL_8N1 };
|
||||||
|
|
||||||
DEBUG("STK500V2: stk600_open()\n");
|
DEBUG("STK500V2: stk600_open()\n");
|
||||||
|
|
||||||
if (pgm->baudrate)
|
if (pgm->baudrate)
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
|
||||||
PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;
|
PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;
|
||||||
|
|
||||||
|
@ -3392,7 +3392,8 @@ static int stk500v2_jtagmkII_open(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -3503,7 +3504,8 @@ static int stk500v2_dragon_isp_open(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
@ -3581,7 +3583,8 @@ static int stk500v2_dragon_hv_open(PROGRAMMER * pgm, char * port)
|
||||||
* a higher baud rate, we switch to it later on, after establishing
|
* a higher baud rate, we switch to it later on, after establishing
|
||||||
* the connection with the ICE.
|
* the connection with the ICE.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the port name starts with "usb", divert the serial routines
|
* If the port name starts with "usb", divert the serial routines
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef updi_constants_h
|
||||||
|
#define updi_constants_h
|
||||||
|
|
||||||
|
#define UPDI_BREAK 0x00
|
||||||
|
|
||||||
|
#define UPDI_LDS 0x00
|
||||||
|
#define UPDI_STS 0x40
|
||||||
|
#define UPDI_LD 0x20
|
||||||
|
#define UPDI_ST 0x60
|
||||||
|
#define UPDI_LDCS 0x80
|
||||||
|
#define UPDI_STCS 0xC0
|
||||||
|
#define UPDI_REPEAT 0xA0
|
||||||
|
#define UPDI_KEY 0xE0
|
||||||
|
|
||||||
|
#define UPDI_PTR 0x00
|
||||||
|
#define UPDI_PTR_INC 0x04
|
||||||
|
#define UPDI_PTR_ADDRESS 0x08
|
||||||
|
|
||||||
|
#define UPDI_ADDRESS_8 0x00
|
||||||
|
#define UPDI_ADDRESS_16 0x04
|
||||||
|
#define UPDI_ADDRESS_24 0x08
|
||||||
|
|
||||||
|
#define UPDI_DATA_8 0x00
|
||||||
|
#define UPDI_DATA_16 0x01
|
||||||
|
#define UPDI_DATA_24 0x02
|
||||||
|
|
||||||
|
#define UPDI_KEY_SIB 0x04
|
||||||
|
#define UPDI_KEY_KEY 0x00
|
||||||
|
|
||||||
|
#define UPDI_KEY_64 0x00
|
||||||
|
#define UPDI_KEY_128 0x01
|
||||||
|
#define UPDI_KEY_256 0x02
|
||||||
|
|
||||||
|
#define UPDI_SIB_8BYTES UPDI_KEY_64
|
||||||
|
#define UPDI_SIB_16BYTES UPDI_KEY_128
|
||||||
|
#define UPDI_SIB_32BYTES UPDI_KEY_256
|
||||||
|
|
||||||
|
#define UPDI_REPEAT_BYTE 0x00
|
||||||
|
#define UPDI_REPEAT_WORD 0x01
|
||||||
|
|
||||||
|
#define UPDI_PHY_SYNC 0x55
|
||||||
|
#define UPDI_PHY_ACK 0x40
|
||||||
|
|
||||||
|
#define UPDI_MAX_REPEAT_SIZE (0xFF+1) // Repeat counter of 1-byte, with off-by-one counting
|
||||||
|
|
||||||
|
//# CS and ASI Register Address map
|
||||||
|
#define UPDI_CS_STATUSA 0x00
|
||||||
|
#define UPDI_CS_STATUSB 0x01
|
||||||
|
#define UPDI_CS_CTRLA 0x02
|
||||||
|
#define UPDI_CS_CTRLB 0x03
|
||||||
|
#define UPDI_ASI_KEY_STATUS 0x07
|
||||||
|
#define UPDI_ASI_RESET_REQ 0x08
|
||||||
|
#define UPDI_ASI_CTRLA 0x09
|
||||||
|
#define UPDI_ASI_SYS_CTRLA 0x0A
|
||||||
|
#define UPDI_ASI_SYS_STATUS 0x0B
|
||||||
|
#define UPDI_ASI_CRC_STATUS 0x0C
|
||||||
|
|
||||||
|
#define UPDI_CTRLA_IBDLY_BIT 7
|
||||||
|
#define UPDI_CTRLB_CCDETDIS_BIT 3
|
||||||
|
#define UPDI_CTRLB_UPDIDIS_BIT 2
|
||||||
|
|
||||||
|
#define UPDI_KEY_NVM "NVMProg "
|
||||||
|
#define UPDI_KEY_CHIPERASE "NVMErase"
|
||||||
|
#define UPDI_KEY_UROW "NVMUs&te"
|
||||||
|
|
||||||
|
#define UPDI_ASI_STATUSA_REVID 4
|
||||||
|
#define UPDI_ASI_STATUSB_PESIG 0
|
||||||
|
|
||||||
|
#define UPDI_ASI_KEY_STATUS_CHIPERASE 3
|
||||||
|
#define UPDI_ASI_KEY_STATUS_NVMPROG 4
|
||||||
|
#define UPDI_ASI_KEY_STATUS_UROWWRITE 5
|
||||||
|
|
||||||
|
#define UPDI_ASI_SYS_STATUS_RSTSYS 5
|
||||||
|
#define UPDI_ASI_SYS_STATUS_INSLEEP 4
|
||||||
|
#define UPDI_ASI_SYS_STATUS_NVMPROG 3
|
||||||
|
#define UPDI_ASI_SYS_STATUS_UROWPROG 2
|
||||||
|
#define UPDI_ASI_SYS_STATUS_LOCKSTATUS 0
|
||||||
|
|
||||||
|
#define UPDI_ASI_SYS_CTRLA_UROW_FINAL 1
|
||||||
|
|
||||||
|
#define UPDI_RESET_REQ_VALUE 0x59
|
||||||
|
|
||||||
|
// FLASH CONTROLLER
|
||||||
|
#define UPDI_NVMCTRL_CTRLA 0x00
|
||||||
|
#define UPDI_NVMCTRL_CTRLB 0x01
|
||||||
|
#define UPDI_NVMCTRL_STATUS 0x02
|
||||||
|
#define UPDI_NVMCTRL_INTCTRL 0x03
|
||||||
|
#define UPDI_NVMCTRL_INTFLAGS 0x04
|
||||||
|
#define UPDI_NVMCTRL_DATAL 0x06
|
||||||
|
#define UPDI_NVMCTRL_DATAH 0x07
|
||||||
|
#define UPDI_NVMCTRL_ADDRL 0x08
|
||||||
|
#define UPDI_NVMCTRL_ADDRH 0x09
|
||||||
|
|
||||||
|
// NVMCTRL v0 CTRLA
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_NOP 0x00
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE 0x01
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE 0x02
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE 0x03
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR 0x04
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE 0x05
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM 0x06
|
||||||
|
#define UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE 0x07
|
||||||
|
|
||||||
|
// NVMCTRL v2 CTRLA
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_NOCMD 0x00
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE 0x02
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE 0x13
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE 0x20
|
||||||
|
#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE 0x30
|
||||||
|
|
||||||
|
// NVMCTRL v3 CTRLA
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_NOCMD 0x00
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_NOP 0x01
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE 0x04
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE_WRITE 0x05
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR 0x0F
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_WRITE 0x14
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE 0x15
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE 0x17
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_BUFFER_CLEAR 0x1F
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE 0x20
|
||||||
|
#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE 0x30
|
||||||
|
|
||||||
|
#define UPDI_NVM_STATUS_WRITE_ERROR 2
|
||||||
|
#define UPDI_NVM_STATUS_EEPROM_BUSY 1
|
||||||
|
#define UPDI_NVM_STATUS_FLASH_BUSY 0
|
||||||
|
|
||||||
|
#endif /* updi_constants_h */
|
|
@ -0,0 +1,930 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "avrdude.h"
|
||||||
|
#include "libavrdude.h"
|
||||||
|
#include "updi_link.h"
|
||||||
|
#include "updi_constants.h"
|
||||||
|
#include "updi_state.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
void msleep(int tms)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = tms / 1000;
|
||||||
|
tv.tv_usec = (tms % 1000) * 1000;
|
||||||
|
select (0, NULL, NULL, NULL, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_physical_open(PROGRAMMER* pgm, int baudrate, unsigned long cflags)
|
||||||
|
{
|
||||||
|
serial_recv_timeout = 100;
|
||||||
|
union pinfo pinfo;
|
||||||
|
|
||||||
|
pinfo.serialinfo.baud = baudrate;
|
||||||
|
pinfo.serialinfo.cflags = cflags;
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Opening serial port...\n", progname);
|
||||||
|
|
||||||
|
if (serial_open(pgm->port, pinfo, &pgm->fd)==-1) {
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Serial port open failed!\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drain any extraneous input
|
||||||
|
*/
|
||||||
|
serial_drain(&pgm->fd, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updi_physical_close(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
serial_close(&pgm->fd);
|
||||||
|
pgm->fd.ifd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_physical_send(PROGRAMMER * pgm, unsigned char * buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Sending %lu bytes [", progname, len);
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
avrdude_message(MSG_DEBUG, "0x%02x", buf[i]);
|
||||||
|
if (i<len-1) {
|
||||||
|
avrdude_message(MSG_DEBUG, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_DEBUG, "]\n");
|
||||||
|
|
||||||
|
rv = serial_send(&pgm->fd, buf, len);
|
||||||
|
serial_recv(&pgm->fd, buf, len);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_physical_recv(PROGRAMMER * pgm, unsigned char * buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = serial_recv(&pgm->fd, buf, len);
|
||||||
|
if (rv < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG,
|
||||||
|
"%s: serialupdi_recv(): programmer is not responding\n",
|
||||||
|
progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Received %lu bytes [", progname, len);
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
avrdude_message(MSG_DEBUG, "0x%02x", buf[i]);
|
||||||
|
if (i<len-1) {
|
||||||
|
avrdude_message(MSG_DEBUG, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_DEBUG, "]\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_physical_send_double_break(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
unsigned char buffer[1];
|
||||||
|
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Sending double break\n", progname);
|
||||||
|
|
||||||
|
updi_physical_close(pgm);
|
||||||
|
|
||||||
|
if (updi_physical_open(pgm, 300, SERIAL_8E1)==-1) {
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[0] = UPDI_BREAK;
|
||||||
|
|
||||||
|
serial_send(&pgm->fd, buffer, 1);
|
||||||
|
serial_recv(&pgm->fd, buffer, 1);
|
||||||
|
|
||||||
|
msleep(100);
|
||||||
|
|
||||||
|
buffer[0] = UPDI_BREAK;
|
||||||
|
|
||||||
|
serial_send(&pgm->fd, buffer, 1);
|
||||||
|
serial_recv(&pgm->fd, buffer, 1);
|
||||||
|
|
||||||
|
updi_physical_close(pgm);
|
||||||
|
|
||||||
|
return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_physical_sib(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def sib(self):
|
||||||
|
"""
|
||||||
|
System information block is just a string coming back from a SIB command
|
||||||
|
"""
|
||||||
|
self.send([
|
||||||
|
constants.UPDI_PHY_SYNC,
|
||||||
|
constants.UPDI_KEY | constants.UPDI_KEY_SIB | constants.UPDI_SIB_32BYTES])
|
||||||
|
return self.ser.readline()
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[2];
|
||||||
|
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_KEY | UPDI_KEY_SIB | UPDI_SIB_32BYTES;
|
||||||
|
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: SIB request send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updi_physical_recv(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_open(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
if (updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_physical_send_double_break(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updi_link_close(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
updi_physical_close(pgm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_link_init_session_parameters(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def _init_session_parameters(self):
|
||||||
|
"""
|
||||||
|
Set the inter-byte delay bit and disable collision detection
|
||||||
|
"""
|
||||||
|
self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT)
|
||||||
|
self.stcs(constants.UPDI_CS_CTRLA, 1 << constants.UPDI_CTRLA_IBDLY_BIT)
|
||||||
|
*/
|
||||||
|
if (updi_link_stcs(pgm, UPDI_CS_CTRLB, 1 << UPDI_CTRLB_CCDETDIS_BIT) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_stcs(pgm, UPDI_CS_CTRLA, 1 << UPDI_CTRLA_IBDLY_BIT) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_link_check(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def _check_datalink(self):
|
||||||
|
"""
|
||||||
|
Check UPDI by loading CS STATUSA
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self.ldcs(constants.UPDI_CS_STATUSA) != 0:
|
||||||
|
self.logger.info("UPDI init OK")
|
||||||
|
return True
|
||||||
|
except PymcuprogError:
|
||||||
|
self.logger.warning("Check failed")
|
||||||
|
return False
|
||||||
|
self.logger.info("UPDI not OK - reinitialisation required")
|
||||||
|
return False
|
||||||
|
*/
|
||||||
|
int result;
|
||||||
|
uint8_t value;
|
||||||
|
result = updi_link_ldcs(pgm, UPDI_CS_STATUSA, &value);
|
||||||
|
if (result < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Check failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (value > 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UDPI init OK\n", progname);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UDPI not OK - reinitialisation required\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int updi_link_init(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def init_datalink(self):
|
||||||
|
"""
|
||||||
|
Init DL layer
|
||||||
|
"""
|
||||||
|
self._init_session_parameters()
|
||||||
|
# Check
|
||||||
|
if not self._check_datalink():
|
||||||
|
# Send double break if all is not well, and re-check
|
||||||
|
self.updi_phy.send_double_break()
|
||||||
|
self._init_session_parameters()
|
||||||
|
if not self._check_datalink():
|
||||||
|
raise PymcuprogError("UPDI initialisation failed")
|
||||||
|
*/
|
||||||
|
if (updi_link_init_session_parameters(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Session initialisation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_check(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Datalink not active, resetting...\n", progname);
|
||||||
|
if (updi_physical_send_double_break(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Datalink initialisation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_link_init_session_parameters(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Session initialisation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_link_check(pgm) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Restoring datalink failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def ldcs(self, address):
|
||||||
|
"""
|
||||||
|
Load data from Control/Status space
|
||||||
|
|
||||||
|
:param address: address to load
|
||||||
|
"""
|
||||||
|
self.logger.debug("LDCS from 0x%02X", address)
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LDCS | (address & 0x0F)])
|
||||||
|
response = self.updi_phy.receive(self.LDCS_RESPONSE_BYTES)
|
||||||
|
numbytes_received = len(response)
|
||||||
|
if numbytes_received != self.LDCS_RESPONSE_BYTES:
|
||||||
|
raise PymcuprogError("Unexpected number of bytes in response: "
|
||||||
|
"{} byte(s) expected {} byte(s)".format(numbytes_received, self.LDCS_RESPONSE_BYTES))
|
||||||
|
|
||||||
|
return response[0]
|
||||||
|
*/
|
||||||
|
unsigned char buffer[2];
|
||||||
|
int result;
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LDCS from 0x%02X\n", progname, address);
|
||||||
|
buffer[0]=UPDI_PHY_SYNC;
|
||||||
|
buffer[1]=UPDI_LDCS | (address & 0x0F);
|
||||||
|
if (updi_physical_send(pgm, buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LDCS send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
result = updi_physical_recv(pgm, buffer, 1);
|
||||||
|
if (result != 1) {
|
||||||
|
if (result >= 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Incorrect response size, received %d instead of %d bytes\n", progname, result, 1);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
* value = buffer[0];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def stcs(self, address, value):
|
||||||
|
"""
|
||||||
|
Store a value to Control/Status space
|
||||||
|
|
||||||
|
:param address: address to store to
|
||||||
|
:param value: value to write
|
||||||
|
"""
|
||||||
|
self.logger.debug("STCS to 0x%02X", address)
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_STCS | (address & 0x0F), value])
|
||||||
|
*/
|
||||||
|
unsigned char buffer[3];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: STCS 0x%02X to address 0x%02X\n", progname, value, address);
|
||||||
|
buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
buffer[1] = UPDI_STCS | (address & 0x0F);
|
||||||
|
buffer[2] = value;
|
||||||
|
return updi_physical_send(pgm, buffer, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def ld_ptr_inc(self, size):
|
||||||
|
"""
|
||||||
|
Loads a number of bytes from the pointer location with pointer post-increment
|
||||||
|
|
||||||
|
:param size: number of bytes to load
|
||||||
|
:return: values read
|
||||||
|
"""
|
||||||
|
self.logger.debug("LD8 from ptr++")
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC |
|
||||||
|
constants.UPDI_DATA_8])
|
||||||
|
return self.updi_phy.receive(size)
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[2];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD8 from ptr++\n", progname);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_8;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD_PTR_INC send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_physical_recv(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def ld_ptr_inc16(self, words):
|
||||||
|
"""
|
||||||
|
Load a 16-bit word value from the pointer location with pointer post-increment
|
||||||
|
|
||||||
|
:param words: number of words to load
|
||||||
|
:return: values read
|
||||||
|
"""
|
||||||
|
self.logger.debug("LD16 from ptr++")
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC |
|
||||||
|
constants.UPDI_DATA_16])
|
||||||
|
return self.updi_phy.receive(words << 1)
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[2];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD16 from ptr++\n", progname);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_16;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD_PTR_INC send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_physical_recv(pgm, buffer, words << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def st_ptr_inc(self, data):
|
||||||
|
"""
|
||||||
|
Store data to the pointer location with pointer post-increment
|
||||||
|
|
||||||
|
:param data: data to store
|
||||||
|
"""
|
||||||
|
self.logger.debug("ST8 to *ptr++")
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_8,
|
||||||
|
data[0]])
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("ACK error with st_ptr_inc")
|
||||||
|
|
||||||
|
num = 1
|
||||||
|
while num < len(data):
|
||||||
|
self.updi_phy.send([data[num]])
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("Error with st_ptr_inc")
|
||||||
|
num += 1
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[3];
|
||||||
|
unsigned char recv_buffer[1];
|
||||||
|
int response;
|
||||||
|
int num = 1;
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST8 to *ptr++\n", progname);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_8;
|
||||||
|
send_buffer[2] = buffer[0];
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 3) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = updi_physical_recv(pgm, recv_buffer, 1);
|
||||||
|
|
||||||
|
if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ACK was expected but not received\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num < size) {
|
||||||
|
send_buffer[0]=buffer[num];
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 1) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC data send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
response = updi_physical_recv(pgm, recv_buffer, 1);
|
||||||
|
|
||||||
|
if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Data ACK was expected but not received\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def st_ptr_inc16(self, data):
|
||||||
|
"""
|
||||||
|
Store a 16-bit word value to the pointer location with pointer post-increment
|
||||||
|
|
||||||
|
:param data: data to store
|
||||||
|
"""
|
||||||
|
self.logger.debug("ST16 to *ptr++")
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |
|
||||||
|
constants.UPDI_DATA_16, data[0], data[1]])
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("ACK error with st_ptr_inc16")
|
||||||
|
|
||||||
|
num = 2
|
||||||
|
while num < len(data):
|
||||||
|
self.updi_phy.send([data[num], data[num + 1]])
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("Error with st_ptr_inc16")
|
||||||
|
num += 2
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[4];
|
||||||
|
unsigned char recv_buffer[1];
|
||||||
|
int response;
|
||||||
|
int num = 2;
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++\n", progname);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16;
|
||||||
|
send_buffer[2] = buffer[0];
|
||||||
|
send_buffer[3] = buffer[1];
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC16 send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = updi_physical_recv(pgm, recv_buffer, 1);
|
||||||
|
|
||||||
|
if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ACK was expected but not received\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num < words) {
|
||||||
|
send_buffer[0]=buffer[num];
|
||||||
|
send_buffer[1]=buffer[num+1];
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC data send operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
response = updi_physical_recv(pgm, recv_buffer, 1);
|
||||||
|
|
||||||
|
if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Data ACK was expected but not received\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num+=2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize) {
|
||||||
|
/*
|
||||||
|
def st_ptr_inc16_RSD(self, data, blocksize):
|
||||||
|
"""
|
||||||
|
Store a 16-bit word value to the pointer location with pointer post-increment
|
||||||
|
:param data: data to store
|
||||||
|
:blocksize: max number of bytes being sent -1 for all.
|
||||||
|
Warning: This does not strictly honor blocksize for values < 6
|
||||||
|
We always glob together the STCS(RSD) and REP commands.
|
||||||
|
But this should pose no problems for compatibility, because your serial adapter can't deal with 6b chunks,
|
||||||
|
none of pymcuprog would work!
|
||||||
|
"""
|
||||||
|
self.logger.debug("ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d", len(data), blocksize)
|
||||||
|
|
||||||
|
#for performance we glob everything together into one USB transfer....
|
||||||
|
repnumber= ((len(data) >> 1) -1)
|
||||||
|
data = [*data, *[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x06]]
|
||||||
|
|
||||||
|
if blocksize == -1 :
|
||||||
|
# Send whole thing at once stcs + repeat + st + (data + stcs)
|
||||||
|
blocksize = 3 + 3 + 2 + len(data)
|
||||||
|
num = 0
|
||||||
|
firstpacket = []
|
||||||
|
if blocksize < 10 :
|
||||||
|
# very small block size - we send pair of 2-byte commands first.
|
||||||
|
firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x0E],
|
||||||
|
*[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)]]
|
||||||
|
data = [*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |constants.UPDI_DATA_16], *data]
|
||||||
|
num = 0
|
||||||
|
else:
|
||||||
|
firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA , 0x0E],
|
||||||
|
*[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)],
|
||||||
|
*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_16],
|
||||||
|
*data[:blocksize - 8]]
|
||||||
|
num = blocksize - 8
|
||||||
|
self.updi_phy.send( firstpacket )
|
||||||
|
|
||||||
|
# if finite block size, this is used.
|
||||||
|
while num < len(data):
|
||||||
|
data_slice = data[num:num+blocksize]
|
||||||
|
self.updi_phy.send(data_slice)
|
||||||
|
num += len(data_slice)
|
||||||
|
*/
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d\n", progname, words * 2, blocksize);
|
||||||
|
|
||||||
|
unsigned int temp_buffer_size = 3 + 3 + 2 + (words * 2) + 3;
|
||||||
|
unsigned int num=0;
|
||||||
|
unsigned char* temp_buffer = malloc(temp_buffer_size);
|
||||||
|
|
||||||
|
if (temp_buffer == 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Allocating temporary buffer failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocksize == -1) {
|
||||||
|
blocksize = temp_buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
temp_buffer[1] = UPDI_STCS | UPDI_CS_CTRLA;
|
||||||
|
temp_buffer[2] = 0x0E;
|
||||||
|
temp_buffer[3] = UPDI_PHY_SYNC;
|
||||||
|
temp_buffer[4] = UPDI_REPEAT | UPDI_REPEAT_BYTE;
|
||||||
|
temp_buffer[5] = (words - 1) & 0xFF;
|
||||||
|
temp_buffer[6] = UPDI_PHY_SYNC;
|
||||||
|
temp_buffer[7] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16;
|
||||||
|
|
||||||
|
memcpy(temp_buffer + 8, buffer, words * 2);
|
||||||
|
|
||||||
|
temp_buffer[temp_buffer_size-3] = UPDI_PHY_SYNC;
|
||||||
|
temp_buffer[temp_buffer_size-2] = UPDI_STCS | UPDI_CS_CTRLA;
|
||||||
|
temp_buffer[temp_buffer_size-1] = 0x06;
|
||||||
|
|
||||||
|
if (blocksize < 10) {
|
||||||
|
if (updi_physical_send(pgm, temp_buffer, 6) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Failed to send first package\n", progname);
|
||||||
|
free(temp_buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num < temp_buffer_size) {
|
||||||
|
int next_package_size;
|
||||||
|
|
||||||
|
if (num + blocksize > temp_buffer_size) {
|
||||||
|
next_package_size = temp_buffer_size - num;
|
||||||
|
} else {
|
||||||
|
next_package_size = blocksize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_physical_send(pgm, temp_buffer + num, next_package_size) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Failed to send package\n", progname);
|
||||||
|
free(temp_buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
num+=next_package_size;
|
||||||
|
}
|
||||||
|
free(temp_buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def repeat(self, repeats):
|
||||||
|
"""
|
||||||
|
Store a value to the repeat counter
|
||||||
|
|
||||||
|
:param repeats: number of repeats requested
|
||||||
|
"""
|
||||||
|
self.logger.debug("Repeat %d", repeats)
|
||||||
|
if (repeats - 1) > constants.UPDI_MAX_REPEAT_SIZE:
|
||||||
|
self.logger.error("Invalid repeat count of %d", repeats)
|
||||||
|
raise Exception("Invalid repeat count!")
|
||||||
|
repeats -= 1
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE,
|
||||||
|
repeats & 0xFF])
|
||||||
|
*/
|
||||||
|
unsigned char buffer[3];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Repeat %d\n", progname, repeats);
|
||||||
|
if ((repeats - 1) > UPDI_MAX_REPEAT_SIZE) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Invalid repeat count of %d\n", progname, repeats);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
repeats-=1;
|
||||||
|
buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
buffer[1] = UPDI_REPEAT | UPDI_REPEAT_BYTE;
|
||||||
|
buffer[2] = repeats & 0xFF;
|
||||||
|
return updi_physical_send(pgm, buffer, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_sib(self):
|
||||||
|
"""
|
||||||
|
Read the SIB
|
||||||
|
"""
|
||||||
|
return self.updi_phy.sib()
|
||||||
|
*/
|
||||||
|
return updi_physical_sib(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def key(self, size, key):
|
||||||
|
"""
|
||||||
|
Write a key
|
||||||
|
|
||||||
|
:param size: size of key (0=64B, 1=128B, 2=256B)
|
||||||
|
:param key: key value
|
||||||
|
"""
|
||||||
|
self.logger.debug("Writing key")
|
||||||
|
if len(key) != 8 << size:
|
||||||
|
raise PymcuprogError("Invalid KEY length!")
|
||||||
|
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_KEY | constants.UPDI_KEY_KEY | size])
|
||||||
|
self.updi_phy.send(list(reversed(list(key))))
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[2];
|
||||||
|
unsigned char reversed_key[256];
|
||||||
|
int index;
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI writing key\n", progname);
|
||||||
|
if (size != (8 << size_type)) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Invalid key length\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_KEY | UPDI_KEY_KEY | size_type;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI key send message failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* reverse key contents */
|
||||||
|
for (index=0; index<size; index++) {
|
||||||
|
reversed_key[index] = buffer[size-index-1];
|
||||||
|
}
|
||||||
|
return updi_physical_send(pgm, reversed_key, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_ld(PROGRAMMER * pgm, uint32_t address, uint8_t * value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def ld(self, address):
|
||||||
|
"""
|
||||||
|
Load a single byte direct from a 24-bit address
|
||||||
|
|
||||||
|
:param address: address to load from
|
||||||
|
:return: value read
|
||||||
|
"""
|
||||||
|
self.logger.info("LD from 0x{0:06X}".format(address))
|
||||||
|
self.updi_phy.send(
|
||||||
|
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8,
|
||||||
|
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
|
||||||
|
return self.updi_phy.receive(1)[0]
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[5];
|
||||||
|
unsigned char recv_buffer[1];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD from 0x%06X\n", progname, address);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_LDS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16);
|
||||||
|
send_buffer[2] = address & 0xFF;
|
||||||
|
send_buffer[3] = (address >> 8) & 0xFF;
|
||||||
|
send_buffer[4] = (address >> 16) & 0xFF;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD operation send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_physical_recv(pgm, recv_buffer, 1) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD operation recv failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
* value = recv_buffer[0];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def ld16(self, address):
|
||||||
|
"""
|
||||||
|
Load a 16-bit word directly from a 24-bit address
|
||||||
|
|
||||||
|
:param address: address to load from
|
||||||
|
:return: values read
|
||||||
|
"""
|
||||||
|
self.logger.info("LD from 0x{0:06X}".format(address))
|
||||||
|
self.updi_phy.send(
|
||||||
|
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16,
|
||||||
|
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
|
||||||
|
return self.updi_phy.receive(2)
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[5];
|
||||||
|
unsigned char recv_buffer[2];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD16 from 0x%06X\n", progname, address);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_LDS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16);
|
||||||
|
send_buffer[2] = address & 0xFF;
|
||||||
|
send_buffer[3] = (address >> 8) & 0xFF;
|
||||||
|
send_buffer[4] = (address >> 16) & 0xFF;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD16 operation send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_physical_recv(pgm, recv_buffer, 2) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: LD16 operation recv failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
* value = (recv_buffer[0] << 8 | recv_buffer[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updi_link_st_data_phase(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def _st_data_phase(self, values):
|
||||||
|
"""
|
||||||
|
Performs data phase of transaction:
|
||||||
|
* receive ACK
|
||||||
|
* send data
|
||||||
|
|
||||||
|
:param values: bytearray of value(s) to send
|
||||||
|
"""
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("Error with st")
|
||||||
|
|
||||||
|
self.updi_phy.send(values)
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("Error with st")
|
||||||
|
*/
|
||||||
|
unsigned char recv_buffer[1];
|
||||||
|
if (updi_physical_recv(pgm, recv_buffer, 1) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI data phase recv failed on first ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI data phase expected first ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_physical_send(pgm, buffer, size) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI data phase send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_physical_recv(pgm, recv_buffer, 1) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI data phase recv failed on second ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI data phase expected second ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def st(self, address, value):
|
||||||
|
"""
|
||||||
|
Store a single byte value directly to a 24-bit address
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param value: value to write
|
||||||
|
"""
|
||||||
|
self.logger.info("ST to 0x{0:06X}".format(address))
|
||||||
|
self.updi_phy.send(
|
||||||
|
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8,
|
||||||
|
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
|
||||||
|
return self._st_data_phase([value & 0xFF])
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[5];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST to 0x%06X\n", progname, address);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_STS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16);
|
||||||
|
send_buffer[2] = address & 0xFF;
|
||||||
|
send_buffer[3] = (address >> 8) & 0xFF;
|
||||||
|
send_buffer[4] = (address >> 16) & 0xFF;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST operation send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
send_buffer[0] = value;
|
||||||
|
return updi_link_st_data_phase(pgm, send_buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def st16(self, address, value):
|
||||||
|
"""
|
||||||
|
Store a 16-bit word value directly to a 24-bit address
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param value: value to write
|
||||||
|
"""
|
||||||
|
self.logger.info("ST to 0x{0:06X}".format(address))
|
||||||
|
self.updi_phy.send(
|
||||||
|
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16,
|
||||||
|
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
|
||||||
|
return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF])
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[5];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST16 to 0x%06X\n", progname, address);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_STS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16);
|
||||||
|
send_buffer[2] = address & 0xFF;
|
||||||
|
send_buffer[3] = (address >> 8) & 0xFF;
|
||||||
|
send_buffer[4] = (address >> 16) & 0xFF;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST16 operation send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
send_buffer[0] = value & 0xFF;
|
||||||
|
send_buffer[1] = (value >> 8) & 0xFF;
|
||||||
|
return updi_link_st_data_phase(pgm, send_buffer, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def st_ptr(self, address):
|
||||||
|
"""
|
||||||
|
Set the pointer location
|
||||||
|
|
||||||
|
:param address: address to write
|
||||||
|
"""
|
||||||
|
self.logger.info("ST to ptr")
|
||||||
|
self.updi_phy.send(
|
||||||
|
[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_24,
|
||||||
|
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
|
||||||
|
response = self.updi_phy.receive(1)
|
||||||
|
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
|
||||||
|
raise PymcuprogError("Error with st_ptr")
|
||||||
|
*/
|
||||||
|
unsigned char send_buffer[5];
|
||||||
|
unsigned char recv_buffer[1];
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR to 0x%06X\n", progname, address);
|
||||||
|
send_buffer[0] = UPDI_PHY_SYNC;
|
||||||
|
send_buffer[1] = UPDI_STS | UPDI_ST | UPDI_PTR_ADDRESS | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_DATA_24 : UPDI_DATA_16);
|
||||||
|
send_buffer[2] = address & 0xFF;
|
||||||
|
send_buffer[3] = (address >> 8) & 0xFF;
|
||||||
|
send_buffer[4] = (address >> 16) & 0xFF;
|
||||||
|
if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR operation send failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_physical_recv(pgm, recv_buffer, 1) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI ST_PTR recv failed on ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (recv_buffer[0] != UPDI_PHY_ACK) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: UPDI ST_PTR expected ACK\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef updi_link_h
|
||||||
|
#define updi_link_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int updi_link_open(PROGRAMMER * pgm);
|
||||||
|
void updi_link_close(PROGRAMMER * pgm);
|
||||||
|
int updi_link_init(PROGRAMMER * pgm);
|
||||||
|
int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value);
|
||||||
|
int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value);
|
||||||
|
int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words);
|
||||||
|
int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words);
|
||||||
|
int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize);
|
||||||
|
int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats);
|
||||||
|
int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size);
|
||||||
|
int updi_link_ld(PROGRAMMER * pgm, uint32_t address, uint8_t * value);
|
||||||
|
int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value);
|
||||||
|
int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value);
|
||||||
|
int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value);
|
||||||
|
int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* updi_link_h */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef updi_nvm_h
|
||||||
|
#define updi_nvm_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p);
|
||||||
|
int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address);
|
||||||
|
int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p);
|
||||||
|
int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size);
|
||||||
|
int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value);
|
||||||
|
int updi_nvm_wait_ready(PROGRAMMER * pgm, AVRPART *p);
|
||||||
|
int updi_nvm_command(PROGRAMMER * pgm, AVRPART *p, uint8_t command);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* updi_nvm_h */
|
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "avrdude.h"
|
||||||
|
#include "libavrdude.h"
|
||||||
|
#include "updi_constants.h"
|
||||||
|
#include "updi_link.h"
|
||||||
|
#include "updi_readwrite.h"
|
||||||
|
|
||||||
|
int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_cs(self, address):
|
||||||
|
"""
|
||||||
|
Read from Control/Status space
|
||||||
|
|
||||||
|
:param address: address (index) to read
|
||||||
|
:return: value read
|
||||||
|
"""
|
||||||
|
return self.datalink.ldcs(address)
|
||||||
|
*/
|
||||||
|
return updi_link_ldcs(pgm, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_cs(self, address, value):
|
||||||
|
"""
|
||||||
|
Write to Control/Status space
|
||||||
|
|
||||||
|
:param address: address (index) to write
|
||||||
|
:param value: 8-bit value to write
|
||||||
|
"""
|
||||||
|
return self.datalink.stcs(address, value)
|
||||||
|
*/
|
||||||
|
return updi_link_stcs(pgm, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_key(self, size, key):
|
||||||
|
"""
|
||||||
|
Write a KEY into UPDI
|
||||||
|
|
||||||
|
:param size: size of key to send
|
||||||
|
:param key: key value
|
||||||
|
"""
|
||||||
|
return self.datalink.key(size, key)
|
||||||
|
*/
|
||||||
|
return updi_link_key(pgm, buffer, size_type, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_sib(self):
|
||||||
|
"""
|
||||||
|
Read the SIB from UPDI
|
||||||
|
|
||||||
|
:return: SIB string (bytearray) read
|
||||||
|
"""
|
||||||
|
return self.datalink.read_sib()
|
||||||
|
*/
|
||||||
|
return updi_link_read_sib(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_byte(self, address):
|
||||||
|
"""
|
||||||
|
Read a single byte from UPDI
|
||||||
|
|
||||||
|
:param address: address to read from
|
||||||
|
:return: value read
|
||||||
|
"""
|
||||||
|
return self.datalink.ld(address)
|
||||||
|
*/
|
||||||
|
return updi_link_ld(pgm, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_byte(self, address, value):
|
||||||
|
"""
|
||||||
|
Writes a single byte to UPDI
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param value: value to write
|
||||||
|
"""
|
||||||
|
return self.datalink.st(address, value)
|
||||||
|
*/
|
||||||
|
return updi_link_st(pgm, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_data(self, address, size):
|
||||||
|
"""
|
||||||
|
Reads a number of bytes of data from UPDI
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param size: number of bytes to read
|
||||||
|
"""
|
||||||
|
self.logger.debug("Reading %d bytes from 0x%04X", size, address)
|
||||||
|
# Range check
|
||||||
|
if size > constants.UPDI_MAX_REPEAT_SIZE:
|
||||||
|
raise PymcuprogError("Cant read that many bytes in one go")
|
||||||
|
|
||||||
|
# Store the address
|
||||||
|
self.datalink.st_ptr(address)
|
||||||
|
|
||||||
|
# Fire up the repeat
|
||||||
|
if size > 1:
|
||||||
|
self.datalink.repeat(size)
|
||||||
|
|
||||||
|
# Do the read(s)
|
||||||
|
return self.datalink.ld_ptr_inc(size)
|
||||||
|
*/
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X\n", progname, size, address);
|
||||||
|
|
||||||
|
if (size > UPDI_MAX_REPEAT_SIZE) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Can't read that many bytes in one go\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_st_ptr(pgm, address) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 1) {
|
||||||
|
if (updi_link_repeat(pgm, size) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updi_link_ld_ptr_inc(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_data(self, address, data):
|
||||||
|
"""
|
||||||
|
Writes a number of bytes to memory
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param data: data to write
|
||||||
|
"""
|
||||||
|
# Special case of 1 byte
|
||||||
|
if len(data) == 1:
|
||||||
|
return self.datalink.st(address, data[0])
|
||||||
|
# Special case of 2 byte
|
||||||
|
if len(data) == 2:
|
||||||
|
self.datalink.st(address, data[0])
|
||||||
|
return self.datalink.st(address + 1, data[1])
|
||||||
|
|
||||||
|
# Range check
|
||||||
|
if len(data) > constants.UPDI_MAX_REPEAT_SIZE:
|
||||||
|
raise PymcuprogError("Invalid length")
|
||||||
|
|
||||||
|
# Store the address
|
||||||
|
self.datalink.st_ptr(address)
|
||||||
|
|
||||||
|
# Fire up the repeat
|
||||||
|
self.datalink.repeat(len(data))
|
||||||
|
return self.datalink.st_ptr_inc(data)
|
||||||
|
*/
|
||||||
|
if (size == 1) {
|
||||||
|
return updi_link_st(pgm, address, buffer[0]);
|
||||||
|
}
|
||||||
|
if (size == 2) {
|
||||||
|
if (updi_link_st(pgm, address, buffer[0]) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_link_st(pgm, address+1, buffer[1]);
|
||||||
|
}
|
||||||
|
if (size > UPDI_MAX_REPEAT_SIZE) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Invalid length\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_link_st_ptr(pgm, address) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_link_repeat(pgm, size) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_link_st_ptr_inc(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def read_data_words(self, address, words):
|
||||||
|
"""
|
||||||
|
Reads a number of words of data from UPDI
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param words: number of words to read
|
||||||
|
"""
|
||||||
|
self.logger.debug("Reading %d words from 0x%04X", words, address)
|
||||||
|
|
||||||
|
# Range check
|
||||||
|
if words > constants.UPDI_MAX_REPEAT_SIZE >> 1:
|
||||||
|
raise PymcuprogError("Cant read that many words in one go")
|
||||||
|
|
||||||
|
# Store the address
|
||||||
|
self.datalink.st_ptr(address)
|
||||||
|
|
||||||
|
# Fire up the repeat
|
||||||
|
if words > 1:
|
||||||
|
self.datalink.repeat(words)
|
||||||
|
|
||||||
|
# Do the read
|
||||||
|
return self.datalink.ld_ptr_inc16(words)
|
||||||
|
*/
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Reading %d words from 0x%06X", progname, size, address);
|
||||||
|
|
||||||
|
if (size > (UPDI_MAX_REPEAT_SIZE >> 1)) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Can't read that many words in one go\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updi_link_st_ptr(pgm, address) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 1) {
|
||||||
|
if (updi_link_repeat(pgm, size) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updi_link_ld_ptr_inc16(pgm, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
def write_data_words(self, address, data):
|
||||||
|
"""
|
||||||
|
Writes a number of words to memory
|
||||||
|
|
||||||
|
:param address: address to write to
|
||||||
|
:param data: data to write
|
||||||
|
"""
|
||||||
|
# Special-case of 1 word
|
||||||
|
if len(data) == 2:
|
||||||
|
value = data[0] + (data[1] << 8)
|
||||||
|
return self.datalink.st16(address, value)
|
||||||
|
|
||||||
|
# Range check
|
||||||
|
if len(data) > constants.UPDI_MAX_REPEAT_SIZE << 1:
|
||||||
|
raise PymcuprogError("Invalid length")
|
||||||
|
|
||||||
|
# Store the address
|
||||||
|
self.datalink.st_ptr(address)
|
||||||
|
|
||||||
|
# Fire up the repeat
|
||||||
|
self.datalink.repeat(len(data) >> 1)
|
||||||
|
return self.datalink.st_ptr_inc16(data)
|
||||||
|
*/
|
||||||
|
if (size == 2) {
|
||||||
|
return updi_link_st16(pgm, address, buffer[0] + (buffer[1] << 8));
|
||||||
|
}
|
||||||
|
if (size > UPDI_MAX_REPEAT_SIZE << 1) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: Invalid length\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (updi_link_st_ptr(pgm, address) < 0) {
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return updi_link_st_ptr_inc16_RSD(pgm, buffer, size >> 1, -1);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef updi_readwrite_h
|
||||||
|
#define updi_readwrite_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value);
|
||||||
|
int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value);
|
||||||
|
int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size);
|
||||||
|
int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size);
|
||||||
|
int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value);
|
||||||
|
int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value);
|
||||||
|
int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size);
|
||||||
|
int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size);
|
||||||
|
int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size);
|
||||||
|
int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* updi_readwrite_h */
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
#include "updi_state.h"
|
||||||
|
|
||||||
|
updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
return &((updi_state *)(pgm->cookie))->sib_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
return ((updi_state *)(pgm->cookie))->datalink_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode)
|
||||||
|
{
|
||||||
|
((updi_state *)(pgm->cookie))->datalink_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm)
|
||||||
|
{
|
||||||
|
return ((updi_state *)(pgm->cookie))->nvm_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode)
|
||||||
|
{
|
||||||
|
((updi_state *)(pgm->cookie))->nvm_mode = mode;
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2021 Dawid Buchwald
|
||||||
|
*
|
||||||
|
* 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$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on pymcuprog
|
||||||
|
* See https://github.com/microchip-pic-avr-tools/pymcuprog
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef updi_state_h
|
||||||
|
#define updi_state_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
UPDI_LINK_MODE_16BIT,
|
||||||
|
UPDI_LINK_MODE_24BIT
|
||||||
|
} updi_datalink_mode;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
UPDI_NVM_MODE_V0,
|
||||||
|
UPDI_NVM_MODE_V2,
|
||||||
|
UPDI_NVM_MODE_V3
|
||||||
|
} updi_nvm_mode;
|
||||||
|
|
||||||
|
#define SIB_INFO_STRING_LENGTH 32
|
||||||
|
#define SIB_INFO_FAMILY_LENGTH 8
|
||||||
|
#define SIB_INFO_NVM_LENGTH 3
|
||||||
|
#define SIB_INFO_DEBUG_LENGTH 3
|
||||||
|
#define SIB_INFO_PDI_LENGTH 4
|
||||||
|
#define SIB_INFO_EXTRA_LENGTH 20
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char sib_string[SIB_INFO_STRING_LENGTH+1];
|
||||||
|
char family_string[SIB_INFO_FAMILY_LENGTH+1];
|
||||||
|
char nvm_string[SIB_INFO_NVM_LENGTH+1];
|
||||||
|
char debug_string[SIB_INFO_DEBUG_LENGTH+1];
|
||||||
|
char pdi_string[SIB_INFO_PDI_LENGTH+1];
|
||||||
|
char extra_string[SIB_INFO_EXTRA_LENGTH+1];
|
||||||
|
char nvm_version;
|
||||||
|
char debug_version;
|
||||||
|
} updi_sib_info;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
updi_sib_info sib_info;
|
||||||
|
updi_datalink_mode datalink_mode;
|
||||||
|
updi_nvm_mode nvm_mode;
|
||||||
|
} updi_state;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm);
|
||||||
|
updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm);
|
||||||
|
void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode);
|
||||||
|
updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm);
|
||||||
|
void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* updi_state_h */
|
|
@ -150,7 +150,8 @@ static int wiring_open(PROGRAMMER * pgm, char * port)
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
|
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.baud = pgm->baudrate ? pgm->baudrate: 115200;
|
pinfo.serialinfo.baud = pgm->baudrate ? pgm->baudrate: 115200;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
serial_open(port, pinfo, &pgm->fd);
|
serial_open(port, pinfo, &pgm->fd);
|
||||||
|
|
||||||
/* If we have a snoozetime, then we wait and do NOT toggle DTR/RTS */
|
/* If we have a snoozetime, then we wait and do NOT toggle DTR/RTS */
|
||||||
|
|
12
src/xbee.c
12
src/xbee.c
|
@ -1198,7 +1198,7 @@ static int xbeedev_open(char *port, union pinfo pinfo,
|
||||||
(unsigned int)xbs->xbee_address[6],
|
(unsigned int)xbs->xbee_address[6],
|
||||||
(unsigned int)xbs->xbee_address[7]);
|
(unsigned int)xbs->xbee_address[7]);
|
||||||
|
|
||||||
if (pinfo.baud) {
|
if (pinfo.serialinfo.baud) {
|
||||||
/*
|
/*
|
||||||
* User supplied the correct baud rate.
|
* User supplied the correct baud rate.
|
||||||
*/
|
*/
|
||||||
|
@ -1222,7 +1222,7 @@ static int xbeedev_open(char *port, union pinfo pinfo,
|
||||||
* plugged in. The doubled clock rate means a doubled serial
|
* plugged in. The doubled clock rate means a doubled serial
|
||||||
* rate. Double 9600 baud == 19200 baud.
|
* rate. Double 9600 baud == 19200 baud.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 19200;
|
pinfo.serialinfo.baud = 19200;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* In normal mode, default to 9600.
|
* In normal mode, default to 9600.
|
||||||
|
@ -1234,10 +1234,11 @@ static int xbeedev_open(char *port, union pinfo pinfo,
|
||||||
* XBee baud rate we should select. The baud rate of the AVR
|
* XBee baud rate we should select. The baud rate of the AVR
|
||||||
* device is irrelevant.
|
* device is irrelevant.
|
||||||
*/
|
*/
|
||||||
pinfo.baud = 9600;
|
pinfo.serialinfo.baud = 9600;
|
||||||
}
|
}
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
avrdude_message(MSG_NOTICE, "%s: Baud %ld\n", progname, (long)pinfo.baud);
|
avrdude_message(MSG_NOTICE, "%s: Baud %ld\n", progname, (long)pinfo.serialinfo.baud);
|
||||||
|
|
||||||
{
|
{
|
||||||
const int rc = xbs->serialDevice->open(tty, pinfo,
|
const int rc = xbs->serialDevice->open(tty, pinfo,
|
||||||
|
@ -1640,7 +1641,8 @@ static int xbee_open(PROGRAMMER *pgm, char *port)
|
||||||
{
|
{
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.baud = pgm->baudrate;
|
pinfo.serialinfo.baud = pgm->baudrate;
|
||||||
|
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||||
|
|
||||||
/* Wireless is lossier than normal serial */
|
/* Wireless is lossier than normal serial */
|
||||||
serial_recv_timeout = 1000;
|
serial_recv_timeout = 1000;
|
||||||
|
|
Loading…
Reference in New Issue