avrdude-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avrdude-dev] Partial stk500v2 code


From: Erik Walthinsen
Subject: [avrdude-dev] Partial stk500v2 code
Date: Mon, 31 Jan 2005 14:51:44 -0800
User-agent: Debian Thunderbird 1.0 (X11/20050116)

Attached is what I've got written up so far, in the form of rewrites of some of the lower level functions in stk500.c.
diff -urN avrdude/Makefile.am avrdude-v2/Makefile.am
--- avrdude/Makefile.am 2005-01-24 13:26:11.000000000 -0800
+++ avrdude-v2/Makefile.am      2005-01-30 16:11:41.000000000 -0800
@@ -82,6 +82,9 @@
        stk500.c \
        stk500.h \
        stk500_private.h \
+       stk500v2.c \
+       stk500v2.h \
+       stk500v2_private.h \
        term.c \
        term.h
 
diff -urN avrdude/avrdude.conf.in avrdude-v2/avrdude.conf.in
--- avrdude/avrdude.conf.in     2004-12-21 17:30:30.000000000 -0800
+++ avrdude-v2/avrdude.conf.in  2005-01-30 16:13:18.000000000 -0800
@@ -15,7 +15,7 @@
 #   programmer
 #       id       = <id1> [, <id2> [, <id3>] ...] ;  # <idN> are quoted strings
 #       desc     = <description> ;                  # quoted string
-#       type     = par | stk500 | avr910;           # programmer type
+#       type     = par | stk500 | stk500v2 | avr910; # programmer type
 #       baudrate = <num> ;                          # baudrate for 
avr910-programmer
 #       vcc      = <num1> [, <num2> ... ] ;         # pin number(s)
 #       reset    = <num> ;                          # pin number
@@ -209,12 +209,24 @@
 ;
 
 programmer
+  id    = "avrispv2";
+  desc  = "Atmel AVR ISP V2";
+  type  =  stk500v2;
+;
+
+programmer
   id    = "stk500";
   desc  = "Atmel STK500";
   type  = stk500;
 ;
 
 programmer
+  id    = "stk500v2";
+  desc  = "Atmel STK500 V2";
+  type  = stk500v2;
+;
+
+programmer
   id    = "avr910";
   desc  = "Atmel Low Cost Serial Programmer";
   type  = avr910;
diff -urN avrdude/config_gram.y avrdude-v2/config_gram.y
--- avrdude/config_gram.y       2004-12-21 17:52:45.000000000 -0800
+++ avrdude-v2/config_gram.y    2005-01-30 16:14:08.000000000 -0800
@@ -33,6 +33,7 @@
 #include "ppi.h"
 #include "pgm.h"
 #include "stk500.h"
+#include "stk500v2.h"
 #include "avr910.h"
 #include "butterfly.h"
 #include "avr.h"
@@ -111,6 +112,7 @@
 %token K_SCK
 %token K_SIZE
 %token K_STK500
+%token K_STK500V2
 %token K_AVR910
 %token K_BUTTERFLY
 %token K_TYPE
@@ -296,6 +298,12 @@
     }
   } |
 
+  K_TYPE TKN_EQUAL K_STK500V2 {
+    { 
+      stk500v2_initpgm(current_prog);
+    }
+  } |
+
   K_TYPE TKN_EQUAL K_AVR910 {
     { 
       avr910_initpgm(current_prog);
diff -urN avrdude/lexer.l avrdude-v2/lexer.l
--- avrdude/lexer.l     2004-12-21 17:52:45.000000000 -0800
+++ avrdude-v2/lexer.l  2005-01-30 16:14:26.000000000 -0800
@@ -155,6 +155,7 @@
 serial           { yylval=NULL; return K_SERIAL; }
 size             { yylval=NULL; return K_SIZE; }
 stk500           { yylval=NULL; return K_STK500; }
+stk500v2         { yylval=NULL; return K_STK500V2; }
 avr910           { yylval=NULL; return K_AVR910; }
 butterfly        { yylval=NULL; return K_BUTTERFLY; }
 type             { yylval=NULL; return K_TYPE; }
diff -urN avrdude/stk500v2.c avrdude-v2/stk500v2.c
--- avrdude/stk500v2.c  1969-12-31 16:00:00.000000000 -0800
+++ avrdude-v2/stk500v2.c       2005-01-31 00:53:46.000000000 -0800
@@ -0,0 +1,1149 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2002-2004 Brian S. Dean <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* $Id: stk500v2.c,v 1.46 2004/12/22 01:52:45 bdean Exp $ */
+
+/*
+ * avrdude interface for Atmel STK500V2 programmer
+ *
+ * Note: most commands use the "universal command" feature of the
+ * programmer in a "pass through" mode, exceptions are "program
+ * enable", "paged read", and "paged write".
+ *
+ */
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "avr.h"
+#include "pgm.h"
+#include "stk500_private.h"    // temp until all code converted
+#include "stk500v2_private.h"
+#include "serial.h"
+
+#define STK500V2_XTAL 7372800U
+
+extern int    verbose;
+extern char * progname;
+extern int do_cycles;
+
+
+static int stk500v2_getparm(PROGRAMMER * pgm, unsigned parm, unsigned * value);
+static int stk500v2_setparm(PROGRAMMER * pgm, unsigned parm, unsigned value);
+static void stk500v2_print_parms1(PROGRAMMER * pgm, char * p);
+static int stk500v2_is_page_empty(unsigned int address, int page_size, 
+    const unsigned char *buf);
+
+
+static int stk500v2_send(PROGRAMMER * pgm, char * data, size_t len)
+{
+  static unsigned char command_sequence = 0;
+  char buf[275 + 6];           // max MESSAGE_BODY of 275 bytes, 6 bytes 
overhead
+  int i;
+
+  buf[0] = MESSAGE_START;
+  buf[1] = command_sequence++;
+  buf[2] = len & 0xff;
+  buf[3] = (len >> 8) & 0xff;
+  buf[4] = TOKEN;
+  memcpy(buf+5, data, len);
+
+  // calculate the XOR checksum
+  buf[5+len] = 0;
+  for (i=0;i<5+len;i++)
+    buf[5+len] ^= buf[i];
+
+  return serial_send(pgm->fd, buf, len+6);
+}
+
+
+static int stk500v2_drain(PROGRAMMER * pgm, int display)
+{
+  return serial_drain(pgm->fd, display);
+}
+
+
+static int stk500v2_recv(PROGRAMMER * pgm, char * data)
+{
+  char buf[275 + 6];           // max MESSAGE_BODY of 275 bytes, 6 bytes 
overhead
+  int len;
+  int i;
+
+  // first sync to the next message-start header
+  do {
+    serial_recv(pgm->fd, buf, 1);
+  } while (buf[0] != MESSAGE_START);
+  // now read the rest of the fixed header
+  serial_recv(pgm->fd, buf+1, 4);
+  // calculate the length
+  len = buf[2] + (buf[3] << 8);
+  // and read the rest of the message, plus checksum
+  serial_recv(pgm->fd, buf+5, len+1);
+
+  // reverse calculate the XOR checksum
+  for (i=0;i<len+5;i++)
+    buf[5+len] ^= buf[i];
+
+  // if we didn't get zero back, the checksum is wrong
+  if (buf[5+len] != 0) {
+    fprintf(stderr,"%s: stk500v2_recv: message checksum failed\n",
+            progname);
+    stk500v2_drain(pgm, 0);
+    exit(1);
+  }
+
+  // copy the message over
+  memcpy(data, buf+5, len);
+
+  // return the message length
+  return len;
+}
+
+
+static int stk500v2_getsync(PROGRAMMER * pgm)
+{
+  unsigned char buf[32], resp[32];
+  int len;
+
+  /*
+   * get in sync */
+  buf[0] = CMD_SIGN_ON;
+  stk500v2_send(pgm, buf, 1);
+  len = stk500v2_recv(pgm, resp);
+  if (resp[0] != Resp_STK_INSYNC) {
+    fprintf(stderr, 
+            "%s: stk500v2_getsync(): not in sync: resp=0x%02x\n",
+            progname, resp[0]);
+    stk500v2_drain(pgm, 0);
+    exit(1);
+  }
+
+  stk500v2_recv(pgm, resp, 1);
+  if (resp[0] != Resp_STK_OK) {
+    fprintf(stderr, 
+            "%s: stk500v2_getsync(): can't communicate with device: "
+            "resp=0x%02x\n",
+            progname, resp[0]);
+    exit(1);
+  }
+
+  return 0;
+}
+
+
+/*
+ * transmit an AVR device command and return the results; 'cmd' and
+ * 'res' must point to at least a 4 byte data buffer
+ */
+static int stk500v2_cmd(PROGRAMMER * pgm, unsigned char cmd[4],
+                      unsigned char res[4])
+{
+  unsigned char buf[32];
+
+  buf[0] = CMD_CHIP_ERASE_ISP;
+  buf[1] = cmd[0];
+  buf[2] = cmd[1];
+  buf[3] = cmd[2];
+  buf[4] = cmd[3];
+  buf[5] = Sync_CRC_EOP;
+
+  stk500v2_send(pgm, buf, 6);
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] != Resp_STK_INSYNC) {
+    fprintf(stderr, "%s: stk500v2_cmd(): programmer is out of sync\n", 
progname);
+    exit(1);
+  }
+
+  res[0] = cmd[1];
+  res[1] = cmd[2];
+  res[2] = cmd[3];
+  stk500v2_recv(pgm, &res[3], 1);
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] != Resp_STK_OK) {
+    fprintf(stderr, "%s: stk500v2_cmd(): protocol error\n", progname);
+    exit(1);
+  }
+
+  return 0;
+}
+
+
+
+/*
+ * issue the 'chip erase' command to the AVR device
+ */
+static int stk500v2_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+
+  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  pgm->pgm_led(pgm, ON);
+
+  memset(cmd, 0, sizeof(cmd));
+
+  avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
+  pgm->cmd(pgm, cmd, res);
+  usleep(p->chip_erase_delay);
+  pgm->initialize(pgm, p);
+
+  pgm->pgm_led(pgm, OFF);
+
+  return 0;
+}
+
+/*
+ * issue the 'program enable' command to the AVR device
+ */
+static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+  int len;
+  unsigned char buf[16];
+
+  buf[0] = CMD_SIGN_ON;
+  stk500v2_send(pgm, buf, 1);
+  len = stk500v2_recv(pgm, buf);
+
+  if (buf[0] != CMD_SIGN_ON) {
+    fprintf(stderr, "%s: stk500v2_program_enable(): can't sign in\n",
+            progname);
+    return -1;
+  }
+
+  if (buf[1] == STATUS_CMD_OK) {
+    return 0;
+  } else {
+    fprintf(stderr, 
+            "%s: stk500v2_program_enable(): failed to enter programming mode, 
%d\n", 
+            progname,buf[1]);
+    return -1;
+  }
+
+  return -1;
+}
+
+
+
+static int stk500v2_set_extended_parms(PROGRAMMER * pgm, int n,
+                                     unsigned char * cmd)
+{
+  unsigned char buf[16];
+  int tries=0;
+  int i;
+
+ retry:
+  
+  tries++;
+
+  buf[0] = Cmnd_STK_SET_DEVICE_EXT;
+  for (i=0; i<n; i++) {
+    buf[1+i] = cmd[i];
+  }
+  i++;
+  buf[i] = Sync_CRC_EOP;
+
+  stk500v2_send(pgm, buf, i+1);
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_NOSYNC) {
+    if (tries > 33) {
+      fprintf(stderr, "%s: stk500v2_set_extended_parms(): can't get into 
sync\n",
+              progname);
+      return -1;
+    }
+    stk500v2_getsync(pgm);
+    goto retry;
+  }
+  else if (buf[0] != Resp_STK_INSYNC) {
+    fprintf(stderr,
+            "%s: stk500v2_set_extended_parms(): protocol error, "
+            "expect=0x%02x, resp=0x%02x\n", 
+            progname, Resp_STK_INSYNC, buf[0]);
+    return -1;
+  }
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_OK) {
+    return 0;
+  }
+  else if (buf[0] == Resp_STK_NODEVICE) {
+    fprintf(stderr, "%s: stk500v2_set_extended_parms(): no device\n",
+            progname);
+    return -1;
+  }
+
+  if(buf[0] == Resp_STK_FAILED)
+  {
+      fprintf(stderr, 
+             "%s: stk500v2_set_extended_parms(): failed to set extended "
+              "device programming parameters\n", 
+              progname);
+         return -1;
+  }
+
+
+  fprintf(stderr, "%s: stk500v2_set_extended_parms(): unknown 
response=0x%02x\n",
+          progname, buf[0]);
+
+  return -1;
+}
+
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+static int stk500v2_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char buf[32];
+  AVRMEM * m;
+  int tries;
+  unsigned maj, min;
+  int rc;
+  int n_extparms = 3;
+
+  stk500v2_getparm(pgm, PARAM_SW_MAJOR, &maj);
+  stk500v2_getparm(pgm, PARAM_SW_MINOR, &min);
+
+  if ((maj > 1) || ((maj == 1) && (min > 10)))
+    n_extparms = 4;
+
+  tries = 0;
+
+ retry:
+  tries++;
+
+  memset(buf, 0, sizeof(buf));
+
+  /*
+   * set device programming parameters
+   */
+  buf[0] = Cmnd_STK_SET_DEVICE;
+
+  buf[1] = p->stk500_devcode;
+  buf[2] = 0; /* device revision */
+
+  if ((p->flags & AVRPART_SERIALOK) && (p->flags & AVRPART_PARALLELOK))
+    buf[3] = 0; /* device supports parallel and serial programming */
+  else
+    buf[3] = 1; /* device supports parallel only */
+
+  if (p->flags & AVRPART_PARALLELOK) {
+    if (p->flags & AVRPART_PSEUDOPARALLEL) {
+      buf[4] = 0; /* pseudo parallel interface */
+      n_extparms = 0;
+    }
+    else {
+      buf[4] = 1; /* full parallel interface */
+    }
+  }
+
+#if 0
+  fprintf(stderr, "%s: stk500v2_initialize(): n_extparms = %d\n", 
+          progname, n_extparms);
+#endif
+    
+  buf[5] = 1; /* polling supported - XXX need this in config file */
+  buf[6] = 1; /* programming is self-timed - XXX need in config file */
+
+  m = avr_locate_mem(p, "lock");
+  if (m)
+    buf[7] = m->size;
+  else
+    buf[7] = 0;
+
+  /*
+   * number of fuse bytes
+   */
+  buf[8] = 0;
+  m = avr_locate_mem(p, "fuse");
+  if (m)
+    buf[8] += m->size;
+  m = avr_locate_mem(p, "lfuse");
+  if (m)
+    buf[8] += m->size;
+  m = avr_locate_mem(p, "hfuse");
+  if (m)
+    buf[8] += m->size;
+  m = avr_locate_mem(p, "efuse");
+  if (m)
+    buf[8] += m->size;
+
+  m = avr_locate_mem(p, "flash");
+  if (m) {
+    buf[9] = m->readback[0];
+    buf[10] = m->readback[1];
+    if (m->paged) {
+      buf[13] = (m->page_size >> 8) & 0x00ff;
+      buf[14] = m->page_size & 0x00ff;
+    }
+    buf[17] = (m->size >> 24) & 0xff;
+    buf[18] = (m->size >> 16) & 0xff;
+    buf[19] = (m->size >> 8) & 0xff;
+    buf[20] = m->size & 0xff;
+  }
+  else {
+    buf[9]  = 0xff;
+    buf[10]  = 0xff;
+    buf[13] = 0;
+    buf[14] = 0;
+    buf[17] = 0;
+    buf[18] = 0;
+    buf[19] = 0;
+    buf[20] = 0;
+  }
+
+  m = avr_locate_mem(p, "eeprom");
+  if (m) {
+    buf[11] = m->readback[0];
+    buf[12] = m->readback[1];
+    buf[15] = (m->size >> 8) & 0x00ff;
+    buf[16] = m->size & 0x00ff;
+  }
+  else {
+    buf[11] = 0xff;
+    buf[12] = 0xff;
+    buf[15] = 0;
+    buf[16] = 0;
+  }
+
+  buf[21] = Sync_CRC_EOP;
+
+  stk500v2_send(pgm, buf, 22);
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_NOSYNC) {
+    fprintf(stderr,
+            "%s: stk500v2_initialize(): programmer not in sync, 
resp=0x%02x\n", 
+            progname, buf[0]);
+    if (tries > 33)
+      return -1;
+    stk500v2_getsync(pgm);
+    goto retry;
+    return -1;
+  }
+  else if (buf[0] != Resp_STK_INSYNC) {
+    fprintf(stderr,
+            "%s: stk500v2_initialize(): (a) protocol error, "
+            "expect=0x%02x, resp=0x%02x\n", 
+            progname, Resp_STK_INSYNC, buf[0]);
+    return -1;
+  }
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] != Resp_STK_OK) {
+    fprintf(stderr,
+            "%s: stk500v2_initialize(): (b) protocol error, "
+            "expect=0x%02x, resp=0x%02x\n", 
+            progname, Resp_STK_OK, buf[0]);
+    return -1;
+  }
+
+  if (n_extparms) {
+    if ((p->pagel == 0) || (p->bs2 == 0)) {
+      fprintf(stderr, 
+              "%s: please define PAGEL and BS2 signals in the configuration "
+              "file for part %s\n", 
+              progname, p->desc);
+    }
+    else {
+      buf[0] = n_extparms+1;
+
+      /*
+       * m is currently pointing to eeprom memory if the part has it
+       */
+      if (m)
+        buf[1] = m->page_size;
+      else
+        buf[1] = 0;
+      
+      buf[2] = p->pagel;
+      buf[3] = p->bs2;
+      
+      if (n_extparms == 4) {
+        if (p->reset_disposition == RESET_DEDICATED)
+          buf[4] = 0;
+        else
+          buf[4] = 1;
+      }
+      
+      rc = stk500v2_set_extended_parms(pgm, n_extparms+1, buf);
+      if (rc) {
+        fprintf(stderr, "%s: stk500v2_initialize(): failed\n", progname);
+        exit(1);
+      }
+    }
+  }
+
+  return pgm->program_enable(pgm, p);
+}
+
+
+static void stk500v2_disable(PROGRAMMER * pgm)
+{
+  unsigned char buf[16];
+  int tries=0;
+
+ retry:
+  
+  tries++;
+
+  buf[0] = Cmnd_STK_LEAVE_PROGMODE;
+  buf[1] = Sync_CRC_EOP;
+
+  stk500v2_send(pgm, buf, 2);
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_NOSYNC) {
+    if (tries > 33) {
+      fprintf(stderr, "%s: stk500v2_disable(): can't get into sync\n",
+              progname);
+      return;
+    }
+    stk500v2_getsync(pgm);
+    goto retry;
+  }
+  else if (buf[0] != Resp_STK_INSYNC) {
+    fprintf(stderr,
+            "%s: stk500v2_disable(): protocol error, expect=0x%02x, "
+            "resp=0x%02x\n", 
+            progname, Resp_STK_INSYNC, buf[0]);
+    return;
+  }
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_OK) {
+    return;
+  }
+  else if (buf[0] == Resp_STK_NODEVICE) {
+    fprintf(stderr, "%s: stk500v2_disable(): no device\n",
+            progname);
+    return;
+  }
+
+  fprintf(stderr, "%s: stk500v2_disable(): unknown response=0x%02x\n",
+          progname, buf[0]);
+
+  return;
+}
+
+static void stk500v2_enable(PROGRAMMER * pgm)
+{
+  return;
+}
+
+
+static int stk500v2_open(PROGRAMMER * pgm, char * port)
+{
+  strcpy(pgm->port, port);
+  if (pgm->baudrate)
+    pgm->fd = serial_open(port, pgm->baudrate);
+  else
+    pgm->fd = serial_open(port, 115200);
+
+  /*
+   * drain any extraneous input
+   */
+  stk500v2_drain(pgm, 0);
+
+  stk500v2_getsync(pgm);
+
+  stk500v2_drain(pgm, 0);
+
+  return 0;
+}
+
+
+static void stk500v2_close(PROGRAMMER * pgm)
+{
+  serial_close(pgm->fd);
+  pgm->fd = -1;
+}
+
+
+static int stk500v2_loadaddr(PROGRAMMER * pgm, unsigned int addr)
+{
+  unsigned char buf[16];
+  int tries;
+
+  tries = 0;
+ retry:
+  tries++;
+  buf[0] = Cmnd_STK_LOAD_ADDRESS;
+  buf[1] = addr & 0xff;
+  buf[2] = (addr >> 8) & 0xff;
+  buf[3] = Sync_CRC_EOP;
+
+  stk500v2_send(pgm, buf, 4);
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_NOSYNC) {
+    if (tries > 33) {
+      fprintf(stderr, "%s: stk500v2_loadaddr(): can't get into sync\n",
+              progname);
+      return -1;
+    }
+    stk500v2_getsync(pgm);
+    goto retry;
+  }
+  else if (buf[0] != Resp_STK_INSYNC) {
+    fprintf(stderr,
+            "%s: stk500v2_loadaddr(): (a) protocol error, "
+            "expect=0x%02x, resp=0x%02x\n", 
+            progname, Resp_STK_INSYNC, buf[0]);
+    return -1;
+  }
+
+  stk500v2_recv(pgm, buf, 1);
+  if (buf[0] == Resp_STK_OK) {
+    return 0;
+  }
+
+  fprintf(stderr,
+          "%s: loadaddr(): (b) protocol error, "
+          "expect=0x%02x, resp=0x%02x\n", 
+          progname, Resp_STK_INSYNC, buf[0]);
+
+  return -1;
+}
+
+
+static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                              int page_size, int n_bytes)
+{
+  unsigned char buf[16];
+  int memtype;
+  unsigned int addr;
+  int a_div;
+  int block_size;
+  int tries;
+  unsigned int n;
+  int flash;
+
+  if (page_size == 0) {
+    page_size = 128;
+  }
+
+  if (strcmp(m->desc, "flash") == 0) {
+    memtype = 'F';
+    flash = 1;
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    memtype = 'E';
+    flash = 0;
+  }
+  else {
+    return -2;
+  }
+
+  if ((m->op[AVR_OP_LOADPAGE_LO]) || (m->op[AVR_OP_READ_LO]))
+    a_div = 2;
+  else
+    a_div = 1;
+
+  if (n_bytes > m->size) {
+    n_bytes = m->size;
+    n = m->size;
+  }
+  else {
+    if ((n_bytes % page_size) != 0) {
+      n = n_bytes + page_size - (n_bytes % page_size);
+    }
+    else {
+      n = n_bytes;
+    }
+  }
+
+#if 0
+  fprintf(stderr, 
+          "n_bytes   = %d\n"
+          "n         = %u\n"
+          "a_div     = %d\n"
+          "page_size = %d\n",
+          n_bytes, n, a_div, page_size);
+#endif     
+
+  for (addr = 0; addr < n; addr += page_size) {
+    report_progress (addr, n_bytes, NULL);
+    
+       if (addr + page_size > n_bytes) {
+          block_size = n_bytes % page_size;
+       }
+       else {
+          block_size = page_size;
+       }
+  
+    /* Only skip on empty page if programming flash. */
+    if (flash) {
+      if (stk500v2_is_page_empty(addr, block_size, m->buf)) {
+          continue;
+      }
+    }
+    tries = 0;
+  retry:
+    tries++;
+    stk500v2_loadaddr(pgm, addr/a_div);
+    buf[0] = Cmnd_STK_PROG_PAGE;
+    buf[1] = (block_size >> 8) & 0xff;
+    buf[2] = block_size & 0xff;
+    buf[3] = memtype;
+    stk500v2_send(pgm, buf, 4);
+
+       stk500v2_send(pgm, &m->buf[addr], block_size);
+
+    buf[0] = Sync_CRC_EOP;
+    stk500v2_send(pgm, buf, 1);
+
+    stk500v2_recv(pgm, buf, 1);
+    if (buf[0] == Resp_STK_NOSYNC) {
+      if (tries > 33) {
+        fprintf(stderr, "\n%s: stk500v2_paged_write(): can't get into sync\n",
+                progname);
+        return -3;
+      }
+      stk500v2_getsync(pgm);
+      goto retry;
+    }
+    else if (buf[0] != Resp_STK_INSYNC) {
+      fprintf(stderr,
+              "\n%s: stk500v2_paged_write(): (a) protocol error, "
+              "expect=0x%02x, resp=0x%02x\n", 
+              progname, Resp_STK_INSYNC, buf[0]);
+      return -4;
+    }
+    
+    stk500v2_recv(pgm, buf, 1);
+    if (buf[0] != Resp_STK_OK) {
+      fprintf(stderr,
+              "\n%s: stk500v2_paged_write(): (a) protocol error, "
+              "expect=0x%02x, resp=0x%02x\n", 
+              progname, Resp_STK_INSYNC, buf[0]);
+      return -5;
+    }
+  }
+
+  return n_bytes;
+}
+
+static int stk500v2_is_page_empty(unsigned int address, int page_size, 
+                                const unsigned char *buf)
+{
+    int i;
+    for(i = 0; i < page_size; i++) {
+        if(buf[address + i] != 0xFF) {
+            /* Page is not empty. */
+            return(0);
+        }
+    }
+    
+    /* Page is empty. */
+    return(1);
+}
+
+static int stk500v2_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                             int page_size, int n_bytes)
+{
+  unsigned char buf[16];
+  int memtype;
+  unsigned int addr;
+  int a_div;
+  int tries;
+  unsigned int n;
+  int block_size;
+
+  if (strcmp(m->desc, "flash") == 0) {
+    memtype = 'F';
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    memtype = 'E';
+  }
+  else {
+    return -2;
+  }
+
+  if ((m->op[AVR_OP_LOADPAGE_LO]) || (m->op[AVR_OP_READ_LO]))
+    a_div = 2;
+  else
+    a_div = 1;
+
+  if (n_bytes > m->size) {
+    n_bytes = m->size;
+    n = m->size;
+  }
+  else {
+    if ((n_bytes % page_size) != 0) {
+      n = n_bytes + page_size - (n_bytes % page_size);
+    }
+    else {
+      n = n_bytes;
+    }
+  }
+
+  for (addr = 0; addr < n; addr += page_size) {
+    report_progress (addr, n_bytes, NULL);
+
+       if (addr + page_size > n_bytes) {
+          block_size = n_bytes % page_size;
+       }
+       else {
+          block_size = page_size;
+       }
+  
+    tries = 0;
+  retry:
+    tries++;
+    stk500v2_loadaddr(pgm, addr/a_div);
+    buf[0] = Cmnd_STK_READ_PAGE;
+    buf[1] = (block_size >> 8) & 0xff;
+    buf[2] = block_size & 0xff;
+    buf[3] = memtype;
+    buf[4] = Sync_CRC_EOP;
+    stk500v2_send(pgm, buf, 5);
+
+    stk500v2_recv(pgm, buf, 1);
+    if (buf[0] == Resp_STK_NOSYNC) {
+      if (tries > 33) {
+        fprintf(stderr, "\n%s: stk500v2_paged_load(): can't get into sync\n",
+                progname);
+        return -3;
+      }
+      stk500v2_getsync(pgm);
+      goto retry;
+    }
+    else if (buf[0] != Resp_STK_INSYNC) {
+      fprintf(stderr,
+              "\n%s: stk500v2_paged_load(): (a) protocol error, "
+              "expect=0x%02x, resp=0x%02x\n", 
+              progname, Resp_STK_INSYNC, buf[0]);
+      return -4;
+    }
+
+    stk500v2_recv(pgm, &m->buf[addr], block_size);
+
+    stk500v2_recv(pgm, buf, 1);
+    if (buf[0] != Resp_STK_OK) {
+      fprintf(stderr,
+              "\n%s: stk500v2_paged_load(): (a) protocol error, "
+              "expect=0x%02x, resp=0x%02x\n", 
+              progname, Resp_STK_INSYNC, buf[0]);
+      return -5;
+    }
+  }
+
+  return n_bytes;
+}
+
+
+static int stk500v2_set_vtarget(PROGRAMMER * pgm, double v)
+{
+  unsigned uaref, utarg;
+
+  utarg = (unsigned)((v + 0.049) * 10);
+
+  if (stk500v2_getparm(pgm, Parm_STK_VADJUST, &uaref) != 0) {
+    fprintf(stderr,
+           "%s: stk500v2_set_vtarget(): cannot obtain V[aref]\n",
+           progname);
+    return -1;
+  }
+
+  if (uaref > utarg) {
+    fprintf(stderr,
+           "%s: stk500v2_set_vtarget(): reducing V[aref] from %.1f to %.1f\n",
+           progname, uaref / 10.0, v);
+    if (stk500v2_setparm(pgm, Parm_STK_VADJUST, utarg)
+       != 0)
+      return -1;
+  }
+  return stk500v2_setparm(pgm, Parm_STK_VTARGET, utarg);
+}
+
+
+static int stk500v2_set_varef(PROGRAMMER * pgm, double v)
+{
+  unsigned uaref, utarg;
+
+  uaref = (unsigned)((v + 0.049) * 10);
+
+  if (stk500v2_getparm(pgm, Parm_STK_VTARGET, &utarg) != 0) {
+    fprintf(stderr,
+           "%s: stk500v2_set_varef(): cannot obtain V[target]\n",
+           progname);
+    return -1;
+  }
+
+  if (uaref > utarg) {
+    fprintf(stderr,
+           "%s: stk500v2_set_varef(): V[aref] must not be greater than "
+           "V[target] = %.1f\n",
+           progname, utarg / 10.0);
+    return -1;
+  }
+  return stk500v2_setparm(pgm, Parm_STK_VADJUST, uaref);
+}
+
+
+static int stk500v2_set_fosc(PROGRAMMER * pgm, double v)
+{
+  unsigned prescale, cmatch, fosc;
+  static unsigned ps[] = {
+    1, 8, 32, 64, 128, 256, 1024
+  };
+  int idx, rc;
+
+  prescale = cmatch = 0;
+  if (v > 0.0) {
+    if (v > STK500V2_XTAL / 2) {
+      const char *unit;
+      if (v > 1e6) {
+        v /= 1e6;
+        unit = "MHz";
+      } else if (v > 1e3) {
+        v /= 1e3;
+        unit = "kHz";
+      } else
+        unit = "Hz";
+      fprintf(stderr,
+          "%s: stk500v2_set_fosc(): f = %.3f %s too high, using %.3f MHz\n",
+          progname, v, unit, STK500V2_XTAL / 2e6);
+      fosc = STK500V2_XTAL / 2;
+    } else
+      fosc = (unsigned)v;
+    
+    for (idx = 0; idx < sizeof(ps) / sizeof(ps[0]); idx++) {
+      if (fosc >= STK500V2_XTAL / (256 * ps[idx] * 2)) {
+        /* this prescaler value can handle our frequency */
+        prescale = idx + 1;
+        cmatch = (unsigned)(STK500V2_XTAL / (2 * fosc * ps[idx])) - 1;
+        break;
+      }
+    }
+    if (idx == sizeof(ps) / sizeof(ps[0])) {
+      fprintf(stderr, "%s: stk500v2_set_fosc(): f = %u Hz too low, %u Hz 
min\n",
+          progname, fosc, STK500V2_XTAL / (256 * 1024 * 2));
+      return -1;
+    }
+  }
+  
+  if ((rc = stk500v2_setparm(pgm, Parm_STK_OSC_PSCALE, prescale)) != 0
+      || (rc = stk500v2_setparm(pgm, Parm_STK_OSC_CMATCH, cmatch)) != 0)
+    return rc;
+  
+  return 0;
+}
+
+
+/* This code assumes that each count of the SCK duration parameter
+   represents 8/f, where f is the clock frequency of the STK500V2 master
+   processors (not the target).  This number comes from Atmel
+   application note AVR061.  It appears that the STK500V2 bit bangs SCK.
+   For small duration values, the actual SCK width is larger than
+   expected.  As the duration value increases, the SCK width error
+   diminishes. */
+static int stk500v2_set_sck_period(PROGRAMMER * pgm, double v)
+{
+  int dur;
+  double min, max;
+
+  min = 8.0 / STK500V2_XTAL;
+  max = 255 * min;
+  dur = v / min + 0.5;
+  
+  if (v < min) {
+      dur = 1;
+      fprintf(stderr,
+             "%s: stk500v2_set_sck_period(): p = %.1f us too small, using %.1f 
us\n",
+             progname, v / 1e-6, dur * min / 1e-6);
+  } else if (v > max) {
+      dur = 255;
+      fprintf(stderr,
+             "%s: stk500v2_set_sck_period(): p = %.1f us too large, using %.1f 
us\n",
+             progname, v / 1e-6, dur * min / 1e-6);
+  }
+  
+  return stk500v2_setparm(pgm, Parm_STK_SCK_DURATION, dur);
+}
+
+
+static int stk500v2_getparm(PROGRAMMER * pgm, unsigned char parm, unsigned 
char * value)
+{
+  unsigned char buf[16];
+  int len;
+  unsigned v;
+
+  buf[0] = CMD_GET_PARAMETER;
+  buf[1] = parm;
+
+  stk500v2_send(pgm, buf, 2);
+  len = stk500v2_recv(pgm, buf);
+  if (buf[1] != STATUS_CMD_OK) {
+    fprintf(stderr, "\n%s: stk500v2_getparm(): can't get parameter\n",
+            progname);
+    return -1;
+  }
+
+  *value = buf[2];
+
+  return 0;
+}
+
+  
+static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned 
char value)
+{
+  unsigned char buf[16];
+  int len;
+
+  buf[0] = CMD_SET_PARAMETER;
+  buf[1] = parm;
+  buf[2] = value;
+
+  stk500v2_send(pgm, buf, 3);
+  len = stk500v2_recv(pgm, buf);
+
+  if (buf[1] != STATUS_CMD_OK) {
+    fprintf(stderr, "\n%s: stk500v2_setparm(): can't set parameter\n",
+            progname);
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static void stk500v2_display(PROGRAMMER * pgm, char * p)
+{
+  unsigned maj, min, hdw, topcard;
+  const char *topcard_name;
+
+  stk500v2_getparm(pgm, PARAM_HW_VER, &hdw);
+  stk500v2_getparm(pgm, PARAM_SW_MAJOR, &maj);
+  stk500v2_getparm(pgm, PARAM_SW_MINOR, &min);
+  stk500v2_getparm(pgm, PARAM_TOPCARD_DETECT, &topcard);
+
+  fprintf(stderr, "%sHardware Version: %d\n", p, hdw);
+  fprintf(stderr, "%sFirmware Version: %d.%d\n", p, maj, min);
+
+  if (1) {                     // should check to see if it's a stk500 first
+    switch (topcard) {
+      case 0xAA: topcard_name = "STK501", break;
+      case 0x55: topcard_name = "STK502", break;
+      case 0xFA: topcard_name = "STK503", break;
+      case 0xEE: topcard_name = "STK504", break;
+      case 0xE4: topcard_name = "STK505", break;
+      case 0xDD: topcard_name = "STK520", break;
+      default: topcard_name = "Unknown", break;
+    }
+    fprintf(stderr, "%sTopcard         : %s\n", p, n);
+  }
+  stk500v2_print_parms1(pgm, p);
+
+  return;
+}
+
+
+static void stk500v2_print_parms1(PROGRAMMER * pgm, char * p)
+{
+  unsigned vtarget, vadjust, osc_pscale, osc_cmatch, sck_duration;
+
+  stk500v2_getparm(pgm, PARAM_VTARGET, &vtarget);
+  stk500v2_getparm(pgm, PARAM_VADJUST, &vadjust);
+  stk500v2_getparm(pgm, PARAM_OSC_PSCALE, &osc_pscale);
+  stk500v2_getparm(pgm, PARAM_OSC_CMATCH, &osc_cmatch);
+  stk500v2_getparm(pgm, PARAM_SCK_DURATION, &sck_duration);
+
+  fprintf(stderr, "%sVtarget         : %.1f V\n", p, vtarget / 10.0);
+  fprintf(stderr, "%sVaref           : %.1f V\n", p, vadjust / 10.0);
+  fprintf(stderr, "%sOscillator      : ", p);
+  if (osc_pscale == 0)
+    fprintf(stderr, "Off\n");
+  else {
+    int prescale = 1;
+    double f = STK500V2_XTAL / 2;
+    const char *unit;
+
+    switch (osc_pscale) {
+      case 2: prescale = 8; break;
+      case 3: prescale = 32; break;
+      case 4: prescale = 64; break;
+      case 5: prescale = 128; break;
+      case 6: prescale = 256; break;
+      case 7: prescale = 1024; break;
+    }
+    f /= prescale;
+    f /= (osc_cmatch + 1);
+    if (f > 1e6) {
+      f /= 1e6;
+      unit = "MHz";
+    } else if (f > 1e3) {
+      f /= 1000;
+      unit = "kHz";
+    } else
+      unit = "Hz";
+    fprintf(stderr, "%.3f %s\n", f, unit);
+  }
+  fprintf(stderr, "%sSCK period      : %.1f us\n", p, 
+         sck_duration * 8.0e6 / STK500V2_XTAL + 0.05);
+
+  return;
+}
+
+
+static void stk500v2_print_parms(PROGRAMMER * pgm)
+{
+  stk500v2_print_parms1(pgm, "");
+}
+
+
+void stk500v2_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "STK500V2");
+
+  /*
+   * mandatory functions
+   */
+  pgm->initialize     = stk500v2_initialize;
+  pgm->display        = stk500v2_display;
+  pgm->enable         = stk500v2_enable;
+  pgm->disable        = stk500v2_disable;
+  pgm->program_enable = stk500v2_program_enable;
+  pgm->chip_erase     = stk500v2_chip_erase;
+  pgm->cmd            = stk500v2_cmd;
+  pgm->open           = stk500v2_open;
+  pgm->close          = stk500v2_close;
+
+  /*
+   * optional functions
+   */
+  pgm->paged_write    = stk500v2_paged_write;
+  pgm->paged_load     = stk500v2_paged_load;
+  pgm->print_parms    = stk500v2_print_parms;
+  pgm->set_vtarget    = stk500v2_set_vtarget;
+  pgm->set_varef      = stk500v2_set_varef;
+  pgm->set_fosc       = stk500v2_set_fosc;
+  pgm->set_sck_period = stk500v2_set_sck_period;
+  pgm->page_size      = 256;
+}
diff -urN avrdude/stk500v2.h avrdude-v2/stk500v2.h
--- avrdude/stk500v2.h  1969-12-31 16:00:00.000000000 -0800
+++ avrdude-v2/stk500v2.h       2005-01-30 16:16:49.000000000 -0800
@@ -0,0 +1,29 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2002-2004  Brian S. Dean <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* $Id: stk500.h,v 1.9 2004/12/22 01:52:45 bdean Exp $ */
+
+#ifndef __stk500v2_h__
+#define __stk500v2_h__
+
+void stk500v2_initpgm (PROGRAMMER * pgm);
+
+#endif
+
+
diff -urN avrdude/stk500v2_private.h avrdude-v2/stk500v2_private.h
--- avrdude/stk500v2_private.h  1969-12-31 16:00:00.000000000 -0800
+++ avrdude-v2/stk500v2_private.h       2005-01-30 17:14:25.000000000 -0800
@@ -0,0 +1,134 @@
+//**** ATMEL AVR - A P P L I C A T I O N   N O T E  ************************
+//*
+//* Title:             AVR068 - STK500 Communication Protocol
+//* Filename:          command.h
+//* Version:           1.0
+//* Last updated:      10.01.2005
+//*
+//* Support E-mail:    address@hidden
+//*
+//**************************************************************************
+
+// *****************[ STK message constants ]***************************
+
+#define MESSAGE_START                       0x1B        //= ESC = 27 decimal
+#define TOKEN                               0x0E
+
+// *****************[ STK general command constants ]**************************
+
+#define CMD_SIGN_ON                         0x01
+#define CMD_SET_PARAMETER                   0x02
+#define CMD_GET_PARAMETER                   0x03
+#define CMD_SET_DEVICE_PARAMETERS           0x04
+#define CMD_OSCCAL                          0x05
+#define CMD_LOAD_ADDRESS                    0x06
+#define CMD_FIRMWARE_UPGRADE                0x07
+
+
+// *****************[ STK ISP command constants ]******************************
+
+#define CMD_ENTER_PROGMODE_ISP              0x10
+#define CMD_LEAVE_PROGMODE_ISP              0x11
+#define CMD_CHIP_ERASE_ISP                  0x12
+#define CMD_PROGRAM_FLASH_ISP               0x13
+#define CMD_READ_FLASH_ISP                  0x14
+#define CMD_PROGRAM_EEPROM_ISP              0x15
+#define CMD_READ_EEPROM_ISP                 0x16
+#define CMD_PROGRAM_FUSE_ISP                0x17
+#define CMD_READ_FUSE_ISP                   0x18
+#define CMD_PROGRAM_LOCK_ISP                0x19
+#define CMD_READ_LOCK_ISP                   0x1A
+#define CMD_READ_SIGNATURE_ISP              0x1B
+#define CMD_READ_OSCCAL_ISP                 0x1C
+#define CMD_SPI_MULTI                       0x1D
+
+// *****************[ STK PP command constants ]*******************************
+
+#define CMD_ENTER_PROGMODE_PP               0x20
+#define CMD_LEAVE_PROGMODE_PP               0x21
+#define CMD_CHIP_ERASE_PP                   0x22
+#define CMD_PROGRAM_FLASH_PP                0x23
+#define CMD_READ_FLASH_PP                   0x24
+#define CMD_PROGRAM_EEPROM_PP               0x25
+#define CMD_READ_EEPROM_PP                  0x26
+#define CMD_PROGRAM_FUSE_PP                 0x27
+#define CMD_READ_FUSE_PP                    0x28
+#define CMD_PROGRAM_LOCK_PP                 0x29
+#define CMD_READ_LOCK_PP                    0x2A
+#define CMD_READ_SIGNATURE_PP               0x2B
+#define CMD_READ_OSCCAL_PP                  0x2C    
+
+#define CMD_SET_CONTROL_STACK               0x2D
+
+// *****************[ STK HVSP command constants ]*****************************
+
+#define CMD_ENTER_PROGMODE_HVSP             0x30
+#define CMD_LEAVE_PROGMODE_HVSP             0x31
+#define CMD_CHIP_ERASE_HVSP                 0x32
+#define CMD_PROGRAM_FLASH_HVSP              0x33
+#define CMD_READ_FLASH_HVSP                 0x34
+#define CMD_PROGRAM_EEPROM_HVSP             0x35
+#define CMD_READ_EEPROM_HVSP                0x36
+#define CMD_PROGRAM_FUSE_HVSP               0x37
+#define CMD_READ_FUSE_HVSP                  0x38
+#define CMD_PROGRAM_LOCK_HVSP               0x39
+#define CMD_READ_LOCK_HVSP                  0x3A
+#define CMD_READ_SIGNATURE_HVSP             0x3B
+#define CMD_READ_OSCCAL_HVSP                0x3C
+
+// *****************[ STK test command constants ]***************************
+
+#define CMD_ENTER_TESTMODE                  0x60
+#define CMD_LEAVE_TESTMODE                  0x61
+#define CMD_CHIP_WRITE                      0x62
+#define CMD_PROGRAM_FLASH_PARTIAL           0x63
+#define CMD_PROGRAM_EEPROM_PARTIAL          0x64
+#define CMD_PROGRAM_SIGNATURE_ROW           0x65
+#define CMD_READ_FLASH_MARGIN               0x66
+#define CMD_READ_EEPROM_MARGIN              0x67
+#define CMD_READ_SIGNATURE_ROW_MARGIN       0x68
+#define CMD_PROGRAM_TEST_FUSE               0x69
+#define CMD_READ_TEST_FUSE                  0x6A
+#define CMD_PROGRAM_HIDDEN_FUSE_LOW         0x6B
+#define CMD_READ_HIDDEN_FUSE_LOW            0x6C
+#define CMD_PROGRAM_HIDDEN_FUSE_HIGH        0x6D
+#define CMD_READ_HIDDEN_FUSE_HIGH           0x6E
+#define CMD_PROGRAM_HIDDEN_FUSE_EXT         0x6F
+#define CMD_READ_HIDDEN_FUSE_EXT            0x70
+
+// *****************[ STK status constants ]***************************
+
+// Success
+#define STATUS_CMD_OK                       0x00
+
+// Warnings
+#define STATUS_CMD_TOUT                     0x80
+#define STATUS_RDY_BSY_TOUT                 0x81
+#define STATUS_SET_PARAM_MISSING            0x82
+
+// Errors
+#define STATUS_CMD_FAILED                   0xC0
+#define STATUS_CKSUM_ERROR                  0xC1
+#define STATUS_CMD_UNKNOWN                  0xC9
+
+// *****************[ STK parameter constants ]***************************
+#define PARAM_BUILD_NUMBER_LOW              0x80
+#define PARAM_BUILD_NUMBER_HIGH             0x81
+#define PARAM_HW_VER                        0x90
+#define PARAM_SW_MAJOR                      0x91
+#define PARAM_SW_MINOR                      0x92
+#define PARAM_VTARGET                       0x94
+#define PARAM_VADJUST                       0x95
+#define PARAM_OSC_PSCALE                    0x96
+#define PARAM_OSC_CMATCH                    0x97
+#define PARAM_SCK_DURATION                  0x98
+#define PARAM_TOPCARD_DETECT                0x9A
+#define PARAM_STATUS                        0x9C
+#define PARAM_DATA                          0x9D
+#define PARAM_RESET_POLARITY                0x9E
+#define PARAM_CONTROLLER_INIT               0x9F
+
+// *****************[ STK answer constants ]***************************
+
+#define ANSWER_CKSUM_ERROR                  0xB0
+

reply via email to

[Prev in Thread] Current Thread [Next in Thread]