diff --git a/NEWS b/NEWS index 7bea2bcb..84852dbf 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,12 @@ Changes since version 7.0: - Fix ft245r paged read for ATmega2560 et al #1018 - Add option -A that supresses trailing 0xff optimisation and automatically do so for -c arduino #936 + - Fix linuxspi default port #933 + - Add support for high-voltage UPDI im jtag3.c #1015 + - Fix terminal write edge cases; + add one read mode; + add quell command #1025 + - Fix usbtiny read for parts with more than 64 kB flash #1029 * Internals: diff --git a/src/avrdude.1 b/src/avrdude.1 index c4fc850f..b312fff5 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -180,7 +180,7 @@ has a revision 1 hardware and firmware version of at least 5.37 (decimal). For ATxmega devices, the JTAGICE3 is supported in PDI mode. .Pp Atmel-ICE (ARM/AVR) is supported in all modes (JTAG, PDI for Xmega, debugWIRE, -ISP). +ISP, UPDI). .Pp Atmel's XplainedPro boards, using the EDBG protocol (CMSIS-DAP compatible), are supported using the "jtag3" programmer type. @@ -226,7 +226,7 @@ thus the name SerialUPDI programmer implementation is based on Microchip's .Em pymcuprog Li https://github.com/microchip-pic-avr-tools/pymcuprog utility, but it also contains some performance improvements included in -Spence Kohde's +Spence Konde's .Em DxCore Arduino core .Li https://github.com/SpenceKonde/DxCore . @@ -978,9 +978,13 @@ versions of the bootloader. .It Ar JTAG ICE mkII .It Ar JTAGICE3 .It Ar Atmel-ICE +.It Ar Power Debugger +.It Ar PICkit 4 +.It Ar MPLAB SNAP .It Ar AVR Dragon -When using the JTAG ICE mkII, JTAGICE3, Atmel-ICE or AVR Dragon in JTAG mode, the -following extended parameter is accepted: +When using the JTAG ICE mkII, JTAGICE3, Atmel-ICE, PICkit 4, MPLAB SNAP, +Power Debugger or AVR Dragon in JTAG mode, the following extended parameter +is accepted: .Bl -tag -offset indent -width indent .It Ar jtagchain=UB,UA,BB,BA Setup the JTAG scan chain for @@ -995,6 +999,14 @@ bits after the target AVR, respectively. Each AVR unit within the chain shifts by 4 bits. Other JTAG units might require a different bit shift count. .El +.Pp +The PICkit 4 and the Power Debugger also supports high-voltage UPDI programming. +This is used to enable a UPDI pin that has previously been set to RESET or +GPIO mode. High-voltage UPDI can be utilized by using an extended parameter: +.Bl -tag -offset indent -width indent +.It Ar hvupdi +Enable high-voltage UPDI initialization for targets that supports this. +.El .It Ar AVR910 .Bl -tag -offset indent -width indent .It Ar devcode=VALUE diff --git a/src/avrdude.conf.in b/src/avrdude.conf.in index 1728497f..8fc67080 100644 --- a/src/avrdude.conf.in +++ b/src/avrdude.conf.in @@ -22,7 +22,7 @@ # desc = ; # quoted string # type = ; # programmer type, quoted string # # supported programmer types can be listed by "-c ?type" -# connection_type = parallel | serial | usb +# connection_type = parallel | serial | usb | spi # baudrate = ; # baudrate for avr910-programmer # vcc = [, ... ] ; # pin number(s) # buff = [, ... ] ; # pin number(s) @@ -40,6 +40,7 @@ # usbvendor = ; # USB Vendor Name # usbproduct = ; # USB Product Name # usbsn = ; # USB Serial Number +# hvupdi_support = [, , ... ] ; # UPDI HV Variants Support # # To invert a bit, use = ~ , the spaces are important. # For a pin list all pins must be inverted. @@ -337,6 +338,7 @@ # default_parallel = "@DEFAULT_PAR_PORT@"; default_serial = "@DEFAULT_SER_PORT@"; +default_spi = "@DEFAULT_SPI_PORT@"; # default_bitclock = 2.5; # @@ -630,6 +632,7 @@ programmer desc = "SerialUPDI"; type = "serialupdi"; connection_type = serial; + hvupdi_support = 1; ; programmer @@ -1177,6 +1180,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2110, 0x2140; + hvupdi_support = 1; ; programmer @@ -1209,6 +1213,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2111; + hvupdi_support = 1; ; programmer @@ -1233,6 +1238,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2145; + hvupdi_support = 1; ; programmer @@ -1257,6 +1263,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2141; + hvupdi_support = 1; ; programmer @@ -1297,6 +1304,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2144; + hvupdi_support = 0, 1; ; programmer @@ -1321,6 +1329,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2177, 0x2178, 0x2179; + hvupdi_support = 0, 1, 2; ; programmer @@ -1345,6 +1354,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x217F, 0x2180, 0x2181; + hvupdi_support = 1; ; programmer @@ -1369,6 +1379,7 @@ programmer type = "jtagice3_updi"; connection_type = usb; usbpid = 0x2175; + hvupdi_support = 1; ; programmer @@ -1642,6 +1653,7 @@ programmer id = "linuxspi"; desc = "Use Linux SPI device in /dev/spidev*"; type = "linuxspi"; + connection_type = spi; reset = 25; # Pi GPIO number - this is J8:22 ; @HAVE_LINUXSPI_END@ @@ -1737,6 +1749,7 @@ programmer type = "jtagmkii_pdi"; connection_type = serial; baudrate = 115200; + hvupdi_support = 1; ; # @@ -17122,13 +17135,15 @@ part # AVR8X tiny family common values #------------------------------------------------------------ -part parent ".avr8x" - id = ".avr8x_tiny"; - desc = "AVR8X tiny family common values"; - family_id = "tinyAVR"; +part parent ".avr8x" + id = ".avr8x_tiny"; + desc = "AVR8X tiny family common values"; + family_id = "tinyAVR"; + # Shared UPDI pin, HV on UPDI pin + hvupdi_variant = 0; memory "userrow" - size = 0x20; + size = 0x20; offset = 0x1300; page_size = 0x20; readsize = 0x100; @@ -17143,13 +17158,15 @@ part parent ".avr8x" # AVR8X mega family common values #------------------------------------------------------------ -part parent ".avr8x" - id = ".avr8x_mega"; - desc = "AVR8X mega family common values"; - family_id = "megaAVR"; +part parent ".avr8x" + id = ".avr8x_mega"; + desc = "AVR8X mega family common values"; + family_id = "megaAVR"; + # Dedicated UPDI pin, no HV + hvupdi_variant = 1; memory "userrow" - size = 0x40; + size = 0x40; offset = 0x1300; page_size = 0x40; readsize = 0x100; @@ -18248,11 +18265,13 @@ part parent ".avr8x_mega" #------------------------------------------------------------ part - id = ".avrdx"; - desc = "AVR-Dx family common values"; - has_updi = yes; - nvm_base = 0x1000; - ocd_base = 0x0F80; + id = ".avrdx"; + desc = "AVR-Dx family common values"; + has_updi = yes; + nvm_base = 0x1000; + ocd_base = 0x0F80; + # Dedicated UPDI pin, no HV + hvupdi_variant = 1; memory "signature" size = 3; @@ -18934,6 +18953,7 @@ part parent ".avrdx" id = "avr16dd14"; desc = "AVR16DD14"; signature = 0x1E 0x94 0x34; + hvupdi_variant = 2; memory "flash" size = 0x4000; @@ -18958,6 +18978,7 @@ part parent ".avrdx" id = "avr16dd20"; desc = "AVR16DD20"; signature = 0x1E 0x94 0x33; + hvupdi_variant = 2; memory "flash" size = 0x4000; @@ -18982,6 +19003,7 @@ part parent ".avrdx" id = "avr16dd28"; desc = "AVR16DD28"; signature = 0x1E 0x94 0x32; + hvupdi_variant = 2; memory "flash" size = 0x4000; @@ -19006,6 +19028,7 @@ part parent ".avrdx" id = "avr16dd32"; desc = "AVR16DD32"; signature = 0x1E 0x94 0x31; + hvupdi_variant = 2; memory "flash" size = 0x4000; @@ -19030,6 +19053,7 @@ part parent ".avrdx" id = "avr32dd14"; desc = "AVR32DD14"; signature = 0x1E 0x95 0x3B; + hvupdi_variant = 2; memory "flash" size = 0x8000; @@ -19054,6 +19078,7 @@ part parent ".avrdx" id = "avr32dd20"; desc = "AVR32DD20"; signature = 0x1E 0x95 0x3A; + hvupdi_variant = 2; memory "flash" size = 0x8000; @@ -19078,6 +19103,7 @@ part parent ".avrdx" id = "avr32dd28"; desc = "AVR32DD28"; signature = 0x1E 0x95 0x39; + hvupdi_variant = 2; memory "flash" size = 0x8000; @@ -19102,6 +19128,7 @@ part parent ".avrdx" id = "avr32dd32"; desc = "AVR32DD32"; signature = 0x1E 0x95 0x38; + hvupdi_variant = 2; memory "flash" size = 0x8000; @@ -19126,6 +19153,7 @@ part parent ".avrdx" id = "avr64dd14"; desc = "AVR64DD14"; signature = 0x1E 0x96 0x1D; + hvupdi_variant = 2; memory "flash" size = 0x10000; @@ -19150,6 +19178,7 @@ part parent ".avrdx" id = "avr64dd20"; desc = "AVR64DD20"; signature = 0x1E 0x96 0x1C; + hvupdi_variant = 2; memory "flash" size = 0x10000; @@ -19174,6 +19203,7 @@ part parent ".avrdx" id = "avr64dd28"; desc = "AVR64DD28"; signature = 0x1E 0x96 0x1B; + hvupdi_variant = 2; memory "flash" size = 0x10000; @@ -19198,6 +19228,7 @@ part parent ".avrdx" id = "avr64dd32"; desc = "AVR64DD32"; signature = 0x1E 0x96 0x1A; + hvupdi_variant = 2; memory "flash" size = 0x10000; @@ -19218,9 +19249,11 @@ part parent ".avrdx" # AVR-Ex family common values #------------------------------------------------------------ -part parent ".avrdx" - id = ".avrex"; - desc = "AVR-Ex family common values"; +part parent ".avrdx" + id = ".avrex"; + desc = "AVR-Ex family common values"; + # Shared UPDI pin, HV on _RESET + hvupdi_variant = 2; memory "userrow" size = 0x40; diff --git a/src/avrpart.c b/src/avrpart.c index 1f4f971f..5bab5a2b 100644 --- a/src/avrpart.c +++ b/src/avrpart.c @@ -584,6 +584,7 @@ AVRPART * avr_new_part(void) memset(p->signature, 0xFF, 3); p->ctl_stack_type = CTL_STACK_NONE; p->ocdrev = -1; + p->hvupdi_variant = -1; p->mem = lcreat(NULL, 0); p->mem_alias = lcreat(NULL, 0); diff --git a/src/config.c b/src/config.c index 3d8a760e..00ac8b0e 100644 --- a/src/config.c +++ b/src/config.c @@ -35,6 +35,7 @@ char default_programmer[MAX_STR_CONST]; char default_parallel[PATH_MAX]; char default_serial[PATH_MAX]; +char default_spi[PATH_MAX]; double default_bitclock; char string_buf[MAX_STR_CONST]; diff --git a/src/config_gram.y b/src/config_gram.y index a8416162..d8c51ca2 100644 --- a/src/config_gram.y +++ b/src/config_gram.y @@ -79,8 +79,11 @@ static int pin_name; %token K_DEFAULT_PARALLEL %token K_DEFAULT_PROGRAMMER %token K_DEFAULT_SERIAL +%token K_DEFAULT_SPI %token K_DESC %token K_FAMILY_ID +%token K_HVUPDI_SUPPORT +%token K_HVUPDI_VARIANT %token K_DEVICECODE %token K_STK500_DEVCODE %token K_AVR910_DEVCODE @@ -115,6 +118,7 @@ static int pin_name; %token K_RESET %token K_RETRY_PULSE %token K_SERIAL +%token K_SPI %token K_SCK %token K_SIGNATURE %token K_SIZE @@ -254,6 +258,12 @@ def : free_token($3); } | + K_DEFAULT_SPI TKN_EQUAL TKN_STRING TKN_SEMI { + strncpy(default_spi, $3->value.string, PATH_MAX); + default_spi[PATH_MAX-1] = 0; + free_token($3); + } | + K_DEFAULT_BITCLOCK TKN_EQUAL number_real TKN_SEMI { default_bitclock = $3->value.number_real; free_token($3); @@ -475,7 +485,8 @@ prog_parm : current_prog->baudrate = $3->value.number; free_token($3); } - } + } | + prog_parm_updi ; prog_parm_type: @@ -507,7 +518,8 @@ prog_parm_conntype: prog_parm_conntype_id: K_PARALLEL { current_prog->conntype = CONNTYPE_PARALLEL; } | K_SERIAL { current_prog->conntype = CONNTYPE_SERIAL; } | - K_USB { current_prog->conntype = CONNTYPE_USB; } + K_USB { current_prog->conntype = CONNTYPE_USB; } | + K_SPI { current_prog->conntype = CONNTYPE_SPI; } ; prog_parm_usb: @@ -576,6 +588,38 @@ usb_pid_list: } ; +prog_parm_updi: + K_HVUPDI_SUPPORT TKN_EQUAL hvupdi_support_list +; + +hvupdi_support_list: + TKN_NUMBER { + { + /* overwrite list entries, so clear the existing entries */ + ldestroy_cb(current_prog->hvupdi_support, free); + current_prog->hvupdi_support = lcreat(NULL, 0); + } + { + int *ip = malloc(sizeof(int)); + if (ip) { + *ip = $1->value.number; + ladd(current_prog->hvupdi_support, ip); + } + free_token($1); + } + } | + hvupdi_support_list TKN_COMMA TKN_NUMBER { + { + int *ip = malloc(sizeof(int)); + if (ip) { + *ip = $3->value.number; + ladd(current_prog->hvupdi_support, ip); + } + free_token($3); + } + } +; + pin_number_non_empty: TKN_NUMBER { if(0 != assign_pin(pin_name, $1, 0)) YYABORT; } | @@ -676,6 +720,12 @@ part_parm : free_token($3); } | + K_HVUPDI_VARIANT TKN_EQUAL TKN_NUMBER + { + current_part->hvupdi_variant = $3->value.number; + free_token($3); + } | + K_DEVICECODE TKN_EQUAL TKN_NUMBER { { yyerror("devicecode is deprecated, use " diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index d99b0a85..83b6fb3f 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -250,7 +250,8 @@ See below for the limitations of debugWire. For ATxmega devices, the JTAG ICE mkII/3 is supported in PDI mode, provided it has a revision 1 hardware and firmware version of at least 5.37 (decimal). -The Atmel-ICE (ARM/AVR) is supported (JTAG, PDI for Xmega, debugWIRE, ISP modes). +The Atmel-ICE (ARM/AVR) is supported (JTAG, PDI for Xmega, debugWIRE, ISP, +UPDI). Atmel's XplainedPro boards, using EDBG protocol (CMSIS-DAP compliant), are supported by the ``jtag3'' programmer type. @@ -854,10 +855,15 @@ accepting extended parameters. @table @code @item JTAG ICE mkII/3 +@itemx Atmel-ICE +@itemx PICkit 4 +@itemx MPLAB SNAP +@itemx Power Debugger @itemx AVR Dragon -When using the JTAG ICE mkII/3 or AVR Dragon in JTAG mode, the -following extended parameter is accepted: +When using the JTAG ICE mkII, JTAGICE3, Atmel-ICE, PICkit 4, MPLAB SNAP, +Power Debugger or AVR Dragon in JTAG mode, the following extended parameter +is accepted: @table @code @item @samp{jtagchain=UB,UA,BB,BA} Setup the JTAG scan chain for @var{UB} units before, @var{UA} units @@ -867,6 +873,14 @@ Each AVR unit within the chain shifts by 4 bits. Other JTAG units might require a different bit shift count. @end table +The PICkit 4 and the Power Debugger also supports high-voltage UPDI programming. +This is used to enable a UPDI pin that has previously been set to RESET or +GPIO mode. High-voltage UPDI can be utilized by using an extended parameter: +@table @code +@item @samp{hvupdi} +Enable high-voltage UPDI initialization for targets that supports this. +@end table + @cindex @code{-x} AVR910 @item AVR910 diff --git a/src/jtag3.c b/src/jtag3.c index 6a4004f3..5612d0ed 100644 --- a/src/jtag3.c +++ b/src/jtag3.c @@ -71,6 +71,9 @@ struct pdata /* Start address of Xmega boot area */ unsigned long boot_start; + /* Flag for triggering HV UPDI */ + bool use_hvupdi; + /* Function to set the appropriate clock parameter */ int (*set_sck)(PROGRAMMER *, unsigned char *); }; @@ -1204,6 +1207,7 @@ static int jtag3_initialize(PROGRAMMER * pgm, AVRPART * p) u16_to_b2(xd.nvm_base_addr, p->nvm_base); u16_to_b2(xd.ocd_base_addr, p->ocd_base); + xd.hvupdi_variant = p->hvupdi_variant; for (ln = lfirst(p->mem); ln; ln = lnext(ln)) { @@ -1258,6 +1262,29 @@ static int jtag3_initialize(PROGRAMMER * pgm, AVRPART * p) } } + // Generate UPDI high-voltage pulse if user asks for it and hardware supports it + LNODEID support; + if (p->flags & AVRPART_HAS_UPDI && + PDATA(pgm)->use_hvupdi == true && + p->hvupdi_variant != HV_UPDI_VARIANT_1) { + parm[0] = PARM3_UPDI_HV_NONE; + for (support = lfirst(pgm->hvupdi_support); support != NULL; support = lnext(support)) { + if(*(int *) ldata(support) == p->hvupdi_variant) { + avrdude_message(MSG_NOTICE, "%s: Sending HV pulse to targets %s pin\n", + progname, p->hvupdi_variant == HV_UPDI_VARIANT_0 ? "UPDI" : "RESET"); + parm[0] = PARM3_UPDI_HV_SIMPLE_PULSE; + break; + } + if (parm[0] == PARM3_UPDI_HV_NONE) { + avrdude_message(MSG_INFO, "%s: %s does not support sending HV pulse to target %s\n", + progname, pgm->desc, p->desc); + return -1; + } + } + if (jtag3_setparm(pgm, SCOPE_AVR, 3, PARM3_OPT_12V_UPDI_ENABLE, parm, 1) < 0) + return -1; + } + u16_to_b2(xd.default_min_div1_voltage, DEFAULT_MINIMUM_CHARACTERISED_DIV1_VOLTAGE_MV); u16_to_b2(xd.default_min_div2_voltage, DEFAULT_MINIMUM_CHARACTERISED_DIV2_VOLTAGE_MV); u16_to_b2(xd.default_min_div4_voltage, DEFAULT_MINIMUM_CHARACTERISED_DIV4_VOLTAGE_MV); @@ -1483,6 +1510,12 @@ static int jtag3_parseextparms(PROGRAMMER * pgm, LISTID extparms) continue; } + else if ((strcmp(extended_param, "hvupdi") == 0) && + (lsize(pgm->hvupdi_support) > 1)) { + PDATA(pgm)->use_hvupdi = true; + continue; + } + avrdude_message(MSG_INFO, "%s: jtag3_parseextparms(): invalid extended parameter '%s'\n", progname, extended_param); rv = -1; @@ -1630,6 +1663,12 @@ static int jtag3_open_updi(PROGRAMMER * pgm, char * port) { avrdude_message(MSG_NOTICE2, "%s: jtag3_open_updi()\n", progname); + LNODEID ln; + avrdude_message(MSG_NOTICE2, "%s: HV UPDI support:", progname); + for (ln = lfirst(pgm->hvupdi_support); ln; ln = lnext(ln)) + avrdude_message(MSG_NOTICE2, " %d", *(int *) ldata(ln)); + avrdude_message(MSG_NOTICE2, "\n", progname); + if (jtag3_open_common(pgm, port) < 0) return -1; @@ -2606,6 +2645,7 @@ void jtag3_updi_initpgm(PROGRAMMER * pgm) * mandatory functions */ pgm->initialize = jtag3_initialize; + pgm->parseextparams = jtag3_parseextparms; pgm->display = jtag3_display; pgm->enable = jtag3_enable; pgm->disable = jtag3_disable; diff --git a/src/jtag3_private.h b/src/jtag3_private.h index 7d0cbb74..ddc7e2e3 100644 --- a/src/jtag3_private.h +++ b/src/jtag3_private.h @@ -237,6 +237,14 @@ #define PARM3_OPT_12V_UPDI_ENABLE 0x06 #define PARM3_OPT_CHIP_ERASE_TO_ENTER 0x07 +/* + * UPDI high-voltage enable modes + */ +#define PARM3_UPDI_HV_NONE 0x00 /* Do not use high-voltage */ +#define PARM3_UPDI_HV_SIMPLE_PULSE 0x01 /* Issue a single high-voltage pulse immediately*/ +#define PARM3_UPDI_HV_AUTO_POWER_TOGGLE 0x02 /* Toggle power automatically and then apply a high-voltage pulse */ +#define PARM3_UPDI_HV_USER_POWER_TOGGLE 0x03 /* The user toggles power, and the tool applies a high-voltage pulse on power-up */ + /* Xmega erase memory types, for CMND_XMEGA_ERASE */ #define XMEGA_ERASE_CHIP 0x00 #define XMEGA_ERASE_APP 0x01 @@ -393,5 +401,7 @@ struct updi_device_desc { unsigned char flash_page_size_msb; // Extends flash_page_size, used in 24-bit mode unsigned char address_mode; // 0x00 = 16-bit mode, 0x01 = 24-bit mode + + unsigned char hvupdi_variant; // Indicates the target UPDI HV implementation }; #endif /* JTAG3_PRIVATE_EXPORTED */ diff --git a/src/lexer.l b/src/lexer.l index 0b31eb21..38d988db 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -139,6 +139,7 @@ default_bitclock { yylval=NULL; return K_DEFAULT_BITCLOCK; } default_parallel { yylval=NULL; return K_DEFAULT_PARALLEL; } default_programmer { yylval=NULL; return K_DEFAULT_PROGRAMMER; } default_serial { yylval=NULL; return K_DEFAULT_SERIAL; } +default_spi { yylval=NULL; return K_DEFAULT_SPI; } delay { yylval=NULL; return K_DELAY; } desc { yylval=NULL; return K_DESC; } family_id { yylval=NULL; return K_FAMILY_ID; } @@ -159,6 +160,8 @@ hventerstabdelay { yylval=NULL; return K_HVENTERSTABDELAY; } hvleavestabdelay { yylval=NULL; return K_HVLEAVESTABDELAY; } hvsp_controlstack { yylval=NULL; return K_HVSP_CONTROLSTACK; } hvspcmdexedelay { yylval=NULL; return K_HVSPCMDEXEDELAY; } +hvupdi_support { yylval=NULL; return K_HVUPDI_SUPPORT; } +hvupdi_variant { yylval=NULL; return K_HVUPDI_VARIANT; } id { yylval=NULL; return K_ID; } idr { yylval=NULL; return K_IDR; } io { yylval=new_token(K_IO); return K_IO; } @@ -222,6 +225,7 @@ sck { yylval=new_token(K_SCK); return K_SCK; } serial { yylval=NULL; return K_SERIAL; } signature { yylval=NULL; return K_SIGNATURE; } size { yylval=NULL; return K_SIZE; } +spi { yylval=NULL; return K_SPI; } spmcr { yylval=NULL; return K_SPMCR; } stabdelay { yylval=NULL; return K_STABDELAY; } stk500_devcode { yylval=NULL; return K_STK500_DEVCODE; } diff --git a/src/libavrdude.h b/src/libavrdude.h index 1eb2358c..57d91937 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -198,6 +198,10 @@ typedef struct opcode { #define AVRPART_IS_AT90S1200 0x1000 /* part is an AT90S1200 (needs special treatment) */ #define AVRPART_HAS_UPDI 0x2000 /* part has UPDI i/f (AVR8X) */ +#define HV_UPDI_VARIANT_0 0 /* Shared UPDI/GPIO/RESET pin, HV on UPDI pin (tinyAVR0/1/2)*/ +#define HV_UPDI_VARIANT_1 1 /* Dedicated UPDI pin, no HV (megaAVR0/AVR-Dx) */ +#define HV_UPDI_VARIANT_2 2 /* Shared UPDI pin, HV on _RESET (AVR-Ex) */ + #define AVR_DESCLEN 64 #define AVR_IDLEN 32 #define AVR_FAMILYIDLEN 7 @@ -212,6 +216,7 @@ typedef struct avrpart { char desc[AVR_DESCLEN]; /* long part name */ char id[AVR_IDLEN]; /* short part name */ char family_id[AVR_FAMILYIDLEN+1]; /* family id in the SIB (avr8x) */ + int hvupdi_variant; /* HV pulse on UPDI pin, no pin or RESET pin */ int stk500_devcode; /* stk500 device code */ int avr910_devcode; /* avr910 device code */ int chip_erase_delay; /* microseconds */ @@ -645,7 +650,8 @@ typedef enum { typedef enum { CONNTYPE_PARALLEL, CONNTYPE_SERIAL, - CONNTYPE_USB + CONNTYPE_USB, + CONNTYPE_SPI } conntype_t; typedef struct programmer_t { @@ -724,6 +730,7 @@ typedef struct programmer_t { int lineno; /* config file line number */ void *cookie; /* for private use by the programmer */ char flag; /* for private use of the programmer */ + LISTID hvupdi_support; /* List of UPDI HV variants the tool supports. See HV_UPDI_VARIANT_ */ } PROGRAMMER; #ifdef __cplusplus @@ -924,6 +931,7 @@ extern LISTID programmers; extern char default_programmer[]; extern char default_parallel[]; extern char default_serial[]; +extern char default_spi[]; extern double default_bitclock; /* This name is fixed, it's only here for symmetry with diff --git a/src/main.c b/src/main.c index f53eedee..d0eddacb 100644 --- a/src/main.c +++ b/src/main.c @@ -135,72 +135,6 @@ static void usage(void) } -static void update_progress_tty (int percent, double etime, char *hdr) -{ - static char hashes[51]; - static char *header; - static int last = 0; - int i; - - setvbuf(stderr, (char*)NULL, _IONBF, 0); - - hashes[50] = 0; - - memset (hashes, ' ', 50); - for (i=0; i>1)*2; - - setvbuf(stderr, (char*)NULL, _IONBF, 0); - - if (hdr) { - avrdude_message(MSG_INFO, "\n%s | ", hdr); - last = 0; - done = 0; - } - else { - while ((cnt > last) && (done == 0)) { - avrdude_message(MSG_INFO, "#"); - cnt -= 2; - } - } - - if ((percent == 100) && (done == 0)) { - avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime); - last = 0; - done = 1; - } - else - last = (percent>>1)*2; /* Make last a multiple of 2. */ - - setvbuf(stderr, (char*)NULL, _IOLBF, 0); -} - static void list_programmers_callback(const char *name, const char *desc, const char *cfgname, int cfglineno, void *cookie) @@ -383,6 +317,7 @@ int main(int argc, char * argv []) default_parallel[0] = 0; default_serial[0] = 0; + default_spi[0] = 0; default_bitclock = 0.0; init_config(); @@ -759,18 +694,8 @@ int main(int argc, char * argv []) } #endif - if (quell_progress == 0) { - if (isatty (STDERR_FILENO)) - update_progress = update_progress_tty; - else { - update_progress = update_progress_no_tty; - /* disable all buffering of stderr for compatibility with - software that captures and redirects output to a GUI - i.e. Programmers Notepad */ - setvbuf( stderr, NULL, _IONBF, 0 ); - setvbuf( stdout, NULL, _IONBF, 0 ); - } - } + if (quell_progress == 0) + terminal_setup_update_progress(); /* * Print out an identifying string so folks can tell what version @@ -932,6 +857,12 @@ int main(int argc, char * argv []) case CONNTYPE_USB: port = DEFAULT_USB; break; + +#ifdef HAVE_LINUXSPI + case CONNTYPE_SPI: + port = *default_spi ? default_spi : "unknown"; + break; +#endif } } diff --git a/src/pgm.c b/src/pgm.c index 851ac5a8..4580cbbd 100644 --- a/src/pgm.c +++ b/src/pgm.c @@ -83,6 +83,7 @@ PROGRAMMER * pgm_new(void) pgm->lineno = 0; pgm->baudrate = 0; pgm->initpgm = NULL; + pgm->hvupdi_support = lcreat(NULL, 0); for (i=0; ipinno[i] = 0; diff --git a/src/term.c b/src/term.c index dc771273..f02f95c5 100644 --- a/src/term.c +++ b/src/term.c @@ -25,7 +25,10 @@ #include #include #include +#include #include +#include +#include #if defined(HAVE_LIBREADLINE) # include @@ -90,14 +93,17 @@ static int cmd_pgm (PROGRAMMER * pgm, struct avrpart * p, static int cmd_verbose (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]); +static int cmd_quell (PROGRAMMER * pgm, struct avrpart * p, + int argc, char *argv[]); + struct command cmd[] = { - { "dump", cmd_dump, "dump memory : %s " }, + { "dump", cmd_dump, "%s [ | ... | | ...]" }, { "read", cmd_dump, "alias for dump" }, - { "write", cmd_write, "write memory : %s ... " }, + { "write", cmd_write, "%s [[,] {[,]} | [,] {[,]} ...]" }, { "erase", cmd_erase, "perform a chip erase" }, { "sig", cmd_sig, "display device signature bytes" }, { "part", cmd_part, "display the current part information" }, - { "send", cmd_send, "send a raw command : %s " }, + { "send", cmd_send, "send a raw command: %s " }, { "parms", cmd_parms, "display adjustable parameters (STK500 and Curiosity Nano only)" }, { "vtarg", cmd_vtarg, "set (STK500 and Curiosity Nano only)" }, { "varef", cmd_varef, "set (STK500 only)" }, @@ -106,12 +112,13 @@ struct command cmd[] = { { "spi", cmd_spi, "enter direct SPI mode" }, { "pgm", cmd_pgm, "return to programming mode" }, { "verbose", cmd_verbose, "change verbosity" }, + { "quell", cmd_quell, "set quell level for progress bars" }, { "help", cmd_help, "help" }, { "?", cmd_help, "help" }, { "quit", cmd_quit, "quit" } }; -#define NCMDS (sizeof(cmd)/sizeof(struct command)) +#define NCMDS ((int)(sizeof(cmd)/sizeof(struct command))) @@ -119,19 +126,22 @@ static int spi_mode = 0; static int nexttok(char * buf, char ** tok, char ** next) { - char * q, * n; + unsigned char *q, *n; - q = buf; - while (isspace((int)*q)) + q = (unsigned char *) buf; + while (isspace(*q)) q++; /* isolate first token */ n = q; uint8_t quotes = 0; - while (*n && (!isspace((int)*n) || quotes)) { - if (*n == '\"') + while (*n && (!isspace(*n) || quotes)) { + // poor man's quote and escape processing + if (*n == '"' || *n == '\'') quotes++; - else if (isspace((int)*n) && *(n-1) == '\"') + else if(*n == '\\' && n[1]) + n++; + else if (isspace(*n) && (n > q+1) && (n[-1] == '"' || n[-1] == '\'')) break; n++; } @@ -142,11 +152,11 @@ static int nexttok(char * buf, char ** tok, char ** next) } /* find start of next token */ - while (isspace((int)*n)) + while (isspace(*n)) n++; - *tok = q; - *next = n; + *tok = (char *) q; + *next = (char *) n; return 0; } @@ -156,8 +166,8 @@ static int hexdump_line(char * buffer, unsigned char * p, int n, int pad) { char * hexdata = "0123456789abcdef"; char * b = buffer; - int32_t i = 0; - int32_t j = 0; + int i = 0; + int j = 0; for (i=0; i sizeof b? sizeof b: n; + + memcpy(b, p, n); + for (int i = 0; i < n; i++) + buffer[i] = isascii(b[i]) && isspace(b[i])? ' ': + isascii(b[i]) && isgraph(b[i])? b[i]: '.'; for (i = n; i < pad; i++) buffer[i] = ' '; @@ -210,10 +219,10 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len) char dst1[80]; char dst2[80]; - int32_t addr = startaddr; + int addr = startaddr; unsigned char * p = (unsigned char *)buf; while (len) { - int32_t n = 16; + int n = 16; if (n > len) n = len; hexdump_line(dst1, p, n, 48); @@ -231,44 +240,47 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len) static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[]) { - if (argc < 2) { - avrdude_message(MSG_INFO, "Usage: %s [ ]\n" - " %s [ <...>]\n" - " %s <...>\n" - " %s \n", - argv[0], argv[0], argv[0], argv[0]); - return -1; + if (argc < 2 || argc > 4) { + terminal_message(MSG_INFO, + "Usage: %s \n" + " %s ...\n" + " %s \n" + " %s ...\n" + " %s \n", + argv[0], argv[0], argv[0], argv[0], argv[0]); + return -1; } enum { read_size = 256 }; - static char prevmem[128] = {0x00}; + static char prevmem[AVR_MEMDESCLEN] = {0x00}; char * memtype = argv[1]; AVRMEM * mem = avr_locate_mem(p, memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - memtype, p->desc); + terminal_message(MSG_INFO, "%s (dump): %s memory type not defined for part %s\n", + progname, memtype, p->desc); return -1; } - uint32_t maxsize = mem->size; + int maxsize = mem->size; // Get start address if present char * end_ptr; - static uint32_t addr = 0; - if (argc == 4) { + static int addr = 0; + + if (argc >= 3 && strcmp(argv[2], "...") != 0) { addr = strtoul(argv[2], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[2])) { - avrdude_message(MSG_INFO, "%s (%s): can't parse address \"%s\"\n", - progname, argv[0], argv[2]); + terminal_message(MSG_INFO, "%s (dump): can't parse address %s\n", + progname, argv[2]); return -1; } else if (addr >= maxsize) { - avrdude_message(MSG_INFO, "%s (%s): address 0x%05lx is out of range for %s memory\n", - progname, argv[0], addr, mem->desc); + terminal_message(MSG_INFO, "%s (dump): address 0x%05lx is out of range for %s memory\n", + progname, (long) addr, mem->desc); return -1; } } // Get no. bytes to read if present - static int32_t len = read_size; + static int len = read_size; if (argc >= 3) { memset(prevmem, 0x00, sizeof(prevmem)); if (strcmp(argv[argc - 1], "...") == 0) { @@ -278,8 +290,8 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, } else if (argc == 4) { len = strtol(argv[3], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[3])) { - avrdude_message(MSG_INFO, "%s (%s): can't parse length \"%s\"\n", - progname, argv[0], argv[3]); + terminal_message(MSG_INFO, "%s (dump): can't parse length %s\n", + progname, argv[3]); return -1; } } else { @@ -304,19 +316,19 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, uint8_t * buf = malloc(len); if (buf == NULL) { - avrdude_message(MSG_INFO, "%s (dump): out of memory\n", progname); + terminal_message(MSG_INFO, "%s (dump): out of memory\n", progname); return -1; } report_progress(0, 1, "Reading"); - for (uint32_t i = 0; i < len; i++) { - int32_t rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]); + for (int i = 0; i < len; i++) { + int rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]); if (rc != 0) { - avrdude_message(MSG_INFO, "error reading %s address 0x%05lx of part %s\n", - mem->desc, addr + i, p->desc); + terminal_message(MSG_INFO, "%s (dump): error reading %s address 0x%05lx of part %s\n", + progname, mem->desc, (long) addr + i, p->desc); if (rc == -1) - avrdude_message(MSG_INFO, "read operation not supported on memory type \"%s\"\n", - mem->desc); + terminal_message(MSG_INFO, "%*sread operation not supported on memory type %s\n", + (int) strlen(progname)+9, "", mem->desc); return -1; } report_progress(i, len, NULL); @@ -334,49 +346,290 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, } +// Convert the next n hex digits of s to a hex number +static unsigned int tohex(const unsigned char *s, unsigned int n) { + int ret, c; + + ret = 0; + while(n--) { + ret *= 16; + c = *s++; + ret += c >= '0' && c <= '9'? c - '0': c >= 'a' && c <= 'f'? c - 'a' + 10: c - 'A' + 10; + } + + return ret; +} + +/* + * Create a utf-8 character sequence from a single unicode character. + * Permissive for some invalid unicode sequences but not for those with + * high bit set). Returns numbers of characters written (0-6). + */ +static int wc_to_utf8str(unsigned int wc, unsigned char *str) { + if(!(wc & ~0x7fu)) { + *str = (char) wc; + return 1; + } + if(!(wc & ~0x7ffu)) { + *str++ = (char) ((wc >> 6) | 0xc0); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 2; + } + if(!(wc & ~0xffffu)) { + *str++ = (char) ((wc >> 12) | 0xe0); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 3; + } + if(!(wc & ~0x1fffffu)) { + *str++ = (char) ((wc >> 18) | 0xf0); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 4; + } + if(!(wc & ~0x3ffffffu)) { + *str++ = (char) ((wc >> 24) | 0xf8); + *str++ = (char) (((wc >> 18) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 5; + } + if(!(wc & ~0x7fffffffu)) { + *str++ = (char) ((wc >> 30) | 0xfc); + *str++ = (char) (((wc >> 24) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 18) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 6; + } + return 0; +} + +// Unescape C-style strings, destination d must hold enough space (and can be source s) +static unsigned char *unescape(unsigned char *d, const unsigned char *s) { + unsigned char *ret = d; + int n, k; + + while(*s) { + switch (*s) { + case '\\': + switch (*++s) { + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + case 'a': + *d = '\a'; + break; + case 'b': + *d = '\b'; + break; + case 'e': // Non-standard ESC + *d = 27; + break; + case 'f': + *d = '\f'; + break; + case 'r': + *d = '\r'; + break; + case 'v': + *d = '\v'; + break; + case '?': + *d = '?'; + break; + case '`': + *d = '`'; + break; + case '"': + *d = '"'; + break; + case '\'': + *d = '\''; + break; + case '\\': + *d = '\\'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': // 1-3 octal digits + n = *s - '0'; + for(k = 0; k < 2 && s[1] >= '0' && s[1] <= '7'; k++) // Max 2 more octal characters + n *= 8, n += s[1] - '0', s++; + *d = n; + break; + case 'x': // Unlimited hex digits + for(k = 0; isxdigit(s[k + 1]); k++) + continue; + if(k > 0) { + *d = tohex(s + 1, k); + s += k; + } else { // No hex digits after \x? copy \x + *d++ = '\\'; + *d = 'x'; + } + break; + case 'u': // Exactly 4 hex digits and valid unicode + if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && + (n = wc_to_utf8str(tohex(s+1, 4), d))) { + d += n - 1; + s += 4; + } else { // Invalid \u sequence? copy \u + *d++ = '\\'; + *d = 'u'; + } + break; + case 'U': // Exactly 6 hex digits and valid unicode + if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) && isxdigit(s[6]) && + (n = wc_to_utf8str(tohex(s+1, 6), d))) { + d += n - 1; + s += 6; + } else { // Invalid \U sequence? copy \U + *d++ = '\\'; + *d = 'U'; + } + break; + default: // Keep the escape sequence (C would warn and remove \) + *d++ = '\\'; + *d = *s; + } + break; + + default: // Not an escape sequence: just copy the character + *d = *s; + } + d++; + s++; + } + *d = *s; // Terminate + + return ret; +} + + +static size_t maxstrlen(int argc, char **argv) { + size_t max = 0; + + for(int i=0; i max) + max = strlen(argv[i]); + + return max; +} + + +// Change data item p of size bytes from big endian to little endian and vice versa +static void change_endian(void *p, int size) { + uint8_t tmp, *w = p; + + for(int i=0; i \n" - " write <...>\n\n" - " Add a suffix to manually specify the size for each field:\n" - " HH/hh: 8-bit, H/h/S/s: 16-bit, L/l: 32-bit, LL/ll: 64-bit, F/f: 32-bit float\n"); + terminal_message(MSG_INFO, + "Usage: write [,] {[,]} \n" + " write [,] {[,]} ...\n" + "\n" + "Ellipsis ... writes bytes padded by repeating the last item.\n" + "\n" + " can be hexadecimal, octal or decimal integers, floating point numbers\n" + "or C-style strings and characters. For integers, an optional case-insensitive\n" + "suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.\n" + "Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point\n" + "number without suffix defaults to 32-bit float. Hexadecimal floating point\n" + "notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as\n" + "no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D\n" + "to clarify.\n" + "\n" + "An optional U suffix makes integers unsigned. Ordinary 0x hex integers are\n" + "always treated as unsigned. +0x or -0x hex numbers are treated as signed\n" + "unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.\n" + "If n is an unsigned integer then -n is also a valid unsigned integer as in C.\n" + "Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly\n" + "smaller range when a suffix specifies a smaller type. Out of range signed\n" + "numbers trigger a warning.\n" + "\n" + "Ordinary 0x hex integers with n hex digits (counting leading zeros) use the\n" + "smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex\n" + "integer. If an integer suffix specifies a size explicitly the corresponding\n" + "number of least significant bytes are written. Otherwise, signed and unsigned\n" + "integers alike occupy the smallest of 1, 2, 4, or 8 bytes needed to\n" + "accommodate them in their respective representation.\n" + ); return -1; } - int32_t i; + int i; uint8_t write_mode; // Operation mode, "standard" or "fill" uint8_t start_offset; // Which argc argument - int32_t len; // Number of bytes to write to memory + int len; // Number of bytes to write to memory char * memtype = argv[1]; // Memory name string AVRMEM * mem = avr_locate_mem(p, memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - memtype, p->desc); + terminal_message(MSG_INFO, "%s (write): %s memory type not defined for part %s\n", + progname, memtype, p->desc); return -1; } - uint32_t maxsize = mem->size; + int maxsize = mem->size; char * end_ptr; - int32_t addr = strtoul(argv[2], &end_ptr, 0); + int addr = strtoul(argv[2], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[2])) { - avrdude_message(MSG_INFO, "%s (write): can't parse address \"%s\"\n", - progname, argv[2]); + terminal_message(MSG_INFO, "%s (write): can't parse address %s\n", + progname, argv[2]); return -1; } if (addr > maxsize) { - avrdude_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n", - progname, addr, memtype); + terminal_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n", + progname, (long) addr, memtype); return -1; } // Allocate a buffer guaranteed to be large enough - uint8_t * buf = calloc(mem->size + 0x10 + strlen(argv[argc - 2]), sizeof(uint8_t)); + uint8_t * buf = calloc(mem->size + 8 + maxstrlen(argc-3, argv+3)+1, sizeof(uint8_t)); if (buf == NULL) { - avrdude_message(MSG_INFO, "%s (write): out of memory\n", progname); + terminal_message(MSG_INFO, "%s (write): out of memory\n", progname); return -1; } @@ -386,8 +639,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, start_offset = 4; len = strtoul(argv[3], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[3])) { - avrdude_message(MSG_INFO, "%s (write ...): can't parse address \"%s\"\n", - progname, argv[3]); + terminal_message(MSG_INFO, "%s (write ...): can't parse length %s\n", + progname, argv[3]); free(buf); return -1; } @@ -400,141 +653,186 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, // Structure related to data that is being written to memory struct Data { // Data info - int32_t bytes_grown; + int bytes_grown; uint8_t size; - bool is_float; - bool is_signed; - char * str_ptr; + char *str_ptr; // Data union union { float f; + double d; int64_t ll; + uint64_t ull; uint8_t a[8]; }; } data = { .bytes_grown = 0, .size = 0, - .is_float = false, - .is_signed = false, .str_ptr = NULL, - .ll = 0 + .ull = 1 }; - for (i = start_offset; i < len + start_offset; i++) { - data.is_float = false; - data.size = 0; + if(sizeof(long long) != sizeof(int64_t) || (data.a[0]^data.a[7]) != 1) + terminal_message(MSG_INFO, "%s (write): assumption on data types not met? " + "Check source and recompile\n", progname); + bool is_big_endian = data.a[7]; + for (i = start_offset; i < len + start_offset; i++) { // Handle the next argument if (i < argc - start_offset + 3) { + char *argi = argv[i]; + size_t arglen = strlen(argi); + + data.size = 0; + // Free string pointer if already allocated if(data.str_ptr) { free(data.str_ptr); data.str_ptr = NULL; } - // Get suffix if present - char suffix = argv[i][strlen(argv[i]) - 1]; - char lsuffix = argv[i][strlen(argv[i]) - 2]; - if ((suffix == 'L' && lsuffix == 'L') || (suffix == 'l' && lsuffix == 'l')) { - argv[i][strlen(argv[i]) - 2] = '\0'; - data.size = 8; - } else if (suffix == 'L' || suffix == 'l') { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 4; - } else if ((suffix == 'F' || suffix == 'f') && - strncmp(argv[i], "0x", 2) != 0 && strncmp(argv[i], "-0x", 3) != 0) { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 4; - } else if ((suffix == 'H' && lsuffix == 'H') || (suffix == 'h' && lsuffix == 'h')) { - argv[i][strlen(argv[i]) - 2] = '\0'; - data.size = 1; - } else if (suffix == 'H' || suffix == 'h' || suffix == 'S' || suffix == 's') { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 2; - } else if (suffix == '\'') { - data.size = 1; - } + // Remove trailing comma to allow cut and paste of lists + if(arglen > 0 && argi[arglen-1] == ',') + argi[--arglen] = 0; - // Try integers - data.ll = strtoll(argv[i], &end_ptr, 0); - if (*end_ptr || (end_ptr == argv[i])) { - // Try float - data.f = strtof(argv[i], &end_ptr); - data.is_float = true; - if (*end_ptr || (end_ptr == argv[i])) { - data.is_float = false; - // Try single character - if (argv[i][0] == '\'' && argv[i][2] == '\'') { - data.ll = argv[i][1]; - } else { - // Try string that starts and ends with quotes - if (argv[i][0] == '\"' && argv[i][strlen(argv[i]) - 1] == '\"') { - data.str_ptr = calloc(strlen(argv[i]), sizeof(char)); - if (data.str_ptr == NULL) { - avrdude_message(MSG_INFO, "%s (write str): out of memory\n", progname); - return -1; - } - // Strip start and end quotes - strncpy(data.str_ptr, argv[i] + 1, strlen(argv[i]) - 2); - } else { - avrdude_message(MSG_INFO, "\n%s (write): can't parse data '%s'\n", - progname, argv[i]); - free(buf); - if(data.str_ptr != NULL) - free(data.str_ptr); - return -1; + // Try integers and assign data size + errno = 0; + data.ull = strtoull(argi, &end_ptr, 0); + if (!(end_ptr == argi || errno)) { + unsigned int nu=0, nl=0, nh=0, ns=0, nx=0; + char *p; + + // Parse suffixes: ULL, LL, UL, L ... UHH, HH + for(p=end_ptr; *p; p++) + switch(toupper(*p)) { + case 'U': nu++; break; + case 'L': nl++; break; + case 'H': nh++; break; + case 'S': ns++; break; + default: nx++; + } + + if(nx==0 && nu<2 && nl<3 && nh<3 && ns<2) { // Could be valid integer suffix + if(nu==0 || toupper(*end_ptr) == 'U' || toupper(p[-1]) == 'U') { // If U, then must be at start or end + bool is_hex = strncasecmp(argi, "0x", 2) == 0; // Ordinary hex: 0x... without explicit +/- sign + bool is_signed = !(nu || is_hex); // Neither explicitly unsigned nor ordinary hex + bool is_outside_int64_t = 0; + bool is_out_of_range = 0; + int nhexdigs = p-argi-2; + + if(is_signed) { // Is input in range for int64_t? + errno = 0; (void) strtoll(argi, NULL, 0); + is_outside_int64_t = errno == ERANGE; } + + if(nl==0 && ns==0 && nh==0) { // No explicit data size + // Ordinary hex numbers have implicit size given by number of hex digits, including leading zeros + if(is_hex) { + data.size = nhexdigs > 8? 8: nhexdigs > 4? 4: nhexdigs > 2? 2: 1; + + } else if(is_signed) { + // Smallest size that fits signed representation + data.size = + is_outside_int64_t? 8: + data.ll < INT32_MIN || data.ll > INT32_MAX? 8: + data.ll < INT16_MIN || data.ll > INT16_MAX? 4: + data.ll < INT8_MIN || data.ll > INT8_MAX? 2: 1; + + } else { + // Smallest size that fits unsigned representation + data.size = + data.ull > UINT32_MAX? 8: + data.ull > UINT16_MAX? 4: + data.ull > UINT8_MAX? 2: 1; + } + } else if(nl==0 && nh==2 && ns==0) { // HH + data.size = 1; + if(is_outside_int64_t || (is_signed && (data.ll < INT8_MIN || data.ll > INT8_MAX))) { + is_out_of_range = 1; + data.ll = (int8_t) data.ll; + } + } else if(nl==0 && ((nh==1 && ns==0) || (nh==0 && ns==1))) { // H or S + data.size = 2; + if(is_outside_int64_t || (is_signed && (data.ll < INT16_MIN || data.ll > INT16_MAX))) { + is_out_of_range = 1; + data.ll = (int16_t) data.ll; + } + } else if(nl==1 && nh==0 && ns==0) { // L + data.size = 4; + if(is_outside_int64_t || (is_signed && (data.ll < INT32_MIN || data.ll > INT32_MAX))) { + is_out_of_range = 1; + data.ll = (int32_t) data.ll; + } + } else if(nl==2 && nh==0 && ns==0) { // LL + data.size = 8; + } + + if(is_outside_int64_t || is_out_of_range) + terminal_message(MSG_INFO, "%s (write): %s out of int%d_t range, " + "interpreted as %d-byte %lld; consider 'U' suffix\n", + progname, argi, data.size*8, data.size, data.ll); } } } - // Print warning if data size might be ambiguous - bool is_hex = (strncmp(argv[i], "0x", 2) == 0); - bool is_neg_hex = (strncmp(argv[i], "-0x", 3) == 0); - bool leading_zero = (strncmp(argv[i], "0x0", 3) == 0); - int8_t hex_digits = (strlen(argv[i]) - 2); - if(!data.size // No pre-defined size - && (is_neg_hex // Hex with - sign in front - || (is_hex && leading_zero && (hex_digits & (hex_digits - 1))) // Hex with 3, 5, 6 or 7 digits - || (!is_hex && !data.is_float && llabs(data.ll) > 0xFF && strlen(argv[i]) > 2))) // Base10 int greater than 255 - { - avrdude_message(MSG_INFO, "Warning: no size suffix specified for \"%s\". " - "Writing %d byte(s)\n", - argv[i], - llabs(data.ll) > UINT32_MAX ? 8 : - llabs(data.ll) > UINT16_MAX || data.is_float ? 4 : \ - llabs(data.ll) > UINT8_MAX ? 2 : 1); - } - // Flag if signed integer and adjust size - if (data.ll < 0 && !data.is_float) { - data.is_signed = true; - if (data.ll < INT32_MIN) + if(!data.size) { // Try double now that input was rejected as integer + data.d = strtod(argi, &end_ptr); + if (end_ptr != argi && toupper(*end_ptr) == 'D' && end_ptr[1] == 0) data.size = 8; - else if (data.ll < INT16_MIN) + } + + if(!data.size) { // Try float + data.f = strtof(argi, &end_ptr); + if (end_ptr != argi && toupper(*end_ptr) == 'F' && end_ptr[1] == 0) data.size = 4; - else if (data.ll < INT8_MIN) - data.size = 2; - else - data.size = 1; + if (end_ptr != argi && *end_ptr == 0) // no suffix defaults to float but ... + // ... do not accept valid mantissa-only floats that are integer rejects (eg, 078 or ULL overflows) + if (!is_mantissa_only(argi)) + data.size = 4; } + + if(!data.size && arglen > 1) { // Try C-style string or single character + if ((*argi == '\'' && argi[arglen-1] == '\'') || (*argi == '\"' && argi[arglen-1] == '\"')) { + char *s = calloc(arglen-1, 1); + if (s == NULL) { + terminal_message(MSG_INFO, "%s (write str): out of memory\n", progname); + free(buf); + return -1; + } + // Strip start and end quotes, and unescape C string + strncpy(s, argi+1, arglen-2); + unescape((unsigned char *) s, (unsigned char *) s); + if (*argi == '\'') { // Single C-style character + if(*s && s[1]) + terminal_message(MSG_INFO, "%s (write): only using first character of %s\n", + progname, argi); + data.ll = *s; + data.size = 1; + free(s); + } else { // C-style string + data.str_ptr = s; + } + } + } + + if(!data.size && !data.str_ptr) { + terminal_message(MSG_INFO, "%s (write): can't parse data %s\n", + progname, argi); + free(buf); + return -1; + } + + // Assume endianness is the same for double and int, and ensure little endian representation + if(is_big_endian && data.size > 1) + change_endian(data.a, data.size); } + if(data.str_ptr) { - for(int16_t j = 0; j < strlen(data.str_ptr); j++) + for(size_t j = 0; j < strlen(data.str_ptr); j++) buf[i - start_offset + data.bytes_grown++] = (uint8_t)data.str_ptr[j]; - } else { - buf[i - start_offset + data.bytes_grown] = data.a[0]; - if (llabs(data.ll) > 0x000000FF || data.size >= 2 || data.is_float) - buf[i - start_offset + ++data.bytes_grown] = data.a[1]; - if (llabs(data.ll) > 0x0000FFFF || data.size >= 4 || data.is_float) { - buf[i - start_offset + ++data.bytes_grown] = data.a[2]; - buf[i - start_offset + ++data.bytes_grown] = data.a[3]; - } - if (llabs(data.ll) > 0xFFFFFFFF || data.size == 8) { - buf[i - start_offset + ++data.bytes_grown] = data.a[4]; - buf[i - start_offset + ++data.bytes_grown] = data.a[5]; - buf[i - start_offset + ++data.bytes_grown] = data.a[6]; - buf[i - start_offset + ++data.bytes_grown] = data.a[7]; - } + } else if(data.size > 0) { + for(int k=0; k maxsize) { - avrdude_message(MSG_INFO, "%s (write): selected address and # bytes exceed " - "range for %s memory\n", - progname, memtype); + terminal_message(MSG_INFO, "%s (write): selected address and # bytes exceed " + "range for %s memory\n", progname, memtype); free(buf); return -1; } @@ -557,31 +854,31 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, if(data.str_ptr) free(data.str_ptr); - avrdude_message(MSG_NOTICE, "\nInfo: Writing %d bytes starting from address 0x%02x", - len + data.bytes_grown, addr); + terminal_message(MSG_NOTICE, "Info: writing %d bytes starting from address 0x%02lx", + len + data.bytes_grown, (long) addr); if (write_mode == WRITE_MODE_FILL) - avrdude_message(MSG_NOTICE, ". Remaining space filled with %s", argv[argc - 2]); - avrdude_message(MSG_NOTICE, "\n"); + terminal_message(MSG_NOTICE, "; remaining space filled with %s", argv[argc - 2]); + terminal_message(MSG_NOTICE, "\n"); pgm->err_led(pgm, OFF); bool werror = false; report_progress(0, 1, "Writing"); for (i = 0; i < (len + data.bytes_grown); i++) { - int32_t rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]); + int rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]); if (rc) { - avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n", - progname, buf[i], addr+i, rc); + terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n", + progname, buf[i], (long) addr+i, (int) rc); if (rc == -1) - avrdude_message(MSG_INFO, "write operation not supported on memory type \"%s\"\n", - mem->desc); + terminal_message(MSG_INFO, "%*swrite operation not supported on memory type %s\n", + (int) strlen(progname)+10, "", mem->desc); werror = true; } uint8_t b; rc = pgm->read_byte(pgm, p, mem, addr+i, &b); if (b != buf[i]) { - avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n", - progname, buf[i], addr+i, b); + terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n", + progname, buf[i], (long) addr+i, b); werror = true; } @@ -608,20 +905,20 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p, int len; if (pgm->cmd == NULL) { - avrdude_message(MSG_INFO, "The %s programmer does not support direct ISP commands.\n", - pgm->type); + terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct ISP commands\n", + progname, pgm->type); return -1; } if (spi_mode && (pgm->spi == NULL)) { - avrdude_message(MSG_INFO, "The %s programmer does not support direct SPI transfers.\n", - pgm->type); + terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct SPI transfers\n", + progname, pgm->type); return -1; } if ((argc > 5) || ((argc < 5) && (!spi_mode))) { - avrdude_message(MSG_INFO, spi_mode? + terminal_message(MSG_INFO, spi_mode? "Usage: send [ [ []]]\n": "Usage: send \n"); return -1; @@ -634,8 +931,8 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p, for (i=1; ichip_erase(pgm, p); return 0; } @@ -690,14 +987,14 @@ static int cmd_sig(PROGRAMMER * pgm, struct avrpart * p, rc = avr_signature(pgm, p); if (rc != 0) { - avrdude_message(MSG_INFO, "error reading signature data, rc=%d\n", - rc); + terminal_message(MSG_INFO, "%s (sig): error reading signature data, rc=%d\n", + progname, rc); } m = avr_locate_mem(p, "signature"); if (m == NULL) { - avrdude_message(MSG_INFO, "signature data not defined for device \"%s\"\n", - p->desc); + terminal_message(MSG_INFO, "%s (sig): signature data not defined for device %s\n", + progname, p->desc); } else { fprintf(stdout, "Device signature = 0x"); @@ -725,9 +1022,8 @@ static int cmd_parms(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[]) { if (pgm->print_parms == NULL) { - avrdude_message(MSG_INFO, "%s (parms): the %s programmer does not support " - "adjustable parameters\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (parms): the %s programmer does not support " + "adjustable parameters\n", progname, pgm->type); return -1; } pgm->print_parms(pgm); @@ -744,23 +1040,23 @@ static int cmd_vtarg(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: vtarg \n"); + terminal_message(MSG_INFO, "Usage: vtarg \n"); return -1; } v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (vtarg): can't parse voltage \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (vtarg): can't parse voltage %s\n", + progname, argv[1]); return -1; } if (pgm->set_vtarget == NULL) { - avrdude_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_vtarget(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -775,7 +1071,7 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: fosc [M|k] | off\n"); + terminal_message(MSG_INFO, "Usage: fosc [M|k] | off\n"); return -1; } v = strtod(argv[1], &endp); @@ -783,8 +1079,8 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, if (strcmp(argv[1], "off") == 0) v = 0.0; else { - avrdude_message(MSG_INFO, "%s (fosc): can't parse frequency \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (fosc): can't parse frequency %s\n", + progname, argv[1]); return -1; } } @@ -793,13 +1089,13 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, else if (*endp == 'k' || *endp == 'K') v *= 1e3; if (pgm->set_fosc == NULL) { - avrdude_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_fosc(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -814,24 +1110,24 @@ static int cmd_sck(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: sck \n"); + terminal_message(MSG_INFO, "Usage: sck \n"); return -1; } v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (sck): can't parse period \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (sck): can't parse period %s\n", + progname, argv[1]); return -1; } v *= 1e-6; /* Convert from microseconds to seconds. */ if (pgm->set_sck_period == NULL) { - avrdude_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_sck_period(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -847,39 +1143,39 @@ static int cmd_varef(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2 && argc != 3) { - avrdude_message(MSG_INFO, "Usage: varef [channel] \n"); + terminal_message(MSG_INFO, "Usage: varef [channel] \n"); return -1; } if (argc == 2) { chan = 0; v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n", + progname, argv[1]); return -1; } } else { chan = strtoul(argv[1], &endp, 10); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse channel \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (varef): can't parse channel %s\n", + progname, argv[1]); return -1; } v = strtod(argv[2], &endp); if (endp == argv[2]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n", - progname, argv[2]); + terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n", + progname, argv[2]); return -1; } } if (pgm->set_varef == NULL) { - avrdude_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_varef(pgm, chan, v)) != 0) { - avrdude_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -891,13 +1187,13 @@ static int cmd_help(PROGRAMMER * pgm, struct avrpart * p, { int i; - fprintf(stdout, "Valid commands:\n\n"); + fprintf(stdout, "Valid commands:\n"); for (i=0; iinitialize(pgm, p); return 0; } - avrdude_message(MSG_INFO, "`pgm' command unavailable for this programmer type\n"); + terminal_message(MSG_INFO, "%s: pgm command unavailable for this programmer type\n", + progname); return -1; } @@ -936,26 +1234,62 @@ static int cmd_verbose(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 1 && argc != 2) { - avrdude_message(MSG_INFO, "Usage: verbose []\n"); + terminal_message(MSG_INFO, "Usage: verbose []\n"); return -1; } if (argc == 1) { - avrdude_message(MSG_INFO, "Verbosity level: %d\n", verbose); + terminal_message(MSG_INFO, "Verbosity level: %d\n", verbose); return 0; } nverb = strtol(argv[1], &endp, 0); - if (endp == argv[2]) { - avrdude_message(MSG_INFO, "%s: can't parse verbosity level \"%s\"\n", - progname, argv[2]); + if (endp == argv[1] || *endp) { + terminal_message(MSG_INFO, "%s (verbose): can't parse verbosity level %s\n", + progname, argv[1]); return -1; } if (nverb < 0) { - avrdude_message(MSG_INFO, "%s: verbosity level must be positive: %d\n", - progname, nverb); + terminal_message(MSG_INFO, "%s: verbosity level must not be negative: %d\n", + progname, nverb); return -1; } verbose = nverb; - avrdude_message(MSG_INFO, "New verbosity level: %d\n", verbose); + terminal_message(MSG_INFO, "New verbosity level: %d\n", verbose); + + return 0; +} + +static int cmd_quell(PROGRAMMER * pgm, struct avrpart * p, + int argc, char * argv[]) +{ + int nquell; + char *endp; + + if (argc != 1 && argc != 2) { + terminal_message(MSG_INFO, "Usage: quell []\n"); + return -1; + } + if (argc == 1) { + terminal_message(MSG_INFO, "Quell level: %d\n", quell_progress); + return 0; + } + nquell = strtol(argv[1], &endp, 0); + if (endp == argv[1] || *endp) { + terminal_message(MSG_INFO, "%s (quell): can't parse quell level %s\n", + progname, argv[1]); + return -1; + } + if (nquell < 0) { + terminal_message(MSG_INFO, "%s: quell level must not be negative: %d\n", + progname, nquell); + return -1; + } + quell_progress = nquell; + terminal_message(MSG_INFO, "New quell level: %d\n", quell_progress); + + if(quell_progress > 0) + update_progress = NULL; + else + terminal_setup_update_progress(); return 0; } @@ -974,9 +1308,9 @@ static int tokenize(char * s, char *** argv) slen = strlen(s); - /* + /* * initialize allow for 20 arguments, use realloc to grow this if - * necessary + * necessary */ nargs = 20; bufsize = slen + 20; @@ -1032,7 +1366,7 @@ static int tokenize(char * s, char *** argv) } } - /* + /* * We have parsed all the args, n == argc, bufv contains an array of * pointers to each arg, and buf points to one memory block that * contains all the args, back to back, seperated by a nul @@ -1073,8 +1407,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p, } else if (strncasecmp(argv[0], cmd[i].name, len)==0) { if (hold != -1) { - avrdude_message(MSG_INFO, "%s: command \"%s\" is ambiguous\n", - progname, argv[0]); + terminal_message(MSG_INFO, "%s (cmd): command %s is ambiguous\n", + progname, argv[0]); return -1; } hold = i; @@ -1084,8 +1418,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p, if (hold != -1) return cmd[hold].func(pgm, p, argc, argv); - avrdude_message(MSG_INFO, "%s: invalid command \"%s\"\n", - progname, argv[0]); + terminal_message(MSG_INFO, "%s (cmd): invalid command %s\n", + progname, argv[0]); return -1; } @@ -1117,7 +1451,6 @@ char * terminal_get_input(const char *prompt) int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) { char * cmdbuf; - int i; char * q; int rc; int argc; @@ -1125,11 +1458,11 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) rc = 0; while ((cmdbuf = terminal_get_input("avrdude> ")) != NULL) { - /* + /* * find the start of the command, skipping any white space */ q = cmdbuf; - while (*q && isspace((int)*q)) + while (*q && isspace((unsigned char) *q)) q++; /* skip blank lines and comments */ @@ -1143,10 +1476,12 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) return argc; } +#if !defined(HAVE_LIBREADLINE) || defined(WIN32) || defined(__APPLE__) fprintf(stdout, ">>> "); - for (i=0; i= msglvl) { + va_start(ap, format); + rc = vfprintf(stderr, format, ap); + va_end(ap); + } + fflush(stderr); + + return rc; +} + + +static void update_progress_tty (int percent, double etime, char *hdr) +{ + static char hashes[51]; + static char *header; + static int last = 0; + int i; + + setvbuf(stderr, (char*)NULL, _IONBF, 0); + + hashes[50] = 0; + + memset (hashes, ' ', 50); + for (i=0; i>1)*2; + + setvbuf(stderr, (char*)NULL, _IONBF, 0); + + if (hdr) { + avrdude_message(MSG_INFO, "\n%s | ", hdr); + last = 0; + done = 0; + } + else { + while ((cnt > last) && (done == 0)) { + avrdude_message(MSG_INFO, "#"); + cnt -= 2; + } + } + + if ((percent == 100) && (done == 0)) { + avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime); + last = 0; + done = 1; + } + else + last = (percent>>1)*2; /* Make last a multiple of 2. */ + + setvbuf(stderr, (char*)NULL, _IOLBF, 0); +} + +void terminal_setup_update_progress() { + if (isatty (STDERR_FILENO)) + update_progress = update_progress_tty; + else { + update_progress = update_progress_no_tty; + /* disable all buffering of stderr for compatibility with + software that captures and redirects output to a GUI + i.e. Programmers Notepad */ + setvbuf( stderr, NULL, _IONBF, 0 ); + setvbuf( stdout, NULL, _IONBF, 0 ); + } +} diff --git a/src/term.h b/src/term.h index f114d4b0..a89927ea 100644 --- a/src/term.h +++ b/src/term.h @@ -34,6 +34,8 @@ typedef enum { int terminal_mode(PROGRAMMER * pgm, struct avrpart * p); char * terminal_get_input(const char *prompt); +void terminal_setup_update_progress(); +int terminal_message(const int msglvl, const char *format, ...); #ifdef __cplusplus } diff --git a/src/usbtiny.c b/src/usbtiny.c index 4bbaca7c..75c88be6 100644 --- a/src/usbtiny.c +++ b/src/usbtiny.c @@ -657,15 +657,39 @@ static int usbtiny_paged_load (PROGRAMMER * pgm, AVRPART * p, AVRMEM* m, unsigned int addr, unsigned int n_bytes) { unsigned int maxaddr = addr + n_bytes; - int chunk; - int function; - + int chunk, function; + OPCODE *lext, *readop; + unsigned char cmd[8]; // First determine what we're doing - if (strcmp( m->desc, "flash" ) == 0) { - function = USBTINY_FLASH_READ; - } else { - function = USBTINY_EEPROM_READ; + function = strcmp(m->desc, "eeprom")==0? + USBTINY_EEPROM_READ: USBTINY_FLASH_READ; + + // paged_load() only called for pages, so OK to set ext addr once at start + if((lext = m->op[AVR_OP_LOAD_EXT_ADDR])) { + memset(cmd, 0, sizeof(cmd)); + avr_set_bits(lext, cmd); + avr_set_addr(lext, cmd, addr/2); + if(pgm->cmd(pgm, cmd, cmd+4) < 0) + return -1; + } + + // Byte acces as work around to correctly read flash above 64 kiB + if(function == USBTINY_FLASH_READ && addr >= 0x10000) { + for(unsigned int i=0; iop[addr&1? AVR_OP_READ_HI: AVR_OP_READ_LO])) + return -1; + + memset(cmd, 0, sizeof(cmd)); + avr_set_bits(readop, cmd); + avr_set_addr(readop, cmd, addr/2); + if(pgm->cmd(pgm, cmd, cmd+4) < 0) + return -1; + m->buf[addr] = 0; + avr_get_output(readop, cmd+4, m->buf + addr); + } + + return n_bytes; } for (; addr < maxaddr; addr += chunk) {