From 0a335e2f73ac24d14ab4df731712346aa0922883 Mon Sep 17 00:00:00 2001 From: MCUdude Date: Tue, 21 Dec 2021 22:10:51 +0100 Subject: [PATCH 01/17] Add jtag2updi programmer --- src/avrdude.conf.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/avrdude.conf.in b/src/avrdude.conf.in index 523137fd..0e4b451c 100644 --- a/src/avrdude.conf.in +++ b/src/avrdude.conf.in @@ -1647,6 +1647,17 @@ programmer miso = ~8; ; +# JTAG2UPDI +# https://github.com/ElTangas/jtag2updi + +programmer + id = "jtag2updi"; + desc = "JTAGv2 to UPDI bridge"; + type = "jtagmkii_pdi"; + connection_type = serial; + baudrate = 115200; +; + # # PART DEFINITIONS # From 71d5dbec48d9ec132aca7a85187191623ac6ef9e Mon Sep 17 00:00:00 2001 From: MCUdude Date: Tue, 21 Dec 2021 23:22:06 +0100 Subject: [PATCH 02/17] Add more jtagmkii baud rates --- src/jtagmkII.c | 41 ++++++++++++++++++++++++++++++++--------- src/jtagmkII_private.h | 23 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/jtagmkII.c b/src/jtagmkII.c index be6a4ed6..38eba40d 100644 --- a/src/jtagmkII.c +++ b/src/jtagmkII.c @@ -1254,15 +1254,38 @@ static unsigned char jtagmkII_get_baud(long baud) long baud; unsigned char val; } baudtab[] = { - { 2400L, PAR_BAUD_2400 }, - { 4800L, PAR_BAUD_4800 }, - { 9600L, PAR_BAUD_9600 }, - { 19200L, PAR_BAUD_19200 }, - { 38400L, PAR_BAUD_38400 }, - { 57600L, PAR_BAUD_57600 }, - { 115200L, PAR_BAUD_115200 }, - { 14400L, PAR_BAUD_14400 }, - }; + { 2400L, PAR_BAUD_2400 }, + { 4800L, PAR_BAUD_4800 }, + { 9600L, PAR_BAUD_9600 }, + { 19200L, PAR_BAUD_19200 }, + { 38400L, PAR_BAUD_38400 }, + { 57600L, PAR_BAUD_57600 }, + { 115200L, PAR_BAUD_115200 }, + { 14400L, PAR_BAUD_14400 }, + /* Extension to jtagmkII protocol: extra baud rates, standard series. */ + { 153600L, PAR_BAUD_153600 }, + { 230400L, PAR_BAUD_230400 }, + { 460800L, PAR_BAUD_460800 }, + { 921600L, PAR_BAUD_921600 }, + /* Extension to jtagmkII protocol: extra baud rates, binary series. */ + { 128000L, PAR_BAUD_128000 }, + { 256000L, PAR_BAUD_256000 }, + { 512000L, PAR_BAUD_512000 }, + { 1024000L, PAR_BAUD_1024000 }, + /* Extension to jtagmkII protocol: extra baud rates, decimal series. */ + { 150000L, PAR_BAUD_150000 }, + { 200000L, PAR_BAUD_200000 }, + { 250000L, PAR_BAUD_250000 }, + { 300000L, PAR_BAUD_300000 }, + { 400000L, PAR_BAUD_400000 }, + { 500000L, PAR_BAUD_500000 }, + { 600000L, PAR_BAUD_600000 }, + { 666666L, PAR_BAUD_666666 }, + { 1000000L, PAR_BAUD_1000000 }, + { 1500000L, PAR_BAUD_1500000 }, + { 2000000L, PAR_BAUD_2000000 }, + { 3000000L, PAR_BAUD_3000000 }, +}; int i; for (i = 0; i < sizeof baudtab / sizeof baudtab[0]; i++) diff --git a/src/jtagmkII_private.h b/src/jtagmkII_private.h index 6df8f6f2..14860854 100644 --- a/src/jtagmkII_private.h +++ b/src/jtagmkII_private.h @@ -206,6 +206,29 @@ # define PAR_BAUD_57600 0x06 # define PAR_BAUD_115200 0x07 # define PAR_BAUD_14400 0x08 +/* Extension to jtagmkII protocol: extra baud rates, standard series. */ +# define PAR_BAUD_153600 0x09 +# define PAR_BAUD_230400 0x0A +# define PAR_BAUD_460800 0x0B +# define PAR_BAUD_921600 0x0C +/* Extension to jtagmkII protocol: extra baud rates, binary series. */ +# define PAR_BAUD_128000 0x0D +# define PAR_BAUD_256000 0x0E +# define PAR_BAUD_512000 0x0F +# define PAR_BAUD_1024000 0x10 +/* Extension to jtagmkII protocol: extra baud rates, decimal series. */ +# define PAR_BAUD_150000 0x11 +# define PAR_BAUD_200000 0x12 +# define PAR_BAUD_250000 0x13 +# define PAR_BAUD_300000 0x14 +# define PAR_BAUD_400000 0x15 +# define PAR_BAUD_500000 0x16 +# define PAR_BAUD_600000 0x17 +# define PAR_BAUD_666666 0x18 +# define PAR_BAUD_1000000 0x19 +# define PAR_BAUD_1500000 0x1A +# define PAR_BAUD_2000000 0x1B +# define PAR_BAUD_3000000 0x1C #define PAR_OCD_VTARGET 0x06 #define PAR_OCD_JTAG_CLK 0x07 #define PAR_OCD_BREAK_CAUSE 0x08 From a2a276a8cc42d85267c1d1733d512975101b799b Mon Sep 17 00:00:00 2001 From: MCUdude Date: Wed, 29 Dec 2021 13:32:56 +0100 Subject: [PATCH 03/17] Add support for UPDI devices though jtag2updi 'Hack' borrowed from https://github.com/facchinm/avrdude --- src/jtagmkII.c | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/jtagmkII.c b/src/jtagmkII.c index 38eba40d..263a1f6b 100644 --- a/src/jtagmkII.c +++ b/src/jtagmkII.c @@ -891,7 +891,7 @@ static int jtagmkII_chip_erase(PROGRAMMER * pgm, AVRPART * p) int status, len; unsigned char buf[6], *resp, c; - if (p->flags & AVRPART_HAS_PDI) { + if (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) { buf[0] = CMND_XMEGA_ERASE; buf[1] = XMEGA_ERASE_CHIP; memset(buf + 2, 0, 4); /* address of area to be erased */ @@ -902,7 +902,7 @@ static int jtagmkII_chip_erase(PROGRAMMER * pgm, AVRPART * p) } avrdude_message(MSG_NOTICE2, "%s: jtagmkII_chip_erase(): Sending %schip erase command: ", progname, - (p->flags & AVRPART_HAS_PDI)? "Xmega ": ""); + (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))? "Xmega ": ""); jtagmkII_send(pgm, buf, len); status = jtagmkII_recv(pgm, &resp); @@ -928,7 +928,7 @@ static int jtagmkII_chip_erase(PROGRAMMER * pgm, AVRPART * p) return -1; } - if (!(p->flags & AVRPART_HAS_PDI)) + if (!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) pgm->initialize(pgm, p); return 0; @@ -986,7 +986,7 @@ static void jtagmkII_set_devdescr(PROGRAMMER * pgm, AVRPART * p) } } sendbuf.dd.ucCacheType = - (p->flags & AVRPART_HAS_PDI)? 0x02 /* ATxmega */: 0x00; + (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))? 0x02 /* ATxmega */: 0x00; avrdude_message(MSG_NOTICE2, "%s: jtagmkII_set_devdescr(): " "Sending set device descriptor command: ", @@ -1312,7 +1312,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) ok = 1; } else if (pgm->flag & PGM_FL_IS_PDI) { ifname = "PDI"; - if (p->flags & AVRPART_HAS_PDI) + if (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) ok = 1; } else { ifname = "JTAG"; @@ -1358,20 +1358,20 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) * mode from JTAG to JTAG_XMEGA. */ if ((pgm->flag & PGM_FL_IS_JTAG) && - (p->flags & AVRPART_HAS_PDI)) { + (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) { if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG_XMEGA) < 0) return -1; } /* * Must set the device descriptor before entering programming mode. */ - if (PDATA(pgm)->fwver >= 0x700 && (p->flags & AVRPART_HAS_PDI) != 0) + if (PDATA(pgm)->fwver >= 0x700 && (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) != 0) jtagmkII_set_xmega_params(pgm, p); else jtagmkII_set_devdescr(pgm, p); PDATA(pgm)->boot_start = ULONG_MAX; - if ((p->flags & AVRPART_HAS_PDI)) { + if ((p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) { /* * Find out where the border between application and boot area * is. @@ -1411,7 +1411,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) } PDATA(pgm)->flash_pageaddr = PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; - if (PDATA(pgm)->fwver >= 0x700 && (p->flags & AVRPART_HAS_PDI)) { + if (PDATA(pgm)->fwver >= 0x700 && (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) { /* * Work around for * https://savannah.nongnu.org/bugs/index.php?37942 @@ -1428,7 +1428,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) return -1; } - if ((pgm->flag & PGM_FL_IS_JTAG) && !(p->flags & AVRPART_HAS_PDI)) { + if ((pgm->flag & PGM_FL_IS_JTAG) && !(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) { strcpy(hfuse.desc, "hfuse"); if (jtagmkII_read_byte(pgm, p, &hfuse, 1, &b) < 0) return -1; @@ -1902,7 +1902,7 @@ static int jtagmkII_page_erase(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, avrdude_message(MSG_NOTICE2, "%s: jtagmkII_page_erase(.., %s, 0x%x)\n", progname, m->desc, addr); - if (!(p->flags & AVRPART_HAS_PDI)) { + if (!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) { avrdude_message(MSG_INFO, "%s: jtagmkII_page_erase: not an Xmega device\n", progname); return -1; @@ -2016,7 +2016,7 @@ static int jtagmkII_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, if (strcmp(m->desc, "flash") == 0) { PDATA(pgm)->flash_pageaddr = (unsigned long)-1L; cmd[1] = jtagmkII_memtype(pgm, p, addr); - if (p->flags & AVRPART_HAS_PDI) + if (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) /* dynamically decide between flash/boot memtype */ dynamic_memtype = 1; } else if (strcmp(m->desc, "eeprom") == 0) { @@ -2035,18 +2035,18 @@ static int jtagmkII_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, free(cmd); return n_bytes; } - cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_EEPROM : MTYPE_EEPROM_PAGE; + cmd[1] = ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ? MTYPE_EEPROM : MTYPE_EEPROM_PAGE; PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; } else if ( ( strcmp(m->desc, "usersig") == 0 ) ) { cmd[1] = MTYPE_USERSIG; } else if ( ( strcmp(m->desc, "boot") == 0 ) ) { cmd[1] = MTYPE_BOOT_FLASH; - } else if ( p->flags & AVRPART_HAS_PDI ) { + } else if ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) { cmd[1] = MTYPE_FLASH; } else { cmd[1] = MTYPE_SPM; } - serial_recv_timeout = 100; + serial_recv_timeout = 200; for (; addr < maxaddr; addr += page_size) { if ((maxaddr - addr) < page_size) block_size = maxaddr - addr; @@ -2143,11 +2143,11 @@ static int jtagmkII_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, cmd[0] = CMND_READ_MEMORY; if (strcmp(m->desc, "flash") == 0) { cmd[1] = jtagmkII_memtype(pgm, p, addr); - if (p->flags & AVRPART_HAS_PDI) + if (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) /* dynamically decide between flash/boot memtype */ dynamic_memtype = 1; } else if (strcmp(m->desc, "eeprom") == 0) { - cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_EEPROM : MTYPE_EEPROM_PAGE; + cmd[1] = ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ? MTYPE_EEPROM : MTYPE_EEPROM_PAGE; if (pgm->flag & PGM_FL_IS_DW) return -1; } else if ( ( strcmp(m->desc, "prodsig") == 0 ) ) { @@ -2156,7 +2156,7 @@ static int jtagmkII_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, cmd[1] = MTYPE_USERSIG; } else if ( ( strcmp(m->desc, "boot") == 0 ) ) { cmd[1] = MTYPE_BOOT_FLASH; - } else if ( p->flags & AVRPART_HAS_PDI ) { + } else if ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) { cmd[1] = MTYPE_FLASH; } else { cmd[1] = MTYPE_SPM; @@ -2241,7 +2241,7 @@ static int jtagmkII_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, unsupp = 0; addr += mem->offset; - cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_FLASH : MTYPE_FLASH_PAGE; + cmd[1] = ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ? MTYPE_FLASH : MTYPE_FLASH_PAGE; if (strcmp(mem->desc, "flash") == 0 || strcmp(mem->desc, "application") == 0 || strcmp(mem->desc, "apptable") == 0 || @@ -2251,7 +2251,7 @@ static int jtagmkII_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, paddr_ptr = &PDATA(pgm)->flash_pageaddr; cache_ptr = PDATA(pgm)->flash_pagecache; } else if (strcmp(mem->desc, "eeprom") == 0) { - if ( (pgm->flag & PGM_FL_IS_DW) || ( p->flags & AVRPART_HAS_PDI ) ) { + if ( (pgm->flag & PGM_FL_IS_DW) || ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ) { /* debugWire cannot use page access for EEPROM */ cmd[1] = MTYPE_EEPROM; } else { @@ -2417,7 +2417,7 @@ static int jtagmkII_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, writedata = data; cmd[0] = CMND_WRITE_MEMORY; - cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_FLASH : MTYPE_SPM; + cmd[1] = ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ? MTYPE_FLASH : MTYPE_SPM; if (strcmp(mem->desc, "flash") == 0) { if ((addr & 1) == 1) { /* odd address = high byte */ @@ -2431,7 +2431,7 @@ static int jtagmkII_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, if (pgm->flag & PGM_FL_IS_DW) unsupp = 1; } else if (strcmp(mem->desc, "eeprom") == 0) { - cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_EEPROM_XMEGA: MTYPE_EEPROM; + cmd[1] = ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) ? MTYPE_EEPROM_XMEGA: MTYPE_EEPROM; need_progmode = 0; PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; } else if (strcmp(mem->desc, "lfuse") == 0) { @@ -2736,7 +2736,7 @@ static void jtagmkII_print_parms(PROGRAMMER * pgm) static unsigned char jtagmkII_memtype(PROGRAMMER * pgm, AVRPART * p, unsigned long addr) { - if ( p->flags & AVRPART_HAS_PDI ) { + if ( p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI) ) { if (addr >= PDATA(pgm)->boot_start) return MTYPE_BOOT_FLASH; else @@ -2752,7 +2752,7 @@ static unsigned int jtagmkII_memaddr(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, * Xmega devices handled by V7+ firmware don't want to be told their * m->offset within the write memory command. */ - if (PDATA(pgm)->fwver >= 0x700 && (p->flags & AVRPART_HAS_PDI) != 0) { + if (PDATA(pgm)->fwver >= 0x700 && (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI)) != 0) { if (addr >= PDATA(pgm)->boot_start) /* * all memories but "flash" are smaller than boot_start anyway, so @@ -4047,4 +4047,3 @@ void jtagmkII_dragon_pdi_initpgm(PROGRAMMER * pgm) pgm->page_size = 256; pgm->flag = PGM_FL_IS_PDI; } - From f5bec43812875c59ba9e504d8ced05f50f509a1a Mon Sep 17 00:00:00 2001 From: MCUdude Date: Wed, 29 Dec 2021 14:25:09 +0100 Subject: [PATCH 04/17] Add jtag2updi programmer to docs --- src/avrdude.1 | 7 +++++++ src/doc/avrdude.texi | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/avrdude.1 b/src/avrdude.1 index 940cd499..6c33f6d1 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -233,6 +233,13 @@ In a nutshell, this programmer consists of simple USB->UART adapter, diode and couple of resistors. It uses serial connection to provide UPDI interface. See the texinfo documentation for more details and known issues. .Pp +The jtag2updi programmer is supported, +and can program AVRs with a UPDI interface. +Jtag2updi is just a firmware that can be uploaded to an AVR, +which enables it to interface with avrdude using the jtagice mkii protocol +via a serial link. +.Li https://github.com/ElTangas/jtag2updi +.Pp Input files can be provided, and output files can be written in different file formats, such as raw binary files containing the data to download to the chip, Intel hex format, or Motorola S-record diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index c45f7e26..13008f33 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -315,6 +315,12 @@ In a nutshell, this programmer consists of simple USB->UART adapter, diode and couple of resistors. It uses serial connection to provide UPDI interface. @xref{SerialUPDI programmer} for more details and known issues. +The jtag2updi programmer is supported, +and can program AVRs with a UPDI interface. +Jtag2updi is just a firmware that can be uploaded to an AVR, +which enables it to interface with avrdude using the jtagice mkii protocol +via a serial link (@url{https://github.com/ElTangas/jtag2updi}). + @menu * History:: @end menu From b9f03b1377ceed439fe7422d752145a1555a6afd Mon Sep 17 00:00:00 2001 From: MCUdude Date: Wed, 29 Dec 2021 14:25:36 +0100 Subject: [PATCH 05/17] Fix typo in URL --- src/avrdude.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/avrdude.1 b/src/avrdude.1 index 6c33f6d1..f7f5bce2 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -228,7 +228,7 @@ utility, but it also contains some performance improvements included in Spence Kohde's .Em DxCore Arduino core -.Li https://github.com/SpenceKonde/DCore . +.Li https://github.com/SpenceKonde/DxCore . In a nutshell, this programmer consists of simple USB->UART adapter, diode and couple of resistors. It uses serial connection to provide UPDI interface. See the texinfo documentation for more details and known issues. From dcd5374ae932372ed5f79dce39d85190053cbc38 Mon Sep 17 00:00:00 2001 From: MCUdude Date: Sun, 2 Jan 2022 12:57:42 +0100 Subject: [PATCH 06/17] Print meaningful error if programmer doesn't support target --- src/jtagmkII.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/jtagmkII.c b/src/jtagmkII.c index 263a1f6b..f41d126b 100644 --- a/src/jtagmkII.c +++ b/src/jtagmkII.c @@ -1305,6 +1305,14 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) int ok; const char *ifname; + /* Abort and print error if programmer does not support the target microcontroller */ + if ((strncmp(ldata(lfirst(pgm->id)), "jtag2updi", strlen("jtag2updi")) == 0 && p->flags & AVRPART_HAS_PDI) || + (strncmp(ldata(lfirst(pgm->id)), "jtagmkII", strlen("jtagmkII")) == 0 && p->flags & AVRPART_HAS_UPDI)) { + avrdude_message(MSG_INFO, "Error: programmer %s does not support target %s\n\n", + ldata(lfirst(pgm->id)), p->desc); + return -1; + } + ok = 0; if (pgm->flag & PGM_FL_IS_DW) { ifname = "debugWire"; From f96b98e9dfb99e422eadef71c97b44d60dceeb35 Mon Sep 17 00:00:00 2001 From: MCUdude Date: Sun, 2 Jan 2022 19:20:05 +0100 Subject: [PATCH 07/17] Mute "flash and boot" warning if s UPDI programmer is used Currently, no UPDI compatible AVR has a dedicated boot section like the Xmegas do --- src/jtagmkII.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jtagmkII.c b/src/jtagmkII.c index f41d126b..a98fdf2a 100644 --- a/src/jtagmkII.c +++ b/src/jtagmkII.c @@ -1387,8 +1387,10 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) AVRMEM *bootmem = avr_locate_mem(p, "boot"); AVRMEM *flashmem = avr_locate_mem(p, "flash"); if (bootmem == NULL || flashmem == NULL) { - avrdude_message(MSG_INFO, "%s: jtagmkII_initialize(): Cannot locate \"flash\" and \"boot\" memories in description\n", - progname); + if (strncmp(ldata(lfirst(pgm->id)), "jtagmkII", strlen("jtagmkII")) == 0) { + avrdude_message(MSG_INFO, "%s: jtagmkII_initialize(): Cannot locate \"flash\" and \"boot\" memories in description\n", + progname); + } } else { if (PDATA(pgm)->fwver < 0x700) { /* V7+ firmware does not need this anymore */ From dc1fed40bfd9e12bcdb42e963b9d66f8ea052c5f Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Sun, 2 Jan 2022 20:56:13 +0100 Subject: [PATCH 08/17] avrftdi: don't use the deprecated ftdi_usb_purge_buffers routine Since commit ed46f09c1ccd1351e003a200ba50e3e4778ac478 (Implement tc[io]flush methods & deprecate broken purge_buffers methods.) ftdi_usb_purge_buffers() routine is deprecated. Use HAVE_FTDI_TCIOFLUSH macro to invoke the newly introduced ftdi_tcioflush() routine. --- src/CMakeLists.txt | 3 +++ src/avrftdi.c | 4 ++++ src/cmake_config.h.in | 3 +++ src/configure.ac | 2 ++ 4 files changed, 12 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89e92c0f..3e5ed177 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ option(DEBUG_CMAKE "Enable debugging output for this CMake project" OFF) include(CheckIncludeFile) include(CheckFunctionExists) +include(CheckSymbolExists) include(GNUInstallDirs) set(CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}") @@ -177,6 +178,8 @@ find_library(HAVE_LIBFTDI1 NAMES ${PREFERRED_LIBFTDI1}) if(HAVE_LIBFTDI1) set(LIB_LIBFTDI1 ${HAVE_LIBFTDI1}) set(HAVE_LIBFTDI_TYPE_232H 1) + set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIBFTDI1} ${LIB_LIBUSB} ${LIB_LIBUSB_1_0}) + check_symbol_exists(ftdi_tcioflush "libftdi1/ftdi.h" HAVE_FTDI_TCIOFLUSH) endif() # ------------------------------------- diff --git a/src/avrftdi.c b/src/avrftdi.c index 654614f6..e7437ddc 100644 --- a/src/avrftdi.c +++ b/src/avrftdi.c @@ -720,7 +720,11 @@ static int avrftdi_open(PROGRAMMER * pgm, char *port) /* set SPI mode */ E(ftdi_set_bitmode(pdata->ftdic, 0, BITMODE_RESET) < 0, pdata->ftdic); E(ftdi_set_bitmode(pdata->ftdic, pdata->pin_direction & 0xff, BITMODE_MPSSE) < 0, pdata->ftdic); +#ifdef HAVE_FTDI_TCIOFLUSH + E(ftdi_tcioflush(pdata->ftdic), pdata->ftdic); +#else E(ftdi_usb_purge_buffers(pdata->ftdic), pdata->ftdic); +#endif write_flush(pdata); diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 9643546e..e94dbf30 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -50,6 +50,9 @@ /* Define if lex/flex has yylex_destroy */ #cmakedefine HAVE_YYLEX_DESTROY 1 +/* Define if ftdi1 has 'ftdi_tcioflush' function. */ +#cmakedefine HAVE_FTDI_TCIOFLUSH 1 + /* ----- Libraries and Headers ----- */ /* Define to 1 if the system has the type `uint_t'. */ diff --git a/src/configure.ac b/src/configure.ac index df74ea14..32e54f9c 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -195,6 +195,8 @@ if test x$have_libftdi1 = xyes; then LIBFTDI1="-lftdi1" AC_DEFINE([HAVE_LIBFTDI1]) AC_SUBST(LIBFTDI1, $LIBFTDI1) + LIBS="${LIBFTDI1}" + AC_CHECK_FUNCS(ftdi_tcioflush) else if test x$have_libftdi = xyes; then LIBFTDI="-lftdi -lusb" From ecca860972b7ceee100e1849d849a04a630b3bd8 Mon Sep 17 00:00:00 2001 From: MCUdude Date: Mon, 3 Jan 2022 18:49:55 +0100 Subject: [PATCH 09/17] Add target voltage adjustment for Curiosity Nano boards in Avrdude terminal mode --- src/jtag3.c | 32 ++++++++++++++++++++++++++++++++ src/jtag3_private.h | 7 +++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/jtag3.c b/src/jtag3.c index 0c04426a..1681a001 100644 --- a/src/jtag3.c +++ b/src/jtag3.c @@ -2305,6 +2305,31 @@ int jtag3_read_sib(PROGRAMMER * pgm, AVRPART * p, char * sib) return 0; } +static int jtag3_set_vtarget(PROGRAMMER * pgm, double v) +{ + unsigned uaref, utarg; + unsigned char buf[2]; + + utarg = (unsigned)(v * 1000); + + if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_VTARGET, buf, 2) < 0) { + avrdude_message(MSG_INFO, "%s: jtag3_set_vtarget(): cannot obtain V[aref]\n", + progname); + return -1; + } + + uaref = b2_to_u16(buf); + u16_to_b2(buf, utarg); + + avrdude_message(MSG_INFO, "%s: jtag3_set_vtarget(): changing V[target] from %.1f to %.1f\n", + progname, uaref / 1000.0, v); + + if (jtag3_setparm(pgm, SCOPE_GENERAL, 1, PARM3_VADJUST, buf, sizeof(buf)) < 0) + return -1; + + return 0; +} + static void jtag3_display(PROGRAMMER * pgm, const char * p) { unsigned char parms[5]; @@ -2564,5 +2589,12 @@ void jtag3_updi_initpgm(PROGRAMMER * pgm) pgm->flag = PGM_FL_IS_UPDI; pgm->unlock = jtag3_unlock_erase_key; pgm->read_sib = jtag3_read_sib; + + /* + * enable target voltage adjustment for PKOB/nEDBG boards + */ + if (matches(ldata(lfirst(pgm->id)), "pkobn_updi")) { + pgm->set_vtarget = jtag3_set_vtarget; + } } diff --git a/src/jtag3_private.h b/src/jtag3_private.h index 5ce634f9..a3e7fb08 100644 --- a/src/jtag3_private.h +++ b/src/jtag3_private.h @@ -189,8 +189,11 @@ #define PARM3_FW_RELEASE 0x03 /* section 0, generic scope, 1 byte; * always asked for by Atmel Studio, * but never displayed there */ -#define PARM3_VTARGET 0x00 /* section 1, generic scope, 2 bytes, - * in millivolts */ +#define PARM3_VTARGET 0x00 /* section 1, generic scope, 2 bytes, in millivolts */ +#define PARM3_VBUF 0x01 /* section 1, generic scope, 2 bytes, bufferred target voltage reference */ +#define PARM3_VUSB 0x02 /* section 1, generic scope, 2 bytes, USB voltage */ +#define PARM3_VADJUST 0x20 /* section 1, generic scope, 2 bytes, set voltage */ + #define PARM3_DEVICEDESC 0x00 /* section 2, memory etc. configuration, * 31 bytes for tiny/mega AVR, 47 bytes * for Xmega; is also used in command From 9c7bb3787d8eedbe11a284cb0a35461a52d7b94c Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Tue, 4 Jan 2022 12:53:27 +0100 Subject: [PATCH 10/17] Revert "Hint about possibly differing licensing terms." This reverts commit fa079bec8c233bbec793b31d883eba698fff2cb3. Changing the COPYING file prevents automatic license detections from working correctly. --- COPYING | 3 --- 1 file changed, 3 deletions(-) diff --git a/COPYING b/COPYING index e6eecee0..e69cf7e6 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,3 @@ -(Note that individual files might have a different license than this -one, but this one is the overall project license.) - GNU GENERAL PUBLIC LICENSE Version 2, June 1991 From 863f77d8277b5947993ce88df6e1f8cb87796567 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Tue, 4 Jan 2022 13:48:11 +0100 Subject: [PATCH 11/17] Fix a (valid) warning about comparison of char vs. int against EOF Closes PR #796 --- src/buspirate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/buspirate.c b/src/buspirate.c index f2a6d9ec..84b2f042 100644 --- a/src/buspirate.c +++ b/src/buspirate.c @@ -178,6 +178,7 @@ static int buspirate_getc(struct programmer_t *pgm) static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size_t len) { char *buf_p; + int c; long orig_serial_recv_timeout = serial_recv_timeout; /* Static local buffer - this may come handy at times */ @@ -190,12 +191,12 @@ static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size buf_p = buf; memset(buf, 0, len); while (buf_p < (buf + len - 1)) { /* keep the very last byte == 0 */ - *buf_p = buspirate_getc(pgm); - if (*buf_p == '\r') + *buf_p = c = buspirate_getc(pgm); + if (c == '\r') continue; - if (*buf_p == '\n') + if (c == '\n') break; - if (*buf_p == EOF) { + if (c == EOF) { *buf_p = '\0'; break; } From dba89e726939326289219cc0e1bb8c242492de1b Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Tue, 4 Jan 2022 14:07:15 +0100 Subject: [PATCH 12/17] Mention PR #796 as fixed --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index f21626c1..b32b7345 100644 --- a/NEWS +++ b/NEWS @@ -47,6 +47,7 @@ Changes since version 6.4: - Use yacc/byacc as an alternative to bison, closes #785 #793 - Derive program version string from last commit #794 - Find 'avrdude.conf' based on absolute path to executable #780 + - buspirate: fix -Wtautological-constant-out-of-range-compare #796 * Internals: From 3747db516ad53d19631ef2a1159bb9f5fc19d88a Mon Sep 17 00:00:00 2001 From: Marius Greuel Date: Tue, 28 Dec 2021 11:55:12 +0100 Subject: [PATCH 13/17] Add support for Micronucleus bootloader --- src/CMakeLists.txt | 2 + src/Makefile.am | 2 + src/avrdude.1 | 19 + src/avrdude.conf.in | 9 + src/doc/avrdude.texi | 18 + src/micronucleus.c | 951 +++++++++++++++++++++++++++++++++++++++++++ src/micronucleus.h | 35 ++ src/pgm_type.c | 2 + 8 files changed, 1038 insertions(+) create mode 100644 src/micronucleus.c create mode 100644 src/micronucleus.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e0bc3a9..a89fb68a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -467,6 +467,8 @@ add_library(libavrdude STATIC linuxspi.h linux_ppdev.h lists.c + micronucleus.c + micronucleus.h my_ddk_hidsdi.h par.c par.h diff --git a/src/Makefile.am b/src/Makefile.am index 0abb8123..834f32fc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -136,6 +136,8 @@ libavrdude_a_SOURCES = \ linuxspi.h \ linux_ppdev.h \ lists.c \ + micronucleus.c \ + micronucleus.h \ my_ddk_hidsdi.h \ par.c \ par.h \ diff --git a/src/avrdude.1 b/src/avrdude.1 index 9805ac75..dec2f3b5 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -233,6 +233,15 @@ In a nutshell, this programmer consists of simple USB->UART adapter, diode and couple of resistors. It uses serial connection to provide UPDI interface. See the texinfo documentation for more details and known issues. .Pp +The Micronucleus bootloader is supported for both protocol version V1 +and V2. As the bootloader does not support reading from flash memory, +use the +.Fl V +option to prevent AVRDUDE from verifing the flash memory. +See the section on +.Em extended parameters +for Micronucleus specific options. +.Pp Input files can be provided, and output files can be written in different file formats, such as raw binary files containing the data to download to the chip, Intel hex format, or Motorola S-record @@ -1084,6 +1093,16 @@ Especially in ascii mode this happens very often, so setting a smaller value can speed up programming a lot. The default value is 100ms. Using 10ms might work in most cases. .El +.It Ar Micronucleus bootloader +.Bl -tag -offset indent -width indent +.It Ar wait[=] +If the device is not connected, wait for the device to be plugged in. +The optional +.Ar timeout +specifies the connection time-out in seconds. +If no time-out is specified, AVRDUDE will wait indefinitely until the +device is plugged in. +.El .It Ar Wiring When using the Wiring programmer type, the following optional extended parameter is accepted: diff --git a/src/avrdude.conf.in b/src/avrdude.conf.in index b6418742..4390708c 100644 --- a/src/avrdude.conf.in +++ b/src/avrdude.conf.in @@ -916,6 +916,15 @@ programmer usbpid = 0x0BA5; ; +programmer + id = "micronucleus"; + desc = "Micronucleus Bootloader"; + type = "micronucleus"; + connection_type = usb; + usbvid = 0x16D0; + usbpid = 0x0753; +; + # commercial version of USBtiny, using a separate VID/PID programmer id = "iseavrprog"; diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index 023e0206..85762839 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -315,6 +315,12 @@ In a nutshell, this programmer consists of simple USB->UART adapter, diode and couple of resistors. It uses serial connection to provide UPDI interface. @xref{SerialUPDI programmer} for more details and known issues. +The Micronucleus bootloader is supported for both protocol version V1 +and V2. As the bootloader does not support reading from flash memory, +use the @code{-V} option to prevent AVRDUDE from verifing the flash memory. +See the section on @emph{extended parameters} +below for Micronucleus specific options. + @menu * History:: @end menu @@ -968,6 +974,18 @@ The default value is 100ms. Using 10ms might work in most cases. @end table +@item Micronucleus bootloader + +When using the Micronucleus programmer type, the +following optional extended parameter is accepted: +@table @code +@item @samp{wait=@var{timeout}} +If the device is not connected, wait for the device to be plugged in. +The optional @var{timeout} specifies the connection time-out in seconds. +If no time-out is specified, AVRDUDE will wait indefinitely until the +device is plugged in. +@end table + @item Wiring When using the Wiring programmer type, the diff --git a/src/micronucleus.c b/src/micronucleus.c new file mode 100644 index 00000000..ef758078 --- /dev/null +++ b/src/micronucleus.c @@ -0,0 +1,951 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2019 Marius Greuel + * Portions Copyright (C) 2014 T. Bo"scke + * Portions Copyright (C) 2012 ihsan Kehribar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Notes: +// This file adds support for the Micronucleus bootloader V1 and V2, +// so you do no longer need the Micronucleus command-line utility. +// +// This bootloader is typically used on small ATtiny boards, +// such as Digispark (ATtiny85), Digispark Pro (ATtiny167), +// and the respective clones. +// By default, it bootloader uses the VID/PID 16d0:0753 (MCS Digistump). +// +// As the micronucleus bootloader is optimized for size, it implements +// writing to flash memory only. Since it does not support reading, +// use the -V option to prevent avrdude from verifing the flash memory. +// To have avrdude wait for the device to be connected, use the +// extended option '-x wait'. +// +// Example: +// avrdude -c micronucleus -p t85 -x wait -V -U flash:w:main.hex + +#include "ac_cfg.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "avrdude.h" +#include "micronucleus.h" +#include "usbdevs.h" + +#if defined(HAVE_LIBUSB) + +#if defined(HAVE_USB_H) +#include +#elif defined(HAVE_LUSB0_USB_H) +#include +#else +#error "libusb needs either or " +#endif + +//----------------------------------------------------------------------------- + +#define MICRONUCLEUS_VID 0x16D0 +#define MICRONUCLEUS_PID 0x0753 + +#define MICRONUCLEUS_CONNECT_WAIT 100 + +#define MICRONUCLEUS_CMD_INFO 0 +#define MICRONUCLEUS_CMD_TRANSFER 1 +#define MICRONUCLEUS_CMD_ERASE 2 +#define MICRONUCLEUS_CMD_PROGRAM 3 +#define MICRONUCLEUS_CMD_START 4 + +#define MICRONUCLEUS_DEFAULT_TIMEOUT 500 +#define MICRONUCLEUS_MAX_MAJOR_VERSION 2 + +#define PDATA(pgm) ((pdata_t*)(pgm->cookie)) + +//----------------------------------------------------------------------------- + +typedef struct pdata +{ + usb_dev_handle* usb_handle; + // Extended parameters + bool wait_until_device_present; + int wait_timout; // in seconds + // Bootloader version + uint8_t major_version; + uint8_t minor_version; + // Bootloader info (via USB request) + uint16_t flash_size; // programmable size (in bytes) of flash + uint8_t page_size; // size (in bytes) of page + uint8_t write_sleep; // milliseconds + uint8_t signature1; // only used in protocol v2 + uint8_t signature2; // only used in protocol v2 + // Calculated bootloader info + uint16_t pages; // total number of pages to program + uint16_t bootloader_start; // start of the bootloader (at page boundary) + uint16_t erase_sleep; // milliseconds + // State + uint16_t user_reset_vector; // reset vector of user program + bool write_last_page; // last page already programmed + bool start_program; // require start after flash +} pdata_t; + +//----------------------------------------------------------------------------- + +static void delay_ms(uint32_t duration) +{ + usleep(duration * 1000); +} + +static int micronucleus_check_connection(pdata_t* pdata) +{ + if (pdata->major_version >= 2) + { + uint8_t buffer[6] = { 0 }; + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT); + return result == sizeof(buffer) ? 0 : -1; + } + else + { + uint8_t buffer[4] = { 0 }; + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT); + return result == sizeof(buffer) ? 0 : -1; + } +} + +static int micronucleus_reconnect(pdata_t* pdata) +{ + struct usb_device* device = usb_device(pdata->usb_handle); + + usb_close(pdata->usb_handle); + pdata->usb_handle = NULL; + + for (int i = 0; i < 25; i++) + { + avrdude_message(MSG_NOTICE, "%s: Trying to reconnect...\n", progname); + + pdata->usb_handle = usb_open(device); + if (pdata->usb_handle != NULL) + return 0; + + delay_ms(MICRONUCLEUS_CONNECT_WAIT); + } + + return -1; +} + +static int micronucleus_get_bootloader_info_v1(pdata_t* pdata) +{ + uint8_t buffer[4] = { 0 }; + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: WARNING: Failed to get bootloader info block: %s\n", + progname, usb_strerror()); + return result; + } + else if (result < sizeof(buffer)) + { + avrdude_message(MSG_INFO, "%s: WARNING: Received invalid bootloader info block size: %d\n", + progname, result); + return -1; + } + + pdata->flash_size = (buffer[0] << 8) | buffer[1]; + pdata->page_size = buffer[2]; + pdata->write_sleep = buffer[3] & 127; + + // Take a wild guess on the part ID, so that we can supply it for device verification + if (pdata->page_size == 128) + { + // ATtiny167 + pdata->signature1 = 0x94; + pdata->signature2 = 0x87; + } + else if (pdata->page_size == 64) + { + if (pdata->flash_size > 4096) + { + // ATtiny85 + pdata->signature1 = 0x93; + pdata->signature2 = 0x0B; + } + else + { + // ATtiny45 + pdata->signature1 = 0x92; + pdata->signature2 = 0x06; + } + } + else if (pdata->page_size == 16) + { + // ATtiny841 + pdata->signature1 = 0x93; + pdata->signature2 = 0x15; + } + else + { + // Unknown device + pdata->signature1 = 0; + pdata->signature2 = 0; + } + + pdata->pages = (pdata->flash_size + pdata->page_size - 1) / pdata->page_size; + pdata->bootloader_start = pdata->pages * pdata->page_size; + pdata->erase_sleep = pdata->write_sleep * pdata->pages; + + return 0; +} + +static int micronucleus_get_bootloader_info_v2(pdata_t* pdata) +{ + uint8_t buffer[6] = { 0 }; + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: WARNING: Failed to get bootloader info block: %s\n", + progname, usb_strerror()); + return result; + } + else if (result < sizeof(buffer)) + { + avrdude_message(MSG_INFO, "%s: WARNING: Received invalid bootloader info block size: %d\n", + progname, result); + return -1; + } + + pdata->flash_size = (buffer[0] << 8) + buffer[1]; + pdata->page_size = buffer[2]; + pdata->write_sleep = (buffer[3] & 127) + 2; + pdata->signature1 = buffer[4]; + pdata->signature2 = buffer[5]; + + pdata->pages = (pdata->flash_size + pdata->page_size - 1) / pdata->page_size; + pdata->bootloader_start = pdata->pages * pdata->page_size; + pdata->erase_sleep = pdata->write_sleep * pdata->pages; + + // if bit 7 of write sleep time is set, divide the erase time by four to + // accomodate to the 4*page erase of the ATtiny841/441 + if ((buffer[3] & 128) != 0) + { + pdata->erase_sleep /= 4; + } + + return 0; +} + +static int micronucleus_get_bootloader_info(pdata_t* pdata) +{ + if (pdata->major_version >= 2) + { + return micronucleus_get_bootloader_info_v2(pdata); + } + else + { + return micronucleus_get_bootloader_info_v1(pdata); + } +} + +static void micronucleus_dump_device_info(pdata_t* pdata) +{ + avrdude_message(MSG_NOTICE, "%s: Bootloader version: %d.%d\n", progname, pdata->major_version, pdata->minor_version); + avrdude_message(MSG_NOTICE, "%s: Available flash size: %u\n", progname, pdata->flash_size); + avrdude_message(MSG_NOTICE, "%s: Page size: %u\n", progname, pdata->page_size); + avrdude_message(MSG_NOTICE, "%s: Bootloader start: 0x%04X\n", progname, pdata->bootloader_start); + avrdude_message(MSG_NOTICE, "%s: Write sleep: %ums\n", progname, pdata->write_sleep); + avrdude_message(MSG_NOTICE, "%s: Erase sleep: %ums\n", progname, pdata->erase_sleep); + avrdude_message(MSG_NOTICE, "%s: Signature1: 0x%02X\n", progname, pdata->signature1); + avrdude_message(MSG_NOTICE, "%s: Signature2: 0x%02X\n", progname, pdata->signature2); +} + +static int micronucleus_erase_device(pdata_t* pdata) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_erase_device()\n", progname); + + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_ERASE, 0, 0, NULL, 0, MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + switch (result) + { + case -EIO: + case -EPIPE: + avrdude_message(MSG_NOTICE, "%s: Ignoring last error of erase command: %s\n", progname, usb_strerror()); + break; + default: + avrdude_message(MSG_INFO, "%s: WARNING: Failed is issue erase command, code %d: %s\n", progname, result, usb_strerror()); + return result; + } + } + + delay_ms(pdata->erase_sleep); + + result = micronucleus_check_connection(pdata); + if (result < 0) + { + avrdude_message(MSG_NOTICE, "%s: Connection dropped, trying to reconnect...\n", progname); + + result = micronucleus_reconnect(pdata); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: WARNING: Failed to reconnect USB device: %s\n", progname, usb_strerror()); + return result; + } + } + + return 0; +} + +static int micronucleus_patch_reset_vector(pdata_t* pdata, uint8_t* buffer) +{ + // Save user reset vector. + uint16_t word0 = (buffer[1] << 8) | buffer[0]; + uint16_t word1 = (buffer[3] << 8) | buffer[2]; + + if (word0 == 0x940C) + { + // long jump + pdata->user_reset_vector = word1; + } + else if ((word0 & 0xF000) == 0xC000) + { + // rjmp + pdata->user_reset_vector = (word0 & 0x0FFF) + 1; + } + else + { + avrdude_message(MSG_INFO, "%s: The reset vector of the user program does not contain a branch instruction.\n", progname); + return -1; + } + + // Patch in jmp to bootloader. + if (pdata->bootloader_start > 0x2000) + { + // jmp + uint16_t data = 0x940C; + buffer[0] = (uint8_t)(data >> 0); + buffer[1] = (uint8_t)(data >> 8); + buffer[2] = (uint8_t)(pdata->bootloader_start >> 0); + buffer[3] = (uint8_t)(pdata->bootloader_start >> 8); + } + else + { + // rjmp + uint16_t data = 0xC000 | ((pdata->bootloader_start / 2 - 1) & 0x0FFF); + buffer[0] = (uint8_t)(data >> 0); + buffer[1] = (uint8_t)(data >> 8); + } + + return 0; +} + +static void micronucleus_patch_user_vector(pdata_t* pdata, uint8_t* buffer) +{ + uint16_t user_reset_addr = pdata->bootloader_start - 4; + uint16_t address = pdata->bootloader_start - pdata->page_size; + if (user_reset_addr > 0x2000) + { + // jmp + uint16_t data = 0x940C; + buffer[user_reset_addr - address + 0] = (uint8_t)(data >> 0); + buffer[user_reset_addr - address + 1] = (uint8_t)(data >> 8); + buffer[user_reset_addr - address + 2] = (uint8_t)(pdata->user_reset_vector >> 0); + buffer[user_reset_addr - address + 3] = (uint8_t)(pdata->user_reset_vector >> 8); + } + else + { + // rjmp + uint16_t data = 0xC000 | ((pdata->user_reset_vector - user_reset_addr / 2 - 1) & 0x0FFF); + buffer[user_reset_addr - address + 0] = (uint8_t)(data >> 0); + buffer[user_reset_addr - address + 1] = (uint8_t)(data >> 8); + } +} + +static int micronucleus_write_page_v1(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size) +{ + int result = usb_control_msg(pdata->usb_handle, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_TRANSFER, + size, address, + buffer, size, + MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror()); + return result; + } + + return 0; +} + +static int micronucleus_write_page_v2(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size) +{ + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_TRANSFER, + size, address, + NULL, 0, + MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror()); + return result; + } + + for (int i = 0; i < size; i += 4) + { + int w1 = (buffer[i + 1] << 8) | (buffer[i + 0] << 0); + int w2 = (buffer[i + 3] << 8) | (buffer[i + 2] << 0); + result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_PROGRAM, + w1, w2, + NULL, 0, + MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror()); + return result; + } + } + + return 0; +} + +static int micronucleus_write_page(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_write_page(address=0x%04X, size=%d)\n", progname, address, size); + + if (address == 0) + { + if (pdata->major_version >= 2) + { + int result = micronucleus_patch_reset_vector(pdata, buffer); + if (result < 0) + { + return result; + } + } + + // Require last page (with application reset vector) to be written. + pdata->write_last_page = true; + + // Require software start. + pdata->start_program = true; + } + else if (address >= pdata->bootloader_start - pdata->page_size) + { + if (pdata->major_version >= 2) + { + micronucleus_patch_user_vector(pdata, buffer); + } + + // Mark last page as written. + pdata->write_last_page = false; + } + + int result; + if (pdata->major_version >= 2) + { + result = micronucleus_write_page_v2(pdata, address, buffer, size); + } + else + { + result = micronucleus_write_page_v1(pdata, address, buffer, size); + } + + if (result < 0) + { + return result; + } + + delay_ms(pdata->write_sleep); + + return 0; +} + +static int micronucleus_start(pdata_t* pdata) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_start()\n", progname); + + int result = usb_control_msg( + pdata->usb_handle, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MICRONUCLEUS_CMD_START, 0, 0, NULL, 0, MICRONUCLEUS_DEFAULT_TIMEOUT); + if (result < 0) + { + avrdude_message(MSG_INFO, "%s: WARNING: Failed is issue start command: %s\n", progname, usb_strerror()); + return result; + } + + return 0; +} + +//----------------------------------------------------------------------------- + +static void micronucleus_setup(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_setup()\n", progname); + + if ((pgm->cookie = malloc(sizeof(pdata_t))) == 0) + { + avrdude_message(MSG_INFO, "%s: micronucleus_setup(): Out of memory allocating private data\n", progname); + exit(1); + } + + memset(pgm->cookie, 0, sizeof(pdata_t)); +} + +static void micronucleus_teardown(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_teardown()\n", progname); + free(pgm->cookie); +} + +static int micronucleus_initialize(PROGRAMMER* pgm, AVRPART* p) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_initialize()\n", progname); + + pdata_t* pdata = PDATA(pgm); + + int result = micronucleus_get_bootloader_info(pdata); + if (result < 0) + return result; + + micronucleus_dump_device_info(pdata); + + return 0; +} + +static void micronucleus_display(PROGRAMMER* pgm, const char* prefix) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_display()\n", progname); +} + +static void micronucleus_powerup(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_powerup()\n", progname); +} + +static void micronucleus_powerdown(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_powerdown()\n", progname); + + pdata_t* pdata = PDATA(pgm); + if (pdata->write_last_page) + { + pdata->write_last_page = false; + + uint8_t* buffer = (unsigned char*)malloc(pdata->page_size); + if (buffer != NULL) + { + memset(buffer, 0xFF, pdata->page_size); + micronucleus_write_page(pdata, pdata->bootloader_start - pdata->page_size, buffer, pdata->page_size); + free(buffer); + } + } + + if (pdata->start_program) + { + pdata->start_program = false; + + micronucleus_start(pdata); + } +} + +static void micronucleus_enable(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_enable()\n", progname); +} + +static void micronucleus_disable(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_disable()\n", progname); +} + +static int micronucleus_program_enable(PROGRAMMER* pgm, AVRPART* p) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_program_enable()\n", progname); + return 0; +} + +static int micronucleus_read_sig_bytes(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_read_sig_bytes()\n", progname); + + if (mem->size < 3) + { + avrdude_message(MSG_INFO, "%s: memory size too small for read_sig_bytes", progname); + return -1; + } + + pdata_t* pdata = PDATA(pgm); + mem->buf[0] = 0x1E; + mem->buf[1] = pdata->signature1; + mem->buf[2] = pdata->signature2; + return 0; +} + +static int micronucleus_chip_erase(PROGRAMMER* pgm, AVRPART* p) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_chip_erase()\n", progname); + + pdata_t* pdata = PDATA(pgm); + return micronucleus_erase_device(pdata); +} + +static int micronucleus_open(PROGRAMMER* pgm, char* port) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_open(\"%s\")\n", progname, port); + + pdata_t* pdata = PDATA(pgm); + char* bus_name = NULL; + char* dev_name = NULL; + + // if no -P was given or '-P usb' was given + if (strcmp(port, "usb") == 0) + { + port = NULL; + } + else + { + // calculate bus and device names from -P option + if (strncmp(port, "usb", 3) == 0 && ':' == port[3]) + { + bus_name = port + 4; + dev_name = strchr(bus_name, ':'); + if (dev_name != NULL) + { + *dev_name = '\0'; + dev_name++; + } + } + } + + if (port != NULL && dev_name == NULL) + { + avrdude_message(MSG_INFO, "%s: ERROR: Invalid -P value: '%s'\n", progname, port); + avrdude_message(MSG_INFO, "%sUse -P usb:bus:device\n", progbuf); + return -1; + } + + // Determine VID/PID + int vid = pgm->usbvid ? pgm->usbvid : MICRONUCLEUS_VID; + int pid = MICRONUCLEUS_PID; + + LNODEID usbpid = lfirst(pgm->usbpid); + if (usbpid != NULL) + { + pid = *(int*)(ldata(usbpid)); + if (lnext(usbpid)) + { + avrdude_message(MSG_INFO, "%s: WARNING: using PID 0x%04x, ignoring remaining PIDs in list\n", + progname, pid); + } + } + + usb_init(); + + bool show_retry_message = true; + + time_t start_time = time(NULL); + for (;;) + { + usb_find_busses(); + usb_find_devices(); + + pdata->usb_handle = NULL; + + // Search for device + struct usb_bus* bus = NULL; + for (bus = usb_busses; bus != NULL && pdata->usb_handle == NULL; bus = bus->next) + { + struct usb_device* device = NULL; + for (device = bus->devices; device != NULL && pdata->usb_handle == NULL; device = device->next) + { + if (device->descriptor.idVendor == vid && device->descriptor.idProduct == pid) + { + pdata->major_version = (uint8_t)(device->descriptor.bcdDevice >> 8); + pdata->minor_version = (uint8_t)(device->descriptor.bcdDevice >> 0); + + avrdude_message(MSG_NOTICE, "%s: Found device with Micronucleus V%d.%d, bus:device: %s:%s\n", + progname, + pdata->major_version, pdata->minor_version, + bus->dirname, device->filename); + + // if -P was given, match device by device name and bus name + if (port != NULL) + { + if (dev_name == NULL || strcmp(bus->dirname, bus_name) || strcmp(device->filename, dev_name)) + { + continue; + } + } + + if (pdata->major_version > MICRONUCLEUS_MAX_MAJOR_VERSION) + { + avrdude_message(MSG_INFO, "%s: WARNING: device with unsupported version (V%d.%d) of Micronucleus detected.\n", + progname, + pdata->major_version, pdata->minor_version); + continue; + } + + pdata->usb_handle = usb_open(device); + if (pdata->usb_handle == NULL) + { + avrdude_message(MSG_INFO, "%s: ERROR: Failed to open USB device: %s\n", progname, usb_strerror()); + } + } + } + } + + if (pdata->usb_handle == NULL && pdata->wait_until_device_present) + { + if (show_retry_message) + { + if (pdata->wait_timout < 0) + { + avrdude_message(MSG_INFO, "%s: No device found, waiting for device to be plugged in...\n", progname); + } + else + { + avrdude_message(MSG_INFO, "%s: No device found, waiting %d seconds for device to be plugged in...\n", + progname, + pdata->wait_timout); + } + + avrdude_message(MSG_INFO, "%s: Press CTRL-C to terminate.\n", progname); + show_retry_message = false; + } + + if (pdata->wait_timout < 0 || (time(NULL) - start_time) < pdata->wait_timout) + { + delay_ms(MICRONUCLEUS_CONNECT_WAIT); + continue; + } + } + + break; + } + + if (!pdata->usb_handle) + { + avrdude_message(MSG_INFO, "%s: ERROR: Could not find device with Micronucleus bootloader (%04X:%04X)\n", + progname, vid, pid); + return -1; + } + + return 0; +} + +static void micronucleus_close(PROGRAMMER* pgm) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_close()\n", progname); + + pdata_t* pdata = PDATA(pgm); + if (pdata->usb_handle != NULL) + { + usb_close(pdata->usb_handle); + pdata->usb_handle = NULL; + } +} + +static int micronucleus_read_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem, + unsigned long addr, unsigned char* value) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_read_byte(desc=%s, addr=0x%0X)\n", + progname, mem->desc, addr); + + if (strcmp(mem->desc, "lfuse") == 0 || + strcmp(mem->desc, "hfuse") == 0 || + strcmp(mem->desc, "efuse") == 0 || + strcmp(mem->desc, "lock") == 0) + { + *value = 0xFF; + return 0; + } + else + { + avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc); + return -1; + } +} + +static int micronucleus_write_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem, + unsigned long addr, unsigned char value) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_write_byte(desc=%s, addr=0x%0X)\n", + progname, mem->desc, addr); + return -1; +} + +static int micronucleus_paged_load(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_paged_load(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", + progname, page_size, addr, n_bytes); + return -1; +} + +static int micronucleus_paged_write(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_paged_write(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n", + progname, page_size, addr, n_bytes); + + if (strcmp(mem->desc, "flash") == 0) + { + pdata_t* pdata = PDATA(pgm); + + if (n_bytes > page_size) + { + avrdude_message(MSG_INFO, "%s: Buffer size (%u) exceeds page size (%u)\n", progname, n_bytes, page_size); + return -1; + } + + if (addr + n_bytes > pdata->flash_size) + { + avrdude_message(MSG_INFO, "%s: Program size (%u) exceeds flash size (%u)\n", progname, addr + n_bytes, pdata->flash_size); + return -1; + } + + uint8_t* page_buffer = (uint8_t*)malloc(pdata->page_size); + if (page_buffer == NULL) + { + avrdude_message(MSG_INFO, "%s: Failed to allocate memory\n", progname); + return -1; + } + + // Note: Page size reported by the bootloader may be smaller than device page size as configured in avrdude.conf. + int result = 0; + while (n_bytes > 0) + { + size_t chunk_size = n_bytes < pdata->page_size ? n_bytes : pdata->page_size; + + memcpy(page_buffer, mem->buf + addr, chunk_size); + memset(page_buffer + chunk_size, 0xFF, pdata->page_size - chunk_size); + + result = micronucleus_write_page(pdata, addr, page_buffer, pdata->page_size); + if (result < 0) + { + break; + } + + addr += chunk_size; + n_bytes -= chunk_size; + } + + free(page_buffer); + return result; + } + else + { + avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc); + return -1; + } +} + +static int micronucleus_parseextparams(PROGRAMMER* pgm, LISTID xparams) +{ + avrdude_message(MSG_DEBUG, "%s: micronucleus_parseextparams()\n", progname); + + pdata_t* pdata = PDATA(pgm); + for (LNODEID node = lfirst(xparams); node != NULL; node = lnext(node)) + { + const char* param = ldata(node); + + if (strcmp(param, "wait") == 0) + { + pdata->wait_until_device_present = true; + pdata->wait_timout = -1; + } + else if (strncmp(param, "wait=", 5) == 0) + { + pdata->wait_until_device_present = true; + pdata->wait_timout = atoi(param + 5); + } + else + { + avrdude_message(MSG_INFO, "%s: Invalid extended parameter '%s'\n", progname, param); + return -1; + } + } + + return 0; +} + +void micronucleus_initpgm(PROGRAMMER* pgm) +{ + strcpy(pgm->type, "Micronucleus V2.0"); + + pgm->setup = micronucleus_setup; + pgm->teardown = micronucleus_teardown; + pgm->initialize = micronucleus_initialize; + pgm->display = micronucleus_display; + pgm->powerup = micronucleus_powerup; + pgm->powerdown = micronucleus_powerdown; + pgm->enable = micronucleus_enable; + pgm->disable = micronucleus_disable; + pgm->program_enable = micronucleus_program_enable; + pgm->read_sig_bytes = micronucleus_read_sig_bytes; + pgm->chip_erase = micronucleus_chip_erase; + pgm->cmd = NULL; + pgm->open = micronucleus_open; + pgm->close = micronucleus_close; + pgm->read_byte = micronucleus_read_byte; + pgm->write_byte = micronucleus_write_byte; + pgm->paged_load = micronucleus_paged_load; + pgm->paged_write = micronucleus_paged_write; + pgm->parseextparams = micronucleus_parseextparams; +} + +#else /* !HAVE_LIBUSB */ + + // Give a proper error if we were not compiled with libusb +static int micronucleus_nousb_open(struct programmer_t* pgm, char* name) +{ + avrdude_message(MSG_INFO, "%s: error: No usb support. Please compile again with libusb installed.\n", progname); + return -1; +} + +void micronucleus_initpgm(PROGRAMMER* pgm) +{ + strcpy(pgm->type, "micronucleus"); + pgm->open = micronucleus_nousb_open; +} + +#endif /* HAVE_LIBUSB */ + +const char micronucleus_desc[] = "Micronucleus Bootloader"; diff --git a/src/micronucleus.h b/src/micronucleus.h new file mode 100644 index 00000000..46c67182 --- /dev/null +++ b/src/micronucleus.h @@ -0,0 +1,35 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2019 Marius Greuel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef micronucleus_h +#define micronucleus_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char micronucleus_desc[]; +void micronucleus_initpgm(PROGRAMMER* pgm); + +#ifdef __cplusplus +} +#endif + +#endif /* micronucleus_h */ diff --git a/src/pgm_type.c b/src/pgm_type.c index becf7bdd..195b6ffb 100644 --- a/src/pgm_type.c +++ b/src/pgm_type.c @@ -41,6 +41,7 @@ #include "jtag3.h" #include "linuxgpio.h" #include "linuxspi.h" +#include "micronucleus.h" #include "par.h" #include "pickit2.h" #include "ppi.h" @@ -85,6 +86,7 @@ const PROGRAMMER_TYPE programmers_types[] = { {"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc}, {"linuxgpio", linuxgpio_initpgm, linuxgpio_desc}, {"linuxspi", linuxspi_initpgm, linuxspi_desc}, + {"micronucleus", micronucleus_initpgm, micronucleus_desc}, {"par", par_initpgm, par_desc}, {"pickit2", pickit2_initpgm, pickit2_desc}, {"serbb", serbb_initpgm, serbb_desc}, From 1529277477a4b46049b32955bb94a3a440998fb4 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Wed, 5 Jan 2022 17:26:39 +0100 Subject: [PATCH 14/17] When finding LIBFTDI, don't clobber LIBS but extend it --- src/configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configure.ac b/src/configure.ac index 32e54f9c..21442bb9 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -195,7 +195,7 @@ if test x$have_libftdi1 = xyes; then LIBFTDI1="-lftdi1" AC_DEFINE([HAVE_LIBFTDI1]) AC_SUBST(LIBFTDI1, $LIBFTDI1) - LIBS="${LIBFTDI1}" + LIBS="${LIBS} ${LIBFTDI1}" AC_CHECK_FUNCS(ftdi_tcioflush) else if test x$have_libftdi = xyes; then From 8c1c803fb26b5cc2e42ce51ccb7ba8a592f21633 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Wed, 5 Jan 2022 17:54:49 +0100 Subject: [PATCH 15/17] Mention PR #792 as closed --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index b32b7345..75fa75ee 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,7 @@ Changes since version 6.4: - Derive program version string from last commit #794 - Find 'avrdude.conf' based on absolute path to executable #780 - buspirate: fix -Wtautological-constant-out-of-range-compare #796 + - avrftdi: don't use the deprecated ftdi_usb_purge_buffers routine #792 * Internals: From a6ad88d9c4558ffc148967dc97918d4780e32128 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Wed, 5 Jan 2022 22:16:56 +0100 Subject: [PATCH 16/17] Ignore ctags index file ctags creates an index file called 'tags'. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6c1fa9c7..60305807 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ avrdude libtool ltmain.sh ylwrap +tags *.o *.lo From 7bf9711392960fd9aef75dd1b9e1f84b0dcd5341 Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Wed, 5 Jan 2022 22:46:36 +0100 Subject: [PATCH 17/17] Mention PR #804 --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 75fa75ee..6098f34c 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,7 @@ Changes since version 6.4: - Find 'avrdude.conf' based on absolute path to executable #780 - buspirate: fix -Wtautological-constant-out-of-range-compare #796 - avrftdi: don't use the deprecated ftdi_usb_purge_buffers routine #792 + - Ignore ctags index file #804 * Internals: