Merge pull request #772 from dbuchwald/main

SerialUPDI implementation - release candidate 1
This commit is contained in:
Jörg Wunsch 2021-12-22 20:01:35 +01:00 committed by GitHub
commit 2c0ccfed3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 4149 additions and 46 deletions

View File

@ -166,6 +166,17 @@ libavrdude_a_SOURCES = \
tpi.h \
usbasp.c \
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 \
usb_hidapi.c \
usb_libusb.c \

View File

@ -84,7 +84,8 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
{
union pinfo pinfo;
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) {
return -1;
}

View File

@ -370,7 +370,8 @@ static int avr910_open(PROGRAMMER * pgm, char * 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) {
return -1;
}

View File

@ -613,6 +613,13 @@ programmer
reset = 3; # TMS 7
;
programmer
id = "serialupdi";
desc = "SerialUPDI";
type = "serialupdi";
connection_type = serial;
;
programmer
id = "avrisp";
desc = "Atmel AVR ISP";
@ -16555,6 +16562,13 @@ part
readsize = 0x4;
;
memory "userrow"
size = 0x20;
offset = 0x1080;
page_size = 0x20;
readsize = 0x20;
;
memory "data"
# SRAM, only used to supply the offset
offset = 0x1000000;

View File

@ -427,7 +427,8 @@ static int buspirate_open(struct programmer_t *pgm, char * port)
if(pgm->baudrate == 0)
pgm->baudrate = 115200;
pinfo.baud = pgm->baudrate;
pinfo.serialinfo.baud = pgm->baudrate;
pinfo.serialinfo.cflags = SERIAL_8N1;
strcpy(pgm->port, port);
if (serial_open(port, pinfo, &pgm->fd)==-1) {
return -1;

View File

@ -391,7 +391,8 @@ static int butterfly_open(PROGRAMMER * pgm, char * port)
if(pgm->baudrate == 0) {
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) {
return -1;
}

View File

@ -553,7 +553,7 @@ static int jtagmkI_initialize(PROGRAMMER * pgm, AVRPART * p)
progname, pgm->baudrate);
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
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++) {
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",
progname, pinfo.baud);
progname, pinfo.serialinfo.baud);
if (serial_open(port, pinfo, &pgm->fd)==-1) {
return -1;
}
@ -697,7 +698,7 @@ static void jtagmkI_close(PROGRAMMER * pgm)
"trying to set baudrate to %d\n",
progname, PDATA(pgm)->initial_baudrate);
if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) {
serial_setspeed(&pgm->fd, pgm->baudrate);
serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1);
}
}
}

View File

@ -1312,7 +1312,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p)
"trying to set baudrate to %d\n",
progname, pgm->baudrate);
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) {
@ -1490,7 +1490,8 @@ static int jtagmkII_open(PROGRAMMER * pgm, char * port)
* a higher baud rate, we switch to it later on, after establishing
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -3330,7 +3336,8 @@ static int jtagmkII_open32(PROGRAMMER * pgm, char * port)
* a higher baud rate, we switch to it later on, after establishing
* 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

View File

@ -530,9 +530,34 @@ union filedescriptor
} 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
{
long baud;
struct {
long baud;
unsigned long cflags;
} serialinfo;
struct
{
unsigned short vid;
@ -548,7 +573,7 @@ struct serial_device
{
// open should return -1 on error, other values on success
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);
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;
#define serial_open (serdev->open)
#define serial_setspeed (serdev->setspeed)
#define serial_setparams (serdev->setparams)
#define serial_close (serdev->close)
#define serial_send (serdev->send)
#define serial_recv (serdev->recv)

View File

@ -45,6 +45,7 @@
#include "pickit2.h"
#include "ppi.h"
#include "serbb.h"
#include "serialupdi.h"
#include "stk500.h"
#include "stk500generic.h"
#include "stk500v2.h"
@ -87,6 +88,7 @@ const PROGRAMMER_TYPE programmers_types[] = {
{"par", par_initpgm, par_desc},
{"pickit2", pickit2_initpgm, pickit2_desc},
{"serbb", serbb_initpgm, serbb_desc},
{"serialupdi", serialupdi_initpgm, serialupdi_desc},
{"stk500", stk500_initpgm, stk500_desc},
{"stk500generic", stk500generic_initpgm, stk500generic_desc},
{"stk500v2", stk500v2_initpgm, stk500v2_desc},

View File

@ -55,6 +55,8 @@ struct baud_mapping {
/* There are a lot more baud rates we could handle, but what's the point? */
static struct baud_mapping baud_lookup_table [] = {
{ 300, B300 },
{ 600, B600 },
{ 1200, B1200 },
{ 2400, B2400 },
{ 4800, B4800 },
@ -96,7 +98,20 @@ static speed_t serial_baud_lookup(long 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;
struct termios termios;
@ -110,7 +125,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
*/
rc = tcgetattr(fd->ifd, &termios);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcgetattr() failed",
avrdude_message(MSG_INFO, "%s: ser_setparams(): tcgetattr() failed",
progname);
return -errno;
}
@ -125,7 +140,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
termios.c_iflag = IGNBRK;
termios.c_oflag = 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[VTIME] = 0;
@ -134,7 +149,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
rc = tcsetattr(fd->ifd, TCSANOW, &termios);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcsetattr() failed\n",
avrdude_message(MSG_INFO, "%s: ser_setparams(): tcsetattr() failed\n",
progname);
return -errno;
}
@ -298,7 +313,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
/*
* set serial line attributes
*/
rc = ser_setspeed(fdp, pinfo.baud);
rc = ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags);
if (rc) {
avrdude_message(MSG_INFO, "%s: ser_open(): can't set attributes for device \"%s\": %s\n",
progname, port, strerror(-rc));
@ -308,7 +323,6 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
return 0;
}
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 =
{
.open = ser_open,
.setspeed = ser_setspeed,
.setparams = ser_setparams,
.close = ser_close,
.send = ser_send,
.recv = ser_recv,

View File

@ -54,6 +54,8 @@ static unsigned char serial_over_ethernet = 0;
/* HANDLE hComPort=INVALID_HANDLE_VALUE; */
static struct baud_mapping baud_lookup_table [] = {
{ 300, CBR_300 },
{ 600, CBR_600 },
{ 1200, CBR_1200 },
{ 2400, CBR_2400 },
{ 4800, CBR_4800 },
@ -97,7 +99,7 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
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) {
return -ENOTTY;
@ -111,9 +113,39 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
dcb.fBinary = 1;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
switch ((cflags & (SERIAL_CS5 | SERIAL_CS6 | SERIAL_CS7 | SERIAL_CS8))) {
case SERIAL_CS5:
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))
return -1;
@ -283,7 +315,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
}
fdp->pfd = (void *)hComPort;
if (ser_setspeed(fdp, pinfo.baud) != 0)
if (ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags) != 0)
{
CloseHandle(hComPort);
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 =
{
.open = ser_open,
.setspeed = ser_setspeed,
.setparams = ser_setparams,
.close = ser_close,
.send = ser_send,
.recv = ser_recv,

965
src/serialupdi.c Normal file
View File

@ -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";

43
src/serialupdi.h Normal file
View File

@ -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 */

View File

@ -657,7 +657,8 @@ static int stk500_open(PROGRAMMER * pgm, char * port)
{
union pinfo pinfo;
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) {
return -1;
}

View File

@ -1603,12 +1603,12 @@ static void stk500v2_enable(PROGRAMMER * pgm)
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");
if (pgm->baudrate)
pinfo.baud = pgm->baudrate;
pinfo.serialinfo.baud = pgm->baudrate;
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)
{
union pinfo pinfo = { .baud = 115200 };
union pinfo pinfo = { .serialinfo.baud = 115200, .serialinfo.cflags = SERIAL_8N1 };
DEBUG("STK500V2: stk600_open()\n");
if (pgm->baudrate)
pinfo.baud = pgm->baudrate;
pinfo.serialinfo.baud = pgm->baudrate;
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
* 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
@ -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
* 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
@ -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
* 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

156
src/updi_constants.h Normal file
View File

@ -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 */

930
src/updi_link.c Normal file
View File

@ -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;
}

59
src/updi_link.h Normal file
View File

@ -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 */

1275
src/updi_nvm.c Normal file

File diff suppressed because it is too large Load Diff

51
src/updi_nvm.h Normal file
View File

@ -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 */

316
src/updi_readwrite.c Normal file
View File

@ -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);
}

51
src/updi_readwrite.h Normal file
View File

@ -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 */

55
src/updi_state.c Normal file
View File

@ -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;
}

85
src/updi_state.h Normal file
View File

@ -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 */

View File

@ -150,7 +150,8 @@ static int wiring_open(PROGRAMMER * pgm, char * port)
union pinfo pinfo;
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);
/* If we have a snoozetime, then we wait and do NOT toggle DTR/RTS */

View File

@ -1198,7 +1198,7 @@ static int xbeedev_open(char *port, union pinfo pinfo,
(unsigned int)xbs->xbee_address[6],
(unsigned int)xbs->xbee_address[7]);
if (pinfo.baud) {
if (pinfo.serialinfo.baud) {
/*
* 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
* rate. Double 9600 baud == 19200 baud.
*/
pinfo.baud = 19200;
pinfo.serialinfo.baud = 19200;
} else {
/*
* 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
* 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,
@ -1640,7 +1641,8 @@ static int xbee_open(PROGRAMMER *pgm, char *port)
{
union pinfo pinfo;
strcpy(pgm->port, port);
pinfo.baud = pgm->baudrate;
pinfo.serialinfo.baud = pgm->baudrate;
pinfo.serialinfo.cflags = SERIAL_8N1;
/* Wireless is lossier than normal serial */
serial_recv_timeout = 1000;