/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 2005 Erik Walthinsen
 * Copyright (C) 2002-2004 Brian S. Dean <bsd@bsdhome.com>
 * Copyright (C) 2006 David Moore
 * Copyright (C) 2006,2007,2010 Joerg Wunsch <j@uriah.heep.sax.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id$ */
/* Based on Id: stk500.c,v 1.46 2004/12/22 01:52:45 bdean Exp */

/*
 * avrdude interface for Atmel STK500V2 programmer
 *
 * As the AVRISP mkII device is basically an STK500v2 one that can
 * only talk across USB, and that misses any kind of framing protocol,
 * this is handled here as well.
 *
 * 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <time.h>

#if !defined(WIN32NATIVE)
#  include <sys/time.h>
#endif

#include "avrdude.h"
#include "libavrdude.h"

#include "stk500_private.h"	// temp until all code converted
#include "stk500v2.h"
#include "stk500v2_private.h"
#include "usbdevs.h"

/*
 * We need to import enough from the JTAG ICE mkII definitions to be
 * able to talk to the ICE, query some parameters etc.  The macro
 * JTAGMKII_PRIVATE_EXPORTED limits the amount of definitions that
 * jtagmkII_private.h will export, so to avoid conflicts with those
 * names that are identical to the STK500v2 ones.
 */
#include "jtagmkII.h"           // public interfaces from jtagmkII.c
#define JTAGMKII_PRIVATE_EXPORTED
#include "jtagmkII_private.h"

#include "jtag3.h"           // public interfaces from jtagmkII.c
#define JTAG3_PRIVATE_EXPORTED
#include "jtag3_private.h"

#define STK500V2_XTAL 7372800U

// Timeout (in seconds) for waiting for serial response
#define SERIAL_TIMEOUT 2

// Retry count
#define RETRIES 0

#if 0
#define DEBUG(...) avrdude_message(MSG_INFO, __VA_ARGS__)
#else
#define DEBUG(...)
#endif

#if 0
#define DEBUGRECV(...) avrdude_message(MSG_INFO, __VA_ARGS__)
#else
#define DEBUGRECV(...)
#endif

enum hvmode
{
  PPMODE, HVSPMODE
};


#define PDATA(pgm) ((struct pdata *)(pgm->cookie))


/*
 * Data structure for displaying STK600 routing and socket cards.
 */
struct carddata
{
  int id;
  const char *name;
};

static const char *pgmname[] =
{
  "unknown",
  "STK500",
  "AVRISP",
  "AVRISP mkII",
  "JTAG ICE mkII",
  "STK600",
  "JTAGICE3",
};

struct jtagispentry
{
  unsigned char cmd;
  unsigned short size;
#define SZ_READ_FLASH_EE USHRT_MAX
#define SZ_SPI_MULTI     (USHRT_MAX - 1)
};

// static const struct jtagispentry jtagispcmds[] = {
//   /* generic */
//   { CMD_SET_PARAMETER, 2 },
//   { CMD_GET_PARAMETER, 3 },
//   { CMD_OSCCAL, 2 },
//   { CMD_LOAD_ADDRESS, 2 },
//   /* ISP mode */
//   { CMD_ENTER_PROGMODE_ISP, 2 },
//   { CMD_LEAVE_PROGMODE_ISP, 2 },
//   { CMD_CHIP_ERASE_ISP, 2 },
//   { CMD_PROGRAM_FLASH_ISP, 2 },
//   { CMD_READ_FLASH_ISP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_EEPROM_ISP, 2 },
//   { CMD_READ_EEPROM_ISP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_FUSE_ISP, 3 },
//   { CMD_READ_FUSE_ISP, 4 },
//   { CMD_PROGRAM_LOCK_ISP, 3 },
//   { CMD_READ_LOCK_ISP, 4 },
//   { CMD_READ_SIGNATURE_ISP, 4 },
//   { CMD_READ_OSCCAL_ISP, 4 },
//   { CMD_SPI_MULTI, SZ_SPI_MULTI },
//   /* all HV modes */
//   { CMD_SET_CONTROL_STACK, 2 },
//   /* HVSP mode */
//   { CMD_ENTER_PROGMODE_HVSP, 2 },
//   { CMD_LEAVE_PROGMODE_HVSP, 2 },
//   { CMD_CHIP_ERASE_HVSP, 2 },
//   { CMD_PROGRAM_FLASH_HVSP, 2 },
//   { CMD_READ_FLASH_HVSP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_EEPROM_HVSP, 2 },
//   { CMD_READ_EEPROM_HVSP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_FUSE_HVSP, 2 },
//   { CMD_READ_FUSE_HVSP, 3 },
//   { CMD_PROGRAM_LOCK_HVSP, 2 },
//   { CMD_READ_LOCK_HVSP, 3 },
//   { CMD_READ_SIGNATURE_HVSP, 3 },
//   { CMD_READ_OSCCAL_HVSP, 3 },
//   /* PP mode */
//   { CMD_ENTER_PROGMODE_PP, 2 },
//   { CMD_LEAVE_PROGMODE_PP, 2 },
//   { CMD_CHIP_ERASE_PP, 2 },
//   { CMD_PROGRAM_FLASH_PP, 2 },
//   { CMD_READ_FLASH_PP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_EEPROM_PP, 2 },
//   { CMD_READ_EEPROM_PP, SZ_READ_FLASH_EE },
//   { CMD_PROGRAM_FUSE_PP, 2 },
//   { CMD_READ_FUSE_PP, 3 },
//   { CMD_PROGRAM_LOCK_PP, 2 },
//   { CMD_READ_LOCK_PP, 3 },
//   { CMD_READ_SIGNATURE_PP, 3 },
//   { CMD_READ_OSCCAL_PP, 3 },
// };

/*
 * From XML file:
  <REVISION>
    <RC_ID_MAJOR>0</RC_ID_MAJOR>
    <RC_ID_MINOR>56</RC_ID_MINOR>
    <EC_ID_MAJOR>0</EC_ID_MAJOR>
    <EC_ID_MINOR>1</EC_ID_MINOR>
  </REVISION>
 */
/*
 * These two tables can be semi-automatically updated from
 * targetboards.xml using tools/get-stk600-cards.xsl.
 */
static const struct carddata routing_cards[] =
{
  { 0x01, "STK600-RC020T-1" },
  { 0x03, "STK600-RC028T-3" },
  { 0x05, "STK600-RC040M-5" },
  { 0x08, "STK600-RC020T-8" },
  { 0x0A, "STK600-RC040M-4" },
  { 0x0C, "STK600-RC008T-2" },
  { 0x0D, "STK600-RC028M-6" },
  { 0x10, "STK600-RC064M-10" },
  { 0x11, "STK600-RC100M-11" },
  { 0x13, "STK600-RC100X-13" },
  { 0x15, "STK600-RC044X-15" },
  { 0x18, "STK600-RC100M-18" },
  { 0x19, "STK600-RCPWM-19" },
  { 0x1A, "STK600-RC064X-14" },
  { 0x1B, "STK600-RC032U-20" },
  { 0x1C, "STK600-RC014T-12" },
  { 0x1E, "STK600-RC064U-17" },
  { 0x1F, "STK600-RCuC3B0-21" },
  { 0x20, "STK600-RCPWM-22" },
  { 0x21, "STK600-RC020T-23" },
  { 0x22, "STK600-RC044M-24" },
  { 0x23, "STK600-RC044U-25" },
  { 0x24, "STK600-RCPWM-26" },
  { 0x25, "STK600-RCuC3B48-27" },
  { 0x27, "STK600-RC032M-29" },
  { 0x28, "STK600-RC044M-30" },
  { 0x29, "STK600-RC044M-31" },
  { 0x2A, "STK600-RC014T-42" },
  { 0x2B, "STK600-RC020T-43" },
  { 0x30, "STK600-RCUC3A144-32" },
  { 0x34, "STK600-RCUC3L0-34" },
  { 0x38, "STK600-RCUC3C0-36" },
  { 0x3B, "STK600-RCUC3C0-37" },
  { 0x3E, "STK600-RCUC3A144-33" },
  { 0x46, "STK600-RCuC3A100-28" },
  { 0x55, "STK600-RC064M-9" },
  { 0x88, "STK600-RCUC3C1-38" },
  { 0x8B, "STK600-RCUC3C1-39" },
  { 0xA0, "STK600-RC008T-7" },
  { 0xB8, "STK600-RCUC3C2-40" },
  { 0xBB, "STK600-RCUC3C2-41" },
};

static const struct carddata socket_cards[] =
{
  { 0x01, "STK600-TQFP48" },
  { 0x02, "STK600-TQFP32" },
  { 0x03, "STK600-TQFP100" },
  { 0x04, "STK600-SOIC" },
  { 0x06, "STK600-TQFP144" },
  { 0x09, "STK600-TinyX3U" },
  { 0x0C, "STK600-TSSOP44" },
  { 0x0D, "STK600-TQFP44" },
  { 0x0E, "STK600-TQFP64-2" },
  { 0x0F, "STK600-ATMEGA2560" },
  { 0x15, "STK600-MLF64" },
  { 0x16, "STK600-ATXMEGAT0" },
  { 0x18, "QT600-ATMEGA324-QM64" },
  { 0x19, "STK600-ATMEGA128RFA1" },
  { 0x1A, "QT600-ATTINY88-QT8" },
  { 0x1B, "QT600-ATXMEGA128A1-QT16" },
  { 0x1C, "QT600-AT32UC3L-QM64" },
  { 0x1D, "STK600-HVE2" },
  { 0x1E, "STK600-ATTINY10" },
  { 0x55, "STK600-TQFP64" },
  { 0x69, "STK600-uC3-144" },
  { 0xF0, "STK600-ATXMEGA1281A1" },
  { 0xF1, "STK600-DIP" },
};

static int stk500v2_getparm(PROGRAMMER * pgm, unsigned char parm, unsigned char * value);
static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned char value);
static int stk500v2_getparm2(PROGRAMMER * pgm, unsigned char parm, unsigned int * value);
static int stk500v2_setparm2(PROGRAMMER * pgm, unsigned char parm, unsigned int value);
static int stk500v2_setparm_real(PROGRAMMER * pgm, unsigned char parm, unsigned char value);
static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p);
static int stk500v2_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                               unsigned int page_size,
                               unsigned int addr, unsigned int n_bytes);
static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                unsigned int page_size,
                                unsigned int addr, unsigned int n_bytes);

static unsigned int stk500v2_mode_for_pagesize(unsigned int pagesize);

static double stk500v2_sck_to_us(PROGRAMMER * pgm, unsigned char dur);
static int stk500v2_set_sck_period_mk2(PROGRAMMER * pgm, double v);

static int stk600_set_sck_period(PROGRAMMER * pgm, double v);

static void stk600_setup_xprog(PROGRAMMER * pgm);
static void stk600_setup_isp(PROGRAMMER * pgm);
static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p);

void stk500v2_setup(PROGRAMMER * pgm)
{
  if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
    // avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
    //                 progname);
    // exit(1);
    avrdude_oom("stk500v2_setup(): Out of memory allocating private data\n");
  }
  memset(pgm->cookie, 0, sizeof(struct pdata));
  PDATA(pgm)->command_sequence = 1;
  PDATA(pgm)->boot_start = ULONG_MAX;
}

static void stk500v2_jtagmkII_setup(PROGRAMMER * pgm)
{
  // void *mycookie, *theircookie;

  // if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
  //                   progname);
  //   exit(1);
  // }
  // memset(pgm->cookie, 0, sizeof(struct pdata));
  // PDATA(pgm)->command_sequence = 1;

  // /*
  //  * Now, have the JTAG ICE mkII backend allocate its own private
  //  * data.  Store our own cookie in a safe place for the time being.
  //  */
  // mycookie = pgm->cookie;
  // jtagmkII_setup(pgm);
  // theircookie = pgm->cookie;
  // pgm->cookie = mycookie;
  // PDATA(pgm)->chained_pdata = theircookie;
}

static void stk500v2_jtag3_setup(PROGRAMMER * pgm)
{
  // void *mycookie, *theircookie;

  // if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
  //                   progname);
  //   exit(1);
  // }
  // memset(pgm->cookie, 0, sizeof(struct pdata));
  // PDATA(pgm)->command_sequence = 1;

  // /*
  //  * Now, have the JTAGICE3 backend allocate its own private
  //  * data.  Store our own cookie in a safe place for the time being.
  //  */
  // mycookie = pgm->cookie;
  // jtag3_setup(pgm);
  // theircookie = pgm->cookie;
  // pgm->cookie = mycookie;
  // PDATA(pgm)->chained_pdata = theircookie;
}

void stk500v2_teardown(PROGRAMMER * pgm)
{
  free(pgm->cookie);
}

static void stk500v2_jtagmkII_teardown(PROGRAMMER * pgm)
{
  // void *mycookie;

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // jtagmkII_teardown(pgm);

  // free(mycookie);
}

static void stk500v2_jtag3_teardown(PROGRAMMER * pgm)
{
  // void *mycookie;

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // jtag3_teardown(pgm);

  // free(mycookie);
}


// static unsigned short
// b2_to_u16(unsigned char *b)
// {
//   unsigned short l;
//   l = b[0];
//   l += (unsigned)b[1] << 8;

//   return l;
// }

static int stk500v2_send_mk2(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
  if (serial_send(&pgm->fd, data, len) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500_send_mk2(): failed to send command to serial port\n",progname);
    return -1;
  }

  return 0;
}

// static unsigned short get_jtagisp_return_size(unsigned char cmd)
// {
//   int i;

//   for (i = 0; i < sizeof jtagispcmds / sizeof jtagispcmds[0]; i++)
//     if (jtagispcmds[i].cmd == cmd)
//       return jtagispcmds[i].size;

//   return 0;
// }

/*
 * Send the data as a JTAG ICE mkII encapsulated ISP packet.
 * Unlike what AVR067 says, the packet gets a length of our
 * response buffer prepended, and replies with RSP_SPI_DATA
 * if successful.
 */
static int stk500v2_jtagmkII_send(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
  return -1;
  // unsigned char *cmdbuf;
  // int rv;
  // unsigned short sz;
  // void *mycookie;

  // sz = get_jtagisp_return_size(data[0]);
  // if (sz == 0) {
  //   avrdude_message(MSG_INFO, "%s: unsupported encapsulated ISP command: %#x\n",
	 //    progname, data[0]);
  //   return -1;
  // }
  // if (sz == SZ_READ_FLASH_EE) {
  //   /*
  //    * For CMND_READ_FLASH_ISP and CMND_READ_EEPROM_ISP, extract the
  //    * size of the return data from the request.  Note that the
  //    * request itself has the size in big endian format, while we are
  //    * supposed to deliver it in little endian.
  //    */
  //   sz = 3 + (data[1] << 8) + data[2];
  // } else if (sz == SZ_SPI_MULTI) {
  //   /*
  //    * CMND_SPI_MULTI has the Rx size encoded in its 3rd byte.
  //    */
  //   sz = 3 + data[2];
  // }

  // if ((cmdbuf = malloc(len + 3)) == NULL) {
  //   avrdude_message(MSG_INFO, "%s: out of memory for command packet\n",
  //           progname);
  //   exit(1);
  // }
  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // cmdbuf[0] = CMND_ISP_PACKET;
  // cmdbuf[1] = sz & 0xff;
  // cmdbuf[2] = (sz >> 8) & 0xff;
  // memcpy(cmdbuf + 3, data, len);
  // rv = jtagmkII_send(pgm, cmdbuf, len + 3);
  // free(cmdbuf);
  // pgm->cookie = mycookie;

  // return rv;
}

/*
 * Send the data as a JTAGICE3 encapsulated ISP packet.
 */
static int stk500v2_jtag3_send(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
  return -1;
  // unsigned char *cmdbuf;
  // int rv;
  // void *mycookie;

  // if ((cmdbuf = malloc(len + 1)) == NULL) {
  //   avrdude_message(MSG_INFO, "%s: out of memory for command packet\n",
  //           progname);
  //   exit(1);
  // }
  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // cmdbuf[0] = SCOPE_AVR_ISP;
  // memcpy(cmdbuf + 1, data, len);
  // rv = jtag3_send(pgm, cmdbuf, len + 1);
  // free(cmdbuf);
  // pgm->cookie = mycookie;

  // return rv;
}

static int stk500v2_send(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
  unsigned char buf[275 + 6];		// max MESSAGE_BODY of 275 bytes, 6 bytes overhead
  int i;

  if (PDATA(pgm)->pgmtype == PGMTYPE_AVRISP_MKII ||
      PDATA(pgm)->pgmtype == PGMTYPE_STK600)
    return stk500v2_send_mk2(pgm, data, len);
  else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII)
    return stk500v2_jtagmkII_send(pgm, data, len);
  else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE3)
    return stk500v2_jtag3_send(pgm, data, len);

  buf[0] = MESSAGE_START;
  buf[1] = PDATA(pgm)->command_sequence;
  buf[2] = (char)(len / 256);
  buf[3] = len % 256;
  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];

  DEBUG("STK500V2: stk500v2_send(");
  for (i=0;i<len+6;i++) DEBUG("0x%02x ",buf[i]);
  DEBUG(", %d)\n",len+6);

  if (serial_send(&pgm->fd, buf, len+6) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname);
    return -1;
  }

  return 0;
}


int stk500v2_drain(PROGRAMMER * pgm, int display)
{
  return serial_drain(&pgm->fd, display);
}

static int stk500v2_recv_mk2(PROGRAMMER * pgm, unsigned char *msg,
			     size_t maxsize)
{
  int rv;

  rv = serial_recv(&pgm->fd, msg, maxsize);
  if (rv < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_recv_mk2: error in USB receive\n", progname);
    return -1;
  }

  return rv;
}

static int stk500v2_jtagmkII_recv(PROGRAMMER * pgm, unsigned char *msg,
                                  size_t maxsize)
{
  return -1;
  // int rv;
  // unsigned char *jtagmsg;
  // void *mycookie;

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // rv = jtagmkII_recv(pgm, &jtagmsg);
  // pgm->cookie = mycookie;
  // if (rv <= 0) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtagmkII_recv(): error in jtagmkII_recv()\n",
  //           progname);
  //   return -1;
  // }
  // if (rv - 1 > maxsize) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtagmkII_recv(): got %u bytes, have only room for %u bytes\n",
  //                   progname, (unsigned)rv - 1, (unsigned)maxsize);
  //   rv = maxsize;
  // }
  // switch (jtagmsg[0]) {
  // case RSP_SPI_DATA:
  //   break;
  // case RSP_FAILED:
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtagmkII_recv(): failed\n",
	 //    progname);
  //   return -1;
  // case RSP_ILLEGAL_MCU_STATE:
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtagmkII_recv(): illegal MCU state\n",
	 //    progname);
  //   return -1;
  // default:
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtagmkII_recv(): unknown status %d\n",
	 //    progname, jtagmsg[0]);
  //   return -1;
  // }
  // memcpy(msg, jtagmsg + 1, rv - 1);
  // return rv;
}

static int stk500v2_jtag3_recv(PROGRAMMER * pgm, unsigned char *msg,
			       size_t maxsize)
{
  return -1;
  // int rv;
  // unsigned char *jtagmsg;
  // void *mycookie;

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // rv = jtag3_recv(pgm, &jtagmsg);
  // pgm->cookie = mycookie;
  // if (rv <= 0) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtag3_recv(): error in jtagmkII_recv()\n",
  //           progname);
  //   return -1;
  // }
  // /* Getting more data than expected is a normal case for the EDBG
  //    implementation of JTAGICE3, as they always request a full 512
  //    octets from the ICE.  Thus, only complain at high verbose
  //    levels. */
  // if (rv - 1 > maxsize) {
  //   avrdude_message(MSG_DEBUG, "%s: stk500v2_jtag3_recv(): got %u bytes, have only room for %u bytes\n",
  //                     progname, (unsigned)rv - 1, (unsigned)maxsize);
  //   rv = maxsize;
  // }
  // if (jtagmsg[0] != SCOPE_AVR_ISP) {
  //   avrdude_message(MSG_INFO, "%s: stk500v2_jtag3_recv(): message is not AVR ISP: 0x%02x\n",
  //                   progname, jtagmsg[0]);
  //   free(jtagmsg);
  //   return -1;
  // }
  // memcpy(msg, jtagmsg + 1, rv - 1);
  // free(jtagmsg);
  // return rv;
}

static int stk500v2_recv(PROGRAMMER * pgm, unsigned char *msg, size_t maxsize) {
  enum states { sINIT, sSTART, sSEQNUM, sSIZE1, sSIZE2, sTOKEN, sDATA, sCSUM, sDONE }  state = sSTART;
  unsigned int msglen = 0;
  unsigned int curlen = 0;
  int timeout = 0;
  unsigned char c, checksum = 0;

  /*
   * The entire timeout handling here is not very consistent, see
   *
   * https://savannah.nongnu.org/bugs/index.php?43626
   */
  long timeoutval = SERIAL_TIMEOUT;		// seconds
  struct timeval tv;
  double tstart, tnow;

  if (PDATA(pgm)->pgmtype == PGMTYPE_AVRISP_MKII ||
      PDATA(pgm)->pgmtype == PGMTYPE_STK600)
    return stk500v2_recv_mk2(pgm, msg, maxsize);
  else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII)
    return stk500v2_jtagmkII_recv(pgm, msg, maxsize);
  else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE3)
    return stk500v2_jtag3_recv(pgm, msg, maxsize);

  DEBUG("STK500V2: stk500v2_recv(): ");

  gettimeofday(&tv, NULL);
  tstart = tv.tv_sec;

  while ( (state != sDONE ) && (!timeout) ) {
    RETURN_IF_CANCEL();
    if (serial_recv(&pgm->fd, &c, 1) < 0)
      goto timedout;
    DEBUG("0x%02x ",c);
    checksum ^= c;

    switch (state) {
      case sSTART:
        DEBUGRECV("hoping for start token...");
        if (c == MESSAGE_START) {
          DEBUGRECV("got it\n");
          checksum = MESSAGE_START;
          state = sSEQNUM;
        } else
          DEBUGRECV("sorry\n");
        break;
      case sSEQNUM:
        DEBUGRECV("hoping for sequence...\n");
        if (c == PDATA(pgm)->command_sequence) {
          DEBUGRECV("got it, incrementing\n");
          state = sSIZE1;
          PDATA(pgm)->command_sequence++;
        } else {
          DEBUGRECV("sorry\n");
          state = sSTART;
        }
        break;
      case sSIZE1:
        DEBUGRECV("hoping for size LSB\n");
        msglen = (unsigned)c * 256;
        state = sSIZE2;
        break;
      case sSIZE2:
        DEBUGRECV("hoping for size MSB...");
        msglen += (unsigned)c;
        DEBUG(" msg is %u bytes\n",msglen);
        state = sTOKEN;
        break;
      case sTOKEN:
        if (c == TOKEN) state = sDATA;
        else state = sSTART;
        break;
      case sDATA:
        if (curlen < maxsize) {
          msg[curlen] = c;
        } else {
          avrdude_message(MSG_INFO, "%s: stk500v2_recv(): buffer too small, received %d byte into %u byte buffer\n",
                  progname,curlen,(unsigned int)maxsize);
          return -2;
        }
        if ((curlen == 0) && (msg[0] == ANSWER_CKSUM_ERROR)) {
          avrdude_message(MSG_INFO, "%s: stk500v2_recv(): previous packet sent with wrong checksum\n",
                  progname);
          return -3;
        }
        curlen++;
        if (curlen == msglen) state = sCSUM;
        break;
      case sCSUM:
        if (checksum == 0) {
          state = sDONE;
        } else {
          state = sSTART;
          avrdude_message(MSG_INFO, "%s: stk500v2_recv(): checksum error\n",
                  progname);
          return -4;
        }
        break;
      default:
        avrdude_message(MSG_INFO, "%s: stk500v2_recv(): unknown state\n",
                progname);
        return -5;
     } /* switch */

     gettimeofday(&tv, NULL);
     tnow = tv.tv_sec;
     if (tnow-tstart > timeoutval) {			// wuff - signed/unsigned/overflow
      timedout:
       avrdude_message(MSG_INFO, "%s: stk500v2_recv(): timeout\n",
               progname);
       return -1;
     }

  } /* while */
  DEBUG("\n");

  return (int)(msglen+6);
}



int stk500v2_getsync(PROGRAMMER * pgm) {
  int tries = 0;
  unsigned char buf[1], resp[32];
  int status;

  DEBUG("STK500V2: stk500v2_getsync()\n");

  if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII ||
      PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE3)
    return 0;

retry:
  tries++;

  RETURN_IF_CANCEL();

  // send the sync command and see if we can get there
  buf[0] = CMD_SIGN_ON;
  if (stk500v2_send(pgm, buf, 1) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device\n", progname);
    return -1;
  }

  RETURN_IF_CANCEL();

  // try to get the response back and see where we got
  status = stk500v2_recv(pgm, resp, sizeof(resp));

  RETURN_IF_CANCEL();

  // if we got bytes returned, check to see what came back
  if (status > 0) {
    if ((resp[0] == CMD_SIGN_ON) && (resp[1] == STATUS_CMD_OK) &&
	(status > 3)) {
      // success!
      unsigned int siglen = resp[2];
      if (siglen >= strlen("STK500_2") &&
	  memcmp(resp + 3, "STK500_2", strlen("STK500_2")) == 0) {
	PDATA(pgm)->pgmtype = PGMTYPE_STK500;
      } else if (siglen >= strlen("AVRISP_2") &&
		 memcmp(resp + 3, "AVRISP_2", strlen("AVRISP_2")) == 0) {
	PDATA(pgm)->pgmtype = PGMTYPE_AVRISP;
      } else if (siglen >= strlen("AVRISP_MK2") &&
		 memcmp(resp + 3, "AVRISP_MK2", strlen("AVRISP_MK2")) == 0) {
	PDATA(pgm)->pgmtype = PGMTYPE_AVRISP_MKII;
      } else if (siglen >= strlen("STK600") &&
	  memcmp(resp + 3, "STK600", strlen("STK600")) == 0) {
	PDATA(pgm)->pgmtype = PGMTYPE_STK600;
      } else {
	resp[siglen + 3] = 0;
        avrdude_message(MSG_NOTICE, "%s: stk500v2_getsync(): got response from unknown "
                          "programmer %s, assuming STK500\n",
                          progname, resp + 3);
	PDATA(pgm)->pgmtype = PGMTYPE_STK500;
      }
      avrdude_message(MSG_DEBUG, "%s: stk500v2_getsync(): found %s programmer\n",
                        progname, pgmname[PDATA(pgm)->pgmtype]);
      return 0;
    } else {
      if (tries > RETRIES) {
        avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device: resp=0x%02x\n",
                        progname, resp[0]);
        return -6;
      } else
        goto retry;
    }

  // or if we got a timeout
  } else if (status == -1) {
    if (tries > RETRIES) {
      avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): timeout communicating with programmer\n",
              progname);
      return -1;
    } else
      goto retry;

  // or any other error
  } else {
    if (tries > RETRIES) {
      avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): error communicating with programmer: (%d)\n",
              progname,status);
    } else
      goto retry;
  }

  return 0;
}

static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf,
                            size_t len, size_t maxlen) {
  int i;
  int tries = 0;
  int status;

  DEBUG("STK500V2: stk500v2_command(");
  for (i=0;i<len;i++) DEBUG("0x%02x ",buf[i]);
  DEBUG(", %d)\n",len);

retry:
  tries++;

  RETURN_IF_CANCEL();

  // send the command to the programmer
  if (stk500v2_send(pgm, buf, len) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_command(): can't communicate with device\n", progname);
    return -1;
  }

  RETURN_IF_CANCEL();

  // attempt to read the status back
  status = stk500v2_recv(pgm,buf,maxlen);

  RETURN_IF_CANCEL();

  // if we got a successful readback, return
  if (status > 0) {
    DEBUG(" = %d\n",status);
    if (status < 2) {
      avrdude_message(MSG_INFO, "%s: stk500v2_command(): short reply\n", progname);
      return -1;
    }
    if (buf[0] == CMD_XPROG_SETMODE || buf[0] == CMD_XPROG) {
        /*
         * Decode XPROG wrapper errors.
         */
        const char *msg;
        int i;

        /*
         * For CMD_XPROG_SETMODE, the status is returned in buf[1].
         * For CMD_XPROG, buf[1] contains the XPRG_CMD_* command, and
         * buf[2] contains the status.
         */
        i = buf[0] == CMD_XPROG_SETMODE? 1: 2;

        if (buf[i] != XPRG_ERR_OK) {
            switch (buf[i]) {
            case XPRG_ERR_FAILED:   msg = "Failed"; break;
            case XPRG_ERR_COLLISION: msg = "Collision"; break;
            case XPRG_ERR_TIMEOUT:  msg = "Timeout"; break;
            default:                msg = "Unknown"; break;
            }
            avrdude_message(MSG_INFO, "%s: stk500v2_command(): error in %s: %s\n",
                    progname,
                    (buf[0] == CMD_XPROG_SETMODE? "CMD_XPROG_SETMODE": "CMD_XPROG"),
                    msg);
            return -1;
        }
        return 0;
    } else {
        /*
         * Decode STK500v2 errors.
         */
        if (buf[1] >= STATUS_CMD_TOUT && buf[1] < 0xa0) {
            const char *msg;
            char msgbuf[30];
            switch (buf[1]) {
            case STATUS_CMD_TOUT:
                msg = "Command timed out";
                break;

            case STATUS_RDY_BSY_TOUT:
                msg = "Sampling of the RDY/nBSY pin timed out";
                break;

            case STATUS_SET_PARAM_MISSING:
                msg = "The `Set Device Parameters' have not been "
                    "executed in advance of this command";

            default:
                sprintf(msgbuf, "unknown, code 0x%02x", buf[1]);
                msg = msgbuf;
                break;
            }
            if (quell_progress < 2) {
                avrdude_message(MSG_INFO, "%s: stk500v2_command(): warning: %s\n",
                        progname, msg);
            }
        } else if (buf[1] == STATUS_CMD_OK) {
            return status;
        } else if (buf[1] == STATUS_CMD_FAILED) {
            avrdude_message(MSG_INFO, "%s: stk500v2_command(): command failed\n",
                            progname);
        } else if (buf[1] == STATUS_CMD_UNKNOWN) {
            avrdude_message(MSG_INFO, "%s: stk500v2_command(): unknown command\n",
                            progname);
        } else {
            avrdude_message(MSG_INFO, "%s: stk500v2_command(): unknown status 0x%02x\n",
                    progname, buf[1]);
        }
        return -1;
    }
  }

  // otherwise try to sync up again
  status = stk500v2_getsync(pgm);
  if (status != 0) {
    if (tries > RETRIES) {
      avrdude_message(MSG_INFO, "%s: stk500v2_command(): failed miserably to execute command 0x%02x\n",
              progname,buf[0]);
      return -1;
    } else
      goto retry;
  }

  DEBUG(" = 0\n");
  return 0;
}

static int stk500v2_cmd(PROGRAMMER * pgm, const unsigned char *cmd,
                        unsigned char *res)
{
  unsigned char buf[8];
  int result;

  DEBUG("STK500V2: stk500v2_cmd(%02x,%02x,%02x,%02x)\n",cmd[0],cmd[1],cmd[2],cmd[3]);

  buf[0] = CMD_SPI_MULTI;
  buf[1] = 4;
  buf[2] = 4;
  buf[3] = 0;
  buf[4] = cmd[0];
  buf[5] = cmd[1];
  buf[6] = cmd[2];
  buf[7] = cmd[3];

  result = stk500v2_command(pgm, buf, 8, sizeof(buf));
  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_cmd(): failed to send command\n",
            progname);
    return -1;
  } else if (result < 6) {
    avrdude_message(MSG_INFO, "%s: stk500v2_cmd(): short reply, len = %d\n",
            progname, result);
    return -1;
  }

  res[0] = buf[2];
  res[1] = buf[3];
  res[2] = buf[4];
  res[3] = buf[5];

  return 0;
}


static int stk500v2_jtag3_cmd(PROGRAMMER * pgm, const unsigned char *cmd,
			      unsigned char *res)
{
  avrdude_message(MSG_INFO, "%s: stk500v2_jtag3_cmd(): Not available in JTAGICE3\n",
                  progname);

  return -1;
}


/*
 * issue the 'chip erase' command to the AVR device
 */
static int stk500v2_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
  int result;
  unsigned char buf[16];

  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_chip_erase: chip erase instruction not defined for part \"%s\"\n",
            progname, p->desc);
    return -1;
  }

  pgm->pgm_led(pgm, ON);

  buf[0] = CMD_CHIP_ERASE_ISP;
  buf[1] = p->chip_erase_delay / 1000;
  buf[2] = 0;	// use delay (?)
  avr_set_bits(p->op[AVR_OP_CHIP_ERASE], buf+3);
  result = stk500v2_command(pgm, buf, 7, sizeof(buf));
  usleep(p->chip_erase_delay);
  pgm->initialize(pgm, p);

  pgm->pgm_led(pgm, OFF);

  return result >= 0? 0: -1;
}

/*
 * issue the 'chip erase' command to the AVR device, generic HV mode
 */
static int stk500hv_chip_erase(PROGRAMMER * pgm, AVRPART * p, enum hvmode mode)
{
  int result;
  unsigned char buf[3];

  pgm->pgm_led(pgm, ON);

  if (mode == PPMODE) {
    buf[0] = CMD_CHIP_ERASE_PP;
    buf[1] = p->chiperasepulsewidth;
    buf[2] = p->chiperasepolltimeout;
  } else {
    buf[0] = CMD_CHIP_ERASE_HVSP;
    buf[1] = p->chiperasepolltimeout;
    buf[2] = p->chiperasetime;
  }
  result = stk500v2_command(pgm, buf, 3, sizeof(buf));
  usleep(p->chip_erase_delay);
  pgm->initialize(pgm, p);

  pgm->pgm_led(pgm, OFF);

  return result >= 0? 0: -1;
}

/*
 * issue the 'chip erase' command to the AVR device, parallel mode
 */
static int stk500pp_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
  return stk500hv_chip_erase(pgm, p, PPMODE);
}

/*
 * issue the 'chip erase' command to the AVR device, HVSP mode
 */
static int stk500hvsp_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
  return stk500hv_chip_erase(pgm, p, HVSPMODE);
}

static struct
{
  unsigned int state;
  const char *description;
} connection_status[] =
{
  { STATUS_CONN_FAIL_MOSI, "MOSI fail" },
  { STATUS_CONN_FAIL_RST, "RST fail" },
  { STATUS_CONN_FAIL_SCK, "SCK fail" },
  { STATUS_TGT_NOT_DETECTED, "Target not detected" },
  { STATUS_TGT_REVERSE_INSERTED, "Target reverse inserted" },
};

/*
 * Max length of returned message is the sum of all the description
 * strings in the table above, plus 2 characters for separation.
 * Currently, this is 76 chars.
 */
static void
stk500v2_translate_conn_status(unsigned char status, char *msg)
{
    size_t i;
    int need_comma;

    *msg = 0;
    need_comma = 0;

    for (i = 0;
         i < sizeof connection_status / sizeof connection_status[0];
         i++)
    {
        if ((status & connection_status[i].state) != 0)
        {
            if (need_comma)
                strcat(msg, ", ");
            strcat(msg, connection_status[i].description);
            need_comma = 1;
        }
    }
    if (*msg == 0)
        sprintf(msg, "Unknown status 0x%02x", status);
}


/*
 * issue the 'program enable' command to the AVR device
 */
static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
  unsigned char buf[16];
  char msg[100];             /* see remarks above about size needed */
  int rv;
  // int tries;

  PDATA(pgm)->lastpart = p;

  if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_program_enable(): program enable instruction not defined for part \"%s\"\n",
	    progname, p->desc);
    return -1;
  }

  if (PDATA(pgm)->pgmtype == PGMTYPE_STK500 ||
      PDATA(pgm)->pgmtype == PGMTYPE_STK600)
      /* Activate AVR-style (low active) RESET */
      stk500v2_setparm_real(pgm, PARAM_RESET_POLARITY, 0x01);

  // tries = 0;
// retry:
  buf[0] = CMD_ENTER_PROGMODE_ISP;
  buf[1] = p->timeout;
  buf[2] = p->stabdelay;
  buf[3] = p->cmdexedelay;
  buf[4] = p->synchloops;
  buf[5] = p->bytedelay;
  buf[6] = p->pollvalue;
  buf[7] = p->pollindex;
  avr_set_bits(p->op[AVR_OP_PGM_ENABLE], buf+8);
  buf[10] = buf[11] = 0;

  rv = stk500v2_command(pgm, buf, 12, sizeof(buf));

  if (rv < 0) {
    switch (PDATA(pgm)->pgmtype)
    {
    case PGMTYPE_STK600:
    case PGMTYPE_AVRISP_MKII:
        if (stk500v2_getparm(pgm, PARAM_STATUS_TGT_CONN, &buf[0]) != 0) {
            avrdude_message(MSG_INFO, "%s: stk500v2_program_enable(): cannot get connection status\n",
                            progname);
        } else {
            stk500v2_translate_conn_status(buf[0], msg);
            avrdude_message(MSG_INFO, "%s: stk500v2_program_enable():"
                    " bad AVRISPmkII connection status: %s\n",
                    progname, msg);
        }
        break;

    case PGMTYPE_JTAGICE3:
        return -1;
        // if (buf[1] == STATUS_CMD_FAILED &&
        //     (p->flags & AVRPART_HAS_DW) != 0) {
        //     void *mycookie;
        //     unsigned char cmd[4], *resp;

        //     /* Try debugWIRE, and MONCON_DISABLE */
        //     avrdude_message(MSG_NOTICE2, "%s: No response in ISP mode, trying debugWIRE\n",
        //                         progname);

        //     mycookie = pgm->cookie;
        //     pgm->cookie = PDATA(pgm)->chained_pdata;

        //     cmd[0] = PARM3_CONN_DW;
        //     if (jtag3_setparm(pgm, SCOPE_AVR, 1, PARM3_CONNECTION, cmd, 1) < 0) {
        //         pgm->cookie = mycookie;
        //         break;
        //     }

        //     cmd[0] = SCOPE_AVR;

        //     cmd[1] = CMD3_SIGN_ON;
        //     cmd[2] = cmd[3] = 0;
        //     if (jtag3_command(pgm, cmd, 4, &resp, "AVR sign-on") >= 0) {
        //         free(resp);

        //         cmd[1] = CMD3_START_DW_DEBUG;
        //         if (jtag3_command(pgm, cmd, 4, &resp, "start DW debug") >= 0) {
        //             free(resp);

        //             cmd[1] = CMD3_MONCON_DISABLE;
        //             if (jtag3_command(pgm, cmd, 3, &resp, "MonCon disable") >= 0)
        //                 free(resp);
        //         }
        //     }
        //     pgm->cookie = mycookie;
        //     if (tries++ > 3) {
        //         avrdude_message(MSG_INFO, "%s: Failed to return from debugWIRE to ISP.\n",
        //                         progname);
        //         break;
        //     }
        //     avrdude_message(MSG_INFO, "%s: Target prepared for ISP, signed off.\n"
        //                     "%s: Now retrying without power-cycling the target.\n",
        //                     progname, progname);
        //     goto retry;
        // }
        break;

    default:
        /* cannot report anything for other pgmtypes */
        break;
    }
  }

  return rv;
}

/*
 * issue the 'program enable' command to the AVR device, parallel mode
 */
static int stk500pp_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
  unsigned char buf[16];

  PDATA(pgm)->lastpart = p;

  buf[0] = CMD_ENTER_PROGMODE_PP;
  buf[1] = p->hventerstabdelay;
  buf[2] = p->progmodedelay;
  buf[3] = p->latchcycles;
  buf[4] = p->togglevtg;
  buf[5] = p->poweroffdelay;
  buf[6] = p->resetdelayms;
  buf[7] = p->resetdelayus;

  return stk500v2_command(pgm, buf, 8, sizeof(buf));
}

/*
 * issue the 'program enable' command to the AVR device, HVSP mode
 */
static int stk500hvsp_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
  unsigned char buf[16];

  PDATA(pgm)->lastpart = p;

  buf[0] = PDATA(pgm)->pgmtype == PGMTYPE_STK600?
  CMD_ENTER_PROGMODE_HVSP_STK600:
  CMD_ENTER_PROGMODE_HVSP;
  buf[1] = p->hventerstabdelay;
  buf[2] = p->hvspcmdexedelay;
  buf[3] = p->synchcycles;
  buf[4] = p->latchcycles;
  buf[5] = p->togglevtg;
  buf[6] = p->poweroffdelay;
  buf[7] = p->resetdelayms;
  buf[8] = p->resetdelayus;

  return stk500v2_command(pgm, buf, 9, sizeof(buf));
}


/*
 * initialize the AVR device and prepare it to accept commands
 */
static int stk500v2_initialize(PROGRAMMER * pgm, AVRPART * p)
{

  LNODEID ln;
  AVRMEM * m;

  if ((PDATA(pgm)->pgmtype == PGMTYPE_STK600 ||
       PDATA(pgm)->pgmtype == PGMTYPE_AVRISP_MKII ||
       PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII) != 0
      && (p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_TPI)) != 0) {
    /*
     * This is an ATxmega device, must use XPROG protocol for the
     * remaining actions.
     */
    if ((p->flags & AVRPART_HAS_PDI) != 0) {
      /*
       * Find out where the border between application and boot area
       * is.
       */
      AVRMEM *bootmem = avr_locate_mem(p, "boot");
      AVRMEM *flashmem = avr_locate_mem(p, "flash");
      if (bootmem == NULL || flashmem == NULL) {
        avrdude_message(MSG_INFO, "%s: stk500v2_initialize(): Cannot locate \"flash\" and \"boot\" memories in description\n",
                        progname);
      } else {
        PDATA(pgm)->boot_start = bootmem->offset - flashmem->offset;
      }
    }
    stk600_setup_xprog(pgm);
  } else {
    stk600_setup_isp(pgm);
  }

  /*
   * Examine the avrpart's memory definitions, and initialize the page
   * caches.  For devices/memory that are not page oriented, treat
   * them as page size 1 for EEPROM, and 2 for flash.
   */
  PDATA(pgm)->flash_pagesize = 2;
  PDATA(pgm)->eeprom_pagesize = 1;
  for (ln = lfirst(p->mem); ln; ln = lnext(ln)) {
    m = ldata(ln);
    if (strcmp(m->desc, "flash") == 0) {
      if (m->page_size > 0) {
        if (m->page_size > 256)
          PDATA(pgm)->flash_pagesize = 256;
        else
          PDATA(pgm)->flash_pagesize = m->page_size;
      }
    } else if (strcmp(m->desc, "eeprom") == 0) {
      if (m->page_size > 0)
	PDATA(pgm)->eeprom_pagesize = m->page_size;
    }
  }
  free(PDATA(pgm)->flash_pagecache);
  free(PDATA(pgm)->eeprom_pagecache);
  if ((PDATA(pgm)->flash_pagecache = malloc(PDATA(pgm)->flash_pagesize)) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_initialize(): Out of memory\n",
	    progname);
    return -1;
  }
  if ((PDATA(pgm)->eeprom_pagecache = malloc(PDATA(pgm)->eeprom_pagesize)) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_initialize(): Out of memory\n",
	    progname);
    free(PDATA(pgm)->flash_pagecache);
    return -1;
  }
  PDATA(pgm)->flash_pageaddr = PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;

  if (p->flags & AVRPART_IS_AT90S1200) {
    /*
     * AT90S1200 needs a positive reset pulse after a chip erase.
     */
    pgm->disable(pgm);
    usleep(10000);
  }
  return pgm->program_enable(pgm, p);
}



/*
 * initialize the AVR device and prepare it to accept commands
 */
static int stk500v2_jtag3_initialize(PROGRAMMER * pgm, AVRPART * p)
{
  return -1;
  // unsigned char parm[4], *resp;
  // LNODEID ln;
  // AVRMEM * m;
  // void *mycookie;

  // if ((p->flags & AVRPART_HAS_PDI) ||
  //     (p->flags & AVRPART_HAS_TPI)) {
  //   avrdude_message(MSG_INFO, "%s: jtag3_initialize(): part %s has no ISP interface\n",
	 //    progname, p->desc);
  //   return -1;
  // }

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;

  // if (p->flags & AVRPART_HAS_DW)
  //   parm[0] = PARM3_ARCH_TINY;
  // else
  //   parm[0] = PARM3_ARCH_MEGA;
  // if (jtag3_setparm(pgm, SCOPE_AVR, 0, PARM3_ARCH, parm, 1) < 0) {
  //   pgm->cookie = mycookie;
  //   return -1;
  // }

  // parm[0] = PARM3_SESS_PROGRAMMING;
  // if (jtag3_setparm(pgm, SCOPE_AVR, 0, PARM3_SESS_PURPOSE, parm, 1) < 0) {
  //   pgm->cookie = mycookie;
  //   return -1;
  // }

  // parm[0] = PARM3_CONN_ISP;
  // if (jtag3_setparm(pgm, SCOPE_AVR, 1, PARM3_CONNECTION, parm, 1) < 0) {
  //   pgm->cookie = mycookie;
  //   return -1;
  // }

  // parm[0] = SCOPE_AVR_ISP;
  // parm[1] = 0x1e;
  // jtag3_send(pgm, parm, 2);

  // if (jtag3_recv(pgm, &resp) > 0)
  //   free(resp);

  // pgm->cookie = mycookie;

  // /*
  //  * Examine the avrpart's memory definitions, and initialize the page
  //  * caches.  For devices/memory that are not page oriented, treat
  //  * them as page size 1 for EEPROM, and 2 for flash.
  //  */
  // PDATA(pgm)->flash_pagesize = 2;
  // PDATA(pgm)->eeprom_pagesize = 1;
 //  for (ln = lfirst(p->mem); ln; ln = lnext(ln)) {
 //    m = ldata(ln);
 //    if (strcmp(m->desc, "flash") == 0) {
 //      if (m->page_size > 0) {
 //        if (m->page_size > 256)
 //          PDATA(pgm)->flash_pagesize = 256;
 //        else
 //          PDATA(pgm)->flash_pagesize = m->page_size;
 //      }
 //    } else if (strcmp(m->desc, "eeprom") == 0) {
 //      if (m->page_size > 0)
	// PDATA(pgm)->eeprom_pagesize = m->page_size;
 //    }
 //  }
 //  free(PDATA(pgm)->flash_pagecache);
 //  free(PDATA(pgm)->eeprom_pagecache);
 //  if ((PDATA(pgm)->flash_pagecache = malloc(PDATA(pgm)->flash_pagesize)) == NULL) {
 //    avrdude_message(MSG_INFO, "%s: stk500hv_initialize(): Out of memory\n",
	//     progname);
 //    return -1;
 //  }
 //  if ((PDATA(pgm)->eeprom_pagecache = malloc(PDATA(pgm)->eeprom_pagesize)) == NULL) {
 //    avrdude_message(MSG_INFO, "%s: stk500hv_initialize(): Out of memory\n",
	//     progname);
 //    free(PDATA(pgm)->flash_pagecache);
 //    return -1;
 //  }
 //  PDATA(pgm)->flash_pageaddr = PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;

 //  return pgm->program_enable(pgm, p);
}


/*
 * initialize the AVR device and prepare it to accept commands, generic HV mode
 */
static int stk500hv_initialize(PROGRAMMER * pgm, AVRPART * p, enum hvmode mode)
{
  unsigned char buf[CTL_STACK_SIZE + 1];
  int result;
  LNODEID ln;
  AVRMEM * m;

  if (p->ctl_stack_type != (mode == PPMODE? CTL_STACK_PP: CTL_STACK_HVSP)) {
    avrdude_message(MSG_INFO, "%s: stk500hv_initialize(): "
                    "%s programming control stack not defined for part \"%s\"\n",
                    progname,
                    (mode == PPMODE? "parallel": "high-voltage serial"),
                    p->desc);
    return -1;
  }

  buf[0] = CMD_SET_CONTROL_STACK;
  memcpy(buf + 1, p->controlstack, CTL_STACK_SIZE);

  result = stk500v2_command(pgm, buf, CTL_STACK_SIZE + 1, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500hv_initalize(): "
                    "failed to set control stack\n",
                    progname);
    return -1;
  }

  /*
   * Examine the avrpart's memory definitions, and initialize the page
   * caches.  For devices/memory that are not page oriented, treat
   * them as page size 1 for EEPROM, and 2 for flash.
   */
  PDATA(pgm)->flash_pagesize = 2;
  PDATA(pgm)->eeprom_pagesize = 1;
  for (ln = lfirst(p->mem); ln; ln = lnext(ln)) {
    m = ldata(ln);
    if (strcmp(m->desc, "flash") == 0) {
      if (m->page_size > 0) {
        if (m->page_size > 256)
          PDATA(pgm)->flash_pagesize = 256;
        else
          PDATA(pgm)->flash_pagesize = m->page_size;
      }
    } else if (strcmp(m->desc, "eeprom") == 0) {
      if (m->page_size > 0)
	PDATA(pgm)->eeprom_pagesize = m->page_size;
    }
  }
  free(PDATA(pgm)->flash_pagecache);
  free(PDATA(pgm)->eeprom_pagecache);
  if ((PDATA(pgm)->flash_pagecache = malloc(PDATA(pgm)->flash_pagesize)) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500hv_initialize(): Out of memory\n",
	    progname);
    return -1;
  }
  if ((PDATA(pgm)->eeprom_pagecache = malloc(PDATA(pgm)->eeprom_pagesize)) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500hv_initialize(): Out of memory\n",
	    progname);
    free(PDATA(pgm)->flash_pagecache);
    return -1;
  }
  PDATA(pgm)->flash_pageaddr = PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;

  return pgm->program_enable(pgm, p);
}

/*
 * initialize the AVR device and prepare it to accept commands, PP mode
 */
static int stk500pp_initialize(PROGRAMMER * pgm, AVRPART * p)
{
  return stk500hv_initialize(pgm, p, PPMODE);
}

/*
 * initialize the AVR device and prepare it to accept commands, HVSP mode
 */
static int stk500hvsp_initialize(PROGRAMMER * pgm, AVRPART * p)
{
  return stk500hv_initialize(pgm, p, HVSPMODE);
}

static void stk500v2_jtag3_disable(PROGRAMMER * pgm)
{
  unsigned char buf[16];
  int result;

  free(PDATA(pgm)->flash_pagecache);
  PDATA(pgm)->flash_pagecache = NULL;
  free(PDATA(pgm)->eeprom_pagecache);
  PDATA(pgm)->eeprom_pagecache = NULL;

  buf[0] = CMD_LEAVE_PROGMODE_ISP;
  buf[1] = 1; // preDelay;
  buf[2] = 1; // postDelay;

  result = stk500v2_command(pgm, buf, 3, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_disable(): failed to leave programming mode\n",
                    progname);
  }

  return;
}

static void stk500v2_disable(PROGRAMMER * pgm)
{
  unsigned char buf[16];
  int result;

  buf[0] = CMD_LEAVE_PROGMODE_ISP;
  buf[1] = 1; // preDelay;
  buf[2] = 1; // postDelay;

  result = stk500v2_command(pgm, buf, 3, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_disable(): failed to leave programming mode\n",
                    progname);
  }

  return;
}

/*
 * Leave programming mode, generic HV mode
 */
static void stk500hv_disable(PROGRAMMER * pgm, enum hvmode mode)
{
  unsigned char buf[16];
  int result;

  free(PDATA(pgm)->flash_pagecache);
  PDATA(pgm)->flash_pagecache = NULL;
  free(PDATA(pgm)->eeprom_pagecache);
  PDATA(pgm)->eeprom_pagecache = NULL;

  buf[0] = mode == PPMODE? CMD_LEAVE_PROGMODE_PP:
  (PDATA(pgm)->pgmtype == PGMTYPE_STK600?
   CMD_LEAVE_PROGMODE_HVSP_STK600:
   CMD_LEAVE_PROGMODE_HVSP);
  buf[1] = 15;  // p->hvleavestabdelay;
  buf[2] = 15;  // p->resetdelay;

  result = stk500v2_command(pgm, buf, 3, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500hv_disable(): "
                    "failed to leave programming mode\n",
                    progname);
  }

  return;
}

/*
 * Leave programming mode, PP mode
 */
static void stk500pp_disable(PROGRAMMER * pgm)
{
  stk500hv_disable(pgm, PPMODE);
}

/*
 * Leave programming mode, HVSP mode
 */
static void stk500hvsp_disable(PROGRAMMER * pgm)
{
  stk500hv_disable(pgm, HVSPMODE);
}

static void stk500v2_enable(PROGRAMMER * pgm)
{
  return;
}


static int stk500v2_open(PROGRAMMER * pgm, char * port)
{
  union pinfo pinfo = { .baud = 115200 };

  DEBUG("STK500V2: stk500v2_open()\n");

  if (pgm->baudrate)
    pinfo.baud = pgm->baudrate;

  PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;

  if(strcasecmp(port, "avrdoper") == 0){
#if defined(HAVE_LIBUSB) || (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
    serdev = &avrdoper_serdev;
    PDATA(pgm)->pgmtype = PGMTYPE_STK500;
#else
    avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
    return -1;
#endif
  }

  /*
   * If the port name starts with "usb", divert the serial routines
   * to the USB ones.  The serial_open() function for USB overrides
   * the meaning of the "baud" parameter to be the USB device ID to
   * search for.
   */
  if (strncmp(port, "usb", 3) == 0) {
#if defined(HAVE_LIBUSB)
    serdev = &usb_serdev_frame;
    pinfo.usbinfo.vid = USB_VENDOR_ATMEL;
    pinfo.usbinfo.flags = 0;
    pinfo.usbinfo.pid = USB_DEVICE_AVRISPMKII;
    PDATA(pgm)->pgmtype = PGMTYPE_AVRISP_MKII;
    pgm->set_sck_period = stk500v2_set_sck_period_mk2;
    pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_MKII;
    pgm->fd.usb.rep = USBDEV_BULK_EP_READ_MKII;
    pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_MKII;
    pgm->fd.usb.eep = 0;           /* no seperate EP for events */
#else
    avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
    return -1;
#endif
  }

  strcpy(pgm->port, port);
  if (serial_open(port, pinfo, &pgm->fd)==-1) {
    return -1;
  }

  /*
   * drain any extraneous input
   */
  stk500v2_drain(pgm, 0);

  stk500v2_getsync(pgm);

  stk500v2_drain(pgm, 0);

  if (pgm->bitclock != 0.0) {
    if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
      serial_close(&pgm->fd);
      return -1;
    }
  }

  return 0;
}


static int stk600_open(PROGRAMMER * pgm, char * port)
{
  union pinfo pinfo = { .baud = 115200 };

  DEBUG("STK500V2: stk600_open()\n");

  if (pgm->baudrate)
    pinfo.baud = pgm->baudrate;

  PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN;

  /*
   * If the port name starts with "usb", divert the serial routines
   * to the USB ones.  The serial_open() function for USB overrides
   * the meaning of the "baud" parameter to be the USB device ID to
   * search for.
   */
  if (strncmp(port, "usb", 3) == 0) {
#if defined(HAVE_LIBUSB)
    serdev = &usb_serdev_frame;
    pinfo.usbinfo.vid = USB_VENDOR_ATMEL;
    pinfo.usbinfo.flags = 0;
    pinfo.usbinfo.pid = USB_DEVICE_STK600;
    PDATA(pgm)->pgmtype = PGMTYPE_STK600;
    pgm->set_sck_period = stk600_set_sck_period;
    pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_MKII;
    pgm->fd.usb.rep = USBDEV_BULK_EP_READ_STK600;
    pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_STK600;
    pgm->fd.usb.eep = 0;           /* no seperate EP for events */
#else
    avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
    return -1;
#endif
  }

  strcpy(pgm->port, port);
  if (serial_open(port, pinfo, &pgm->fd)==-1) {
    return -1;
  }

  /*
   * drain any extraneous input
   */
  stk500v2_drain(pgm, 0);

  stk500v2_getsync(pgm);

  stk500v2_drain(pgm, 0);

  if (pgm->bitclock != 0.0) {
    if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
      serial_close(&pgm->fd);
      return -1;
    }
  }

  return 0;
}


static void stk500v2_close(PROGRAMMER * pgm)
{
  DEBUG("STK500V2: stk500v2_close()\n");

  serial_close(&pgm->fd);
  pgm->fd.ifd = -1;
}


static int stk500v2_loadaddr(PROGRAMMER * pgm, unsigned int addr)
{
  unsigned char buf[16];
  int result;

  DEBUG("STK500V2: stk500v2_loadaddr(%d)\n",addr);

  buf[0] = CMD_LOAD_ADDRESS;
  buf[1] = (addr >> 24) & 0xff;
  buf[2] = (addr >> 16) & 0xff;
  buf[3] = (addr >> 8) & 0xff;
  buf[4] = addr & 0xff;

  result = stk500v2_command(pgm, buf, 5, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_loadaddr(): failed to set load address\n",
                    progname);
    return -1;
  }

  return 0;
}


/*
 * Read a single byte, generic HV mode
 */
static int stk500hv_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			      unsigned long addr, unsigned char * value,
			      enum hvmode mode)
{
  int result, cmdlen = 2;
  unsigned char buf[266];
  unsigned long paddr = 0UL, *paddr_ptr = NULL;
  unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0;
  unsigned char *cache_ptr = NULL;

  avrdude_message(MSG_NOTICE2, "%s: stk500hv_read_byte(.., %s, 0x%lx, ...)\n",
	    progname, mem->desc, addr);

  if (strcmp(mem->desc, "flash") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_FLASH_PP: CMD_READ_FLASH_HVSP;
    cmdlen = 3;
    pagesize = PDATA(pgm)->flash_pagesize;
    paddr = addr & ~(pagesize - 1);
    paddr_ptr = &PDATA(pgm)->flash_pageaddr;
    cache_ptr = PDATA(pgm)->flash_pagecache;
    addrshift = 1;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (mem->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  } else if (strcmp(mem->desc, "eeprom") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_EEPROM_PP: CMD_READ_EEPROM_HVSP;
    cmdlen = 3;
    pagesize = mem->page_size;
    if (pagesize == 0)
      pagesize = 1;
    paddr = addr & ~(pagesize - 1);
    paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
    cache_ptr = PDATA(pgm)->eeprom_pagecache;
  } else if (strcmp(mem->desc, "lfuse") == 0 ||
	     strcmp(mem->desc, "fuse") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_FUSE_PP: CMD_READ_FUSE_HVSP;
    addr = 0;
  } else if (strcmp(mem->desc, "hfuse") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_FUSE_PP: CMD_READ_FUSE_HVSP;
    addr = 1;
  } else if (strcmp(mem->desc, "efuse") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_FUSE_PP: CMD_READ_FUSE_HVSP;
    addr = 2;
  } else if (strcmp(mem->desc, "lock") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_LOCK_PP: CMD_READ_LOCK_HVSP;
  } else if (strcmp(mem->desc, "calibration") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_OSCCAL_PP: CMD_READ_OSCCAL_HVSP;
  } else if (strcmp(mem->desc, "signature") == 0) {
    buf[0] = mode == PPMODE? CMD_READ_SIGNATURE_PP: CMD_READ_SIGNATURE_HVSP;
  }

  /*
   * In HV mode, we have to use paged reads for flash and
   * EEPROM, and cache the results in a page cache.
   *
   * Page cache validation is based on "{flash,eeprom}_pageaddr"
   * (holding the base address of the most recent cache fill
   * operation).  This variable is set to (unsigned long)-1L when the
   * cache needs to be invalidated.
   */
  if (pagesize && paddr == *paddr_ptr) {
    *value = cache_ptr[addr & (pagesize - 1)];
    return 0;
  }

  if (cmdlen == 3) {
    /* long command, fill in # of bytes */
    buf[1] = (pagesize >> 8) & 0xff;
    buf[2] = pagesize & 0xff;

    /* flash and EEPROM reads require the load address command */
    if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
        return -1;
  } else {
    buf[1] = (char)addr;
  }

  avrdude_message(MSG_NOTICE2, "%s: stk500hv_read_byte(): Sending read memory command: ",
	    progname);

  result = stk500v2_command(pgm, buf, cmdlen, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500hv_read_byte(): "
                    "timeout/error communicating with programmer\n",
                    progname);
    return -1;
  }

  if (pagesize) {
    *paddr_ptr = paddr;
    memcpy(cache_ptr, buf + 2, pagesize);
    *value = cache_ptr[addr & (pagesize - 1)];
  } else {
    *value = buf[2];
  }

  return 0;
}

/*
 * Read a single byte, PP mode
 */
static int stk500pp_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			      unsigned long addr, unsigned char * value)
{
  return stk500hv_read_byte(pgm, p, mem, addr, value, PPMODE);
}

/*
 * Read a single byte, HVSP mode
 */
static int stk500hvsp_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
				unsigned long addr, unsigned char * value)
{
  return stk500hv_read_byte(pgm, p, mem, addr, value, HVSPMODE);
}

/*
 * Read a single byte, ISP mode
 *
 * By now, only used on the JTAGICE3 which does not implement the
 * CMD_SPI_MULTI SPI passthrough command.
 */
static int stk500isp_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			       unsigned long addr, unsigned char * value)
{
  int result, pollidx;
  unsigned char buf[6];
  unsigned long paddr = 0UL, *paddr_ptr = NULL;
  unsigned int pagesize = 0;
  unsigned char *cache_ptr = NULL;
  OPCODE *op;

  avrdude_message(MSG_NOTICE2, "%s: stk500isp_read_byte(.., %s, 0x%lx, ...)\n",
	    progname, mem->desc, addr);

  if (strcmp(mem->desc, "flash") == 0 ||
      strcmp(mem->desc, "eeprom") == 0) {
    // use paged access, and cache result
    if (strcmp(mem->desc, "flash") == 0) {
      pagesize = PDATA(pgm)->flash_pagesize;
      paddr = addr & ~(pagesize - 1);
      paddr_ptr = &PDATA(pgm)->flash_pageaddr;
      cache_ptr = PDATA(pgm)->flash_pagecache;
    } else {
      pagesize = mem->page_size;
      if (pagesize == 0)
	pagesize = 1;
      paddr = addr & ~(pagesize - 1);
      paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
      cache_ptr = PDATA(pgm)->eeprom_pagecache;
    }

    if (paddr == *paddr_ptr) {
      *value = cache_ptr[addr & (pagesize - 1)];
      return 0;
    }

    if (stk500v2_paged_load(pgm, p, mem, pagesize, paddr, pagesize) < 0)
      return -1;

    *paddr_ptr = paddr;
    memcpy(cache_ptr, &mem->buf[paddr], pagesize);
    *value = cache_ptr[addr & (pagesize - 1)];

    return 0;
  }

  if (strcmp(mem->desc, "lfuse") == 0 ||
	     strcmp(mem->desc, "fuse") == 0) {
    buf[0] = CMD_READ_FUSE_ISP;
    addr = 0;
  } else if (strcmp(mem->desc, "hfuse") == 0) {
    buf[0] = CMD_READ_FUSE_ISP;
    addr = 1;
  } else if (strcmp(mem->desc, "efuse") == 0) {
    buf[0] = CMD_READ_FUSE_ISP;
    addr = 2;
  } else if (strcmp(mem->desc, "lock") == 0) {
    buf[0] = CMD_READ_LOCK_ISP;
  } else if (strcmp(mem->desc, "calibration") == 0) {
    buf[0] = CMD_READ_OSCCAL_ISP;
  } else if (strcmp(mem->desc, "signature") == 0) {
    buf[0] = CMD_READ_SIGNATURE_ISP;
  }

  memset(buf + 1, 0, 5);
  if ((op = mem->op[AVR_OP_READ]) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500isp_read_byte(): invalid operation AVR_OP_READ on %s memory\n",
                    progname, mem->desc);
    return -1;
  }
  avr_set_bits(op, buf + 2);
  if ((pollidx = avr_get_output_index(op)) == -1) {
    avrdude_message(MSG_INFO, "%s: stk500isp_read_byte(): cannot determine pollidx to read %s memory\n",
                    progname, mem->desc);
    pollidx = 3;
  }
  buf[1] = pollidx + 1;
  avr_set_addr(op, buf + 2, addr);

  avrdude_message(MSG_NOTICE2, "%s: stk500isp_read_byte(): Sending read memory command: ",
	    progname);

  result = stk500v2_command(pgm, buf, 6, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500isp_read_byte(): "
                    "timeout/error communicating with programmer\n",
                    progname);
    return -1;
  }

  *value = buf[2];

  return 0;
}

/*
 * Write one byte, generic HV mode
 */
static int stk500hv_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			       unsigned long addr, unsigned char data,
			       enum hvmode mode)
{
  int result, cmdlen, timeout = 0, pulsewidth = 0;
  unsigned char buf[266];
  unsigned long paddr = 0UL, *paddr_ptr = NULL;
  unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0;
  unsigned char *cache_ptr = NULL;

  avrdude_message(MSG_NOTICE2, "%s: stk500hv_write_byte(.., %s, 0x%lx, ...)\n",
	    progname, mem->desc, addr);

  if (strcmp(mem->desc, "flash") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_FLASH_PP: CMD_PROGRAM_FLASH_HVSP;
    pagesize = PDATA(pgm)->flash_pagesize;
    paddr = addr & ~(pagesize - 1);
    paddr_ptr = &PDATA(pgm)->flash_pageaddr;
    cache_ptr = PDATA(pgm)->flash_pagecache;
    addrshift = 1;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (mem->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  } else if (strcmp(mem->desc, "eeprom") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_EEPROM_PP: CMD_PROGRAM_EEPROM_HVSP;
    pagesize = mem->page_size;
    if (pagesize == 0)
      pagesize = 1;
    paddr = addr & ~(pagesize - 1);
    paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
    cache_ptr = PDATA(pgm)->eeprom_pagecache;
  } else if (strcmp(mem->desc, "lfuse") == 0 ||
	     strcmp(mem->desc, "fuse") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_FUSE_PP: CMD_PROGRAM_FUSE_HVSP;
    addr = 0;
    pulsewidth = p->programfusepulsewidth;
    timeout = p->programfusepolltimeout;
  } else if (strcmp(mem->desc, "hfuse") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_FUSE_PP: CMD_PROGRAM_FUSE_HVSP;
    addr = 1;
    pulsewidth = p->programfusepulsewidth;
    timeout = p->programfusepolltimeout;
  } else if (strcmp(mem->desc, "efuse") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_FUSE_PP: CMD_PROGRAM_FUSE_HVSP;
    addr = 2;
    pulsewidth = p->programfusepulsewidth;
    timeout = p->programfusepolltimeout;
  } else if (strcmp(mem->desc, "lock") == 0) {
    buf[0] = mode == PPMODE? CMD_PROGRAM_LOCK_PP: CMD_PROGRAM_LOCK_HVSP;
    pulsewidth = p->programlockpulsewidth;
    timeout = p->programlockpolltimeout;
  } else {
    avrdude_message(MSG_INFO, "%s: stk500hv_write_byte(): "
                    "unsupported memory type: %s\n",
                    progname, mem->desc);
    return -1;
  }

  cmdlen = 5 + pagesize;

  /*
   * In HV mode, we have to use paged writes for flash and
   * EEPROM.  As both, flash and EEPROM cells can only be programmed
   * from `1' to `0' bits (even EEPROM does not support auto-erase in
   * parallel mode), we just pre-fill the page cache with 0xff, so all
   * those cells that are outside our current address will remain
   * unaffected.
   */
  if (pagesize) {
    memset(cache_ptr, 0xff, pagesize);
    cache_ptr[addr & (pagesize - 1)] = data;

    /* long command, fill in # of bytes */
    buf[1] = (pagesize >> 8) & 0xff;
    buf[2] = pagesize & 0xff;

    /*
     * Synthesize the mode byte.  This is simpler than adding yet
     * another parameter to the avrdude.conf file.  We calculate the
     * bits corresponding to the page size, as explained in AVR068.
     * We set bit 7, to indicate this is to actually write the page to
     * the target device.  We set bit 6 to indicate this is the very
     * last page to be programmed, whatever this means -- we just
     * pretend we don't know any better. ;-)  Bit 0 is set if this is
     * a paged memory, which means it has a page size of more than 2.
     */
    buf[3] = 0x80 | 0x40;
    if (pagesize > 2) {
      unsigned int rv = stk500v2_mode_for_pagesize(pagesize);
      if (rv == 0)
        return -1;
      buf[3] |= rv;
      buf[3] |= 0x01;
    }
    buf[4] = mem->delay;
    memcpy(buf + 5, cache_ptr, pagesize);

    /* flash and EEPROM reads require the load address command */
    if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
        return -1;
  } else {
    buf[1] = (char)addr;
    buf[2] = data;
    if (mode == PPMODE) {
      buf[3] = pulsewidth;
      buf[4] = timeout;
    } else {
      buf[3] = timeout;
      cmdlen--;
    }
  }

  avrdude_message(MSG_NOTICE2, "%s: stk500hv_write_byte(): Sending write memory command: ",
	    progname);

  result = stk500v2_command(pgm, buf, cmdlen, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500hv_write_byte(): "
                    "timeout/error communicating with programmer\n",
                    progname);
    return -1;
  }

  if (pagesize) {
    /* Invalidate the page cache. */
    *paddr_ptr = (unsigned long)-1L;
  }

  return 0;
}

/*
 * Write one byte, PP mode
 */
static int stk500pp_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			       unsigned long addr, unsigned char data)
{
  return stk500hv_write_byte(pgm, p, mem, addr, data, PPMODE);
}

/*
 * Write one byte, HVSP mode
 */
static int stk500hvsp_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
			       unsigned long addr, unsigned char data)
{
  return stk500hv_write_byte(pgm, p, mem, addr, data, HVSPMODE);
}


/*
 * Write one byte, ISP mode
 */
static int stk500isp_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
				unsigned long addr, unsigned char data)
{
  int result;
  unsigned char buf[5];
  unsigned long paddr = 0UL, *paddr_ptr = NULL;
  unsigned int pagesize = 0;
  unsigned char *cache_ptr = NULL;
  OPCODE *op;

  avrdude_message(MSG_NOTICE2, "%s: stk500isp_write_byte(.., %s, 0x%lx, ...)\n",
	    progname, mem->desc, addr);

  if (strcmp(mem->desc, "flash") == 0 ||
      strcmp(mem->desc, "eeprom") == 0) {
    if (strcmp(mem->desc, "flash") == 0) {
      pagesize = PDATA(pgm)->flash_pagesize;
      paddr = addr & ~(pagesize - 1);
      paddr_ptr = &PDATA(pgm)->flash_pageaddr;
      cache_ptr = PDATA(pgm)->flash_pagecache;
    } else {
      pagesize = mem->page_size;
      if (pagesize == 0)
	pagesize = 1;
      paddr = addr & ~(pagesize - 1);
      paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
      cache_ptr = PDATA(pgm)->eeprom_pagecache;
    }

    /*
     * We use paged writes for flash and EEPROM, reading back the
     * current page first, modify the byte to write, and write out the
     * entire page.
     */
    if (stk500v2_paged_load(pgm, p, mem, pagesize, paddr, pagesize) < 0)
      return -1;

    memcpy(cache_ptr, mem->buf + paddr, pagesize);
    *paddr_ptr = paddr;
    cache_ptr[addr & (pagesize - 1)] = data;
    memcpy(mem->buf + paddr, cache_ptr, pagesize);

    stk500v2_paged_write(pgm, p, mem, pagesize, paddr, pagesize);

    return 0;
  }

  memset(buf, 0, sizeof buf);
  if (strcmp(mem->desc, "lfuse") == 0 ||
	     strcmp(mem->desc, "fuse") == 0) {
    buf[0] = CMD_PROGRAM_FUSE_ISP;
    addr = 0;
  } else if (strcmp(mem->desc, "hfuse") == 0) {
    buf[0] = CMD_PROGRAM_FUSE_ISP;
    addr = 1;
  } else if (strcmp(mem->desc, "efuse") == 0) {
    buf[0] = CMD_PROGRAM_FUSE_ISP;
    addr = 2;
  } else if (strcmp(mem->desc, "lock") == 0) {
    buf[0] = CMD_PROGRAM_LOCK_ISP;
  } else {
    avrdude_message(MSG_INFO, "%s: stk500isp_write_byte(): "
                    "unsupported memory type: %s\n",
                    progname, mem->desc);
    return -1;
  }

  if ((op = mem->op[AVR_OP_WRITE]) == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500isp_write_byte(): "
                    "no AVR_OP_WRITE for %s memory\n",
                    progname, mem->desc);
    return -1;
  }

  avr_set_bits(op, buf + 1);
  avr_set_addr(op, buf + 1, addr);
  avr_set_input(op, buf + 1, data);

  avrdude_message(MSG_NOTICE2, "%s: stk500isp_write_byte(): Sending write memory command: ",
	    progname);

  result = stk500v2_command(pgm, buf, 5, sizeof(buf));

  if (result < 0) {
    avrdude_message(MSG_INFO, "%s: stk500isp_write_byte(): "
                    "timeout/error communicating with programmer\n",
                    progname);
    return -1;
  }

  /*
   * Prevent verification readback to be too fast, see
   * https://savannah.nongnu.org/bugs/index.php?42267
   *
   * After all, this is just an ugly hack working around some
   * brokeness in the Atmel firmware starting with the AVRISPmkII (the
   * old JTAGICEmkII isn't affected).  Let's hope 10 ms of additional
   * delay are good enough for everyone.
   */
  usleep(10000);

  return 0;
}

static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                unsigned int page_size,
                                unsigned int addr, unsigned int n_bytes)
{
// static int page = 0;
  unsigned int block_size, last_addr, addrshift, use_ext_addr;
  unsigned int maxaddr = addr + n_bytes;
  unsigned char commandbuf[10];
  unsigned char buf[266];
  unsigned char cmds[4];
  int result;
  OPCODE * rop, * wop;

  // Prusa3D workaround for a bug in the USB communication controller. The semicolon character is used as an initial character
  // for special command sequences for the USB communication chip. The early releases of the USB communication chip had
  // a bug, which produced a 0x0ff character after the semicolon. The patch is to program the semicolons by flashing
  // firmware blocks twice: First the low nibbles of semicolons, second the high nibbles of the semicolon characters.
  //
  // Inside the 2nd round of a firmware block flashing?
  bool prusa3d_semicolon_workaround_round2 = false;
  // Buffer containing the other nibbles of semicolons to be flashed in the 2nd round.
  unsigned char prusa3d_semicolon_workaround_round2_data[256];

  DEBUG("STK500V2: stk500v2_paged_write(..,%s,%u,%u,%u)\n",
        m->desc, page_size, addr, n_bytes);

  if (page_size == 0) page_size = 256;
  addrshift = 0;
  use_ext_addr = 0;

  // determine which command is to be used
  if (strcmp(m->desc, "flash") == 0) {
    addrshift = 1;
    commandbuf[0] = CMD_PROGRAM_FLASH_ISP;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  } else if (strcmp(m->desc, "eeprom") == 0) {
    commandbuf[0] = CMD_PROGRAM_EEPROM_ISP;
  }
  commandbuf[4] = m->delay;

  if (addrshift == 0) {
    wop = m->op[AVR_OP_WRITE];
    rop = m->op[AVR_OP_READ];
  }
  else {
    wop = m->op[AVR_OP_WRITE_LO];
    rop = m->op[AVR_OP_READ_LO];
  }

  // if the memory is paged, load the appropriate commands into the buffer
  if (m->mode & 0x01) {
    commandbuf[3] = m->mode | 0x80;   // yes, write the page to flash

    if (m->op[AVR_OP_LOADPAGE_LO] == NULL) {
      avrdude_message(MSG_INFO, "%s: stk500v2_paged_write: loadpage instruction not defined for part \"%s\"\n",
              progname, p->desc);
      return -1;
    }
    avr_set_bits(m->op[AVR_OP_LOADPAGE_LO], cmds);
    commandbuf[5] = cmds[0];

    if (m->op[AVR_OP_WRITEPAGE] == NULL) {
      avrdude_message(MSG_INFO, "%s: stk500v2_paged_write: write page instruction not defined for part \"%s\"\n",
              progname, p->desc);
      return -1;
    }
    avr_set_bits(m->op[AVR_OP_WRITEPAGE], cmds);
    commandbuf[6] = cmds[0];

  // otherwise, we need to load different commands in
  } 
  else {
    commandbuf[3] = m->mode | 0x80;   // yes, write the words to flash

    if (wop == NULL) {
      avrdude_message(MSG_INFO, "%s: stk500v2_paged_write: write instruction not defined for part \"%s\"\n",
              progname, p->desc);
      return -1;
    }
    avr_set_bits(wop, cmds);
    commandbuf[5] = cmds[0];
    commandbuf[6] = 0;
  }

  // the read command is common to both methods
  if (rop == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_paged_write: read instruction not defined for part \"%s\"\n",
            progname, p->desc);
    return -1;
  }
  avr_set_bits(rop, cmds);
  commandbuf[7] = cmds[0];

  commandbuf[8] = m->readback[0];
  commandbuf[9] = m->readback[1];

  last_addr=UINT_MAX;   /* impossible address */

  for (; addr < maxaddr; addr += prusa3d_semicolon_workaround_round2 ? 0 : page_size) {
    if ((maxaddr - addr) < page_size)
      block_size = maxaddr - addr;
    else
      block_size = page_size;

    DEBUG("block_size at addr %d is %d\n",addr,block_size);

    memcpy(buf,commandbuf,sizeof(commandbuf));

    buf[1] = block_size >> 8;
    buf[2] = block_size & 0xff;

    if((last_addr==UINT_MAX)||(last_addr+block_size != addr)||prusa3d_semicolon_workaround_round2){
      if (stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)) < 0)
        return -1;
    }
    last_addr=addr;

    if (prusa3d_semicolon_workaround_round2) {
        // printf("Round 2: address %d\r\n", addr);
        memcpy(buf+10, prusa3d_semicolon_workaround_round2_data, block_size);
        prusa3d_semicolon_workaround_round2 = false;
    } else {
        for (size_t i = 0; i < block_size; ++ i) {
            unsigned char b = m->buf[addr+i];
            if (b == ';') {
              // printf("semicolon at %d %d\r\n", addr, i);
              prusa3d_semicolon_workaround_round2_data[i] = b | 0x0f0;
              b |= 0x0f;
              prusa3d_semicolon_workaround_round2 = true;
            } else 
              prusa3d_semicolon_workaround_round2_data[i] = 0x0ff;
            buf[i+10] = b;
        }
    }

    result = stk500v2_command(pgm,buf,block_size+10, sizeof(buf));
    if (result < 0) {
      avrdude_message(MSG_INFO, "%s: stk500v2_paged_write: write command failed\n",
                      progname);
      return -1;
    }
  }

  return n_bytes;
}

/*
 * Write pages of flash/EEPROM, generic HV mode
 */
static int stk500hv_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                unsigned int page_size,
                                unsigned int addr, unsigned int n_bytes,
                                enum hvmode mode)
{
  unsigned int block_size, last_addr, addrshift, use_ext_addr;
  unsigned int maxaddr = addr + n_bytes;
  unsigned char commandbuf[5], buf[266];
  int result;

  DEBUG("STK500V2: stk500hv_paged_write(..,%s,%u,%u)\n",
        m->desc, page_size, addr, n_bytes);

  addrshift = 0;
  use_ext_addr = 0;

  // determine which command is to be used
  if (strcmp(m->desc, "flash") == 0) {
    addrshift = 1;
    PDATA(pgm)->flash_pageaddr = (unsigned long)-1L;
    commandbuf[0] = mode == PPMODE? CMD_PROGRAM_FLASH_PP: CMD_PROGRAM_FLASH_HVSP;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  } else if (strcmp(m->desc, "eeprom") == 0) {
    PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;
    commandbuf[0] = mode == PPMODE? CMD_PROGRAM_EEPROM_PP: CMD_PROGRAM_EEPROM_HVSP;
  }
  /*
   * Synthesize the mode byte.  This is simpler than adding yet
   * another parameter to the avrdude.conf file.  We calculate the
   * bits corresponding to the page size, as explained in AVR068.  We
   * set bit 7, to indicate this is to actually write the page to the
   * target device.  We set bit 6 to indicate this is the very last
   * page to be programmed, whatever this means -- we just pretend we
   * don't know any better. ;-)  Finally, we set bit 0 to say this is
   * a paged memory, after all, that's why we got here at all.
   */
  commandbuf[3] = 0x80 | 0x40;
  if (page_size > 2) {
    unsigned int rv = stk500v2_mode_for_pagesize(page_size);
    if (rv == 0)
      return -1;
    commandbuf[3] |= rv;
    commandbuf[3] |= 0x01;
  }
  commandbuf[4] = m->delay;

  if (page_size == 0) page_size = 256;

  last_addr = UINT_MAX;		/* impossible address */

  for (; addr < maxaddr; addr += page_size) {
    if ((maxaddr - addr) < page_size)
      block_size = maxaddr - addr;
    else
      block_size = page_size;

    DEBUG("block_size at addr %d is %d\n",addr,block_size);

    memcpy(buf, commandbuf, sizeof(commandbuf));

    buf[1] = page_size >> 8;
    buf[2] = page_size & 0xff;

    if ((last_addr == UINT_MAX) || (last_addr + block_size != addr)) {
      if (stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)) < 0)
        return -1;
    }
    last_addr=addr;

    memcpy(buf + 5, m->buf + addr, block_size);
    if (block_size != page_size)
      memset(buf + 5 + block_size, 0xff, page_size - block_size);

    result = stk500v2_command(pgm, buf, page_size + 5, sizeof(buf));
    if (result < 0) {
      avrdude_message(MSG_INFO, "%s: stk500hv_paged_write: write command failed\n",
                      progname);
      return -1;
    }
  }

  return n_bytes;
}

/*
 * Write pages of flash/EEPROM, PP mode
 */
static int stk500pp_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                unsigned int page_size,
                                unsigned int addr, unsigned int n_bytes)
{
  return stk500hv_paged_write(pgm, p, m, page_size, addr, n_bytes, PPMODE);
}

/*
 * Write pages of flash/EEPROM, HVSP mode
 */
static int stk500hvsp_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                  unsigned int page_size,
                                  unsigned int addr, unsigned int n_bytes)
{
  return stk500hv_paged_write(pgm, p, m, page_size, addr, n_bytes, HVSPMODE);
}

static int stk500v2_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                               unsigned int page_size,
                               unsigned int addr, unsigned int n_bytes)
{
  unsigned int block_size, hiaddr, addrshift, use_ext_addr;
  unsigned int maxaddr = addr + n_bytes;
  unsigned char commandbuf[4];
  unsigned char buf[275];	// max buffer size for stk500v2 at this point
  unsigned char cmds[4];
  int result;
  OPCODE * rop;

  DEBUG("STK500V2: stk500v2_paged_load(..,%s,%u,%u,%u)\n",
        m->desc, page_size, addr, n_bytes);

  page_size = m->readsize;

  rop = m->op[AVR_OP_READ];

  hiaddr = UINT_MAX;
  addrshift = 0;
  use_ext_addr = 0;

  // determine which command is to be used
  if (strcmp(m->desc, "flash") == 0) {
    commandbuf[0] = CMD_READ_FLASH_ISP;
    rop = m->op[AVR_OP_READ_LO];
    addrshift = 1;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  }
  else if (strcmp(m->desc, "eeprom") == 0) {
    commandbuf[0] = CMD_READ_EEPROM_ISP;
  }

  // the read command is common to both methods
  if (rop == NULL) {
    avrdude_message(MSG_INFO, "%s: stk500v2_paged_load: read instruction not defined for part \"%s\"\n",
            progname, p->desc);
    return -1;
  }
  avr_set_bits(rop, cmds);
  commandbuf[3] = cmds[0];

  for (; addr < maxaddr; addr += page_size) {
    if ((maxaddr - addr) < page_size)
      block_size = maxaddr - addr;
    else
      block_size = page_size;
    DEBUG("block_size at addr %d is %d\n",addr,block_size);

    memcpy(buf,commandbuf,sizeof(commandbuf));

    buf[1] = block_size >> 8;
    buf[2] = block_size & 0xff;

    // Ensure a new "load extended address" will be issued
    // when crossing a 64 KB boundary in flash.
    if (hiaddr != (addr & ~0xFFFF)) {
      hiaddr = addr & ~0xFFFF;
      if (stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)) < 0)
        return -1;
    }

    result = stk500v2_command(pgm,buf,4,sizeof(buf));
    if (result < 0) {
      avrdude_message(MSG_INFO, "%s: stk500v2_paged_load: read command failed\n",
                      progname);
      return -1;
    }
#if 0
    for (i=0;i<page_size;i++) {
      avrdude_message(MSG_INFO, "%02X",buf[2+i]);
      if (i%16 == 15) avrdude_message(MSG_INFO, "\n");
    }
#endif

    memcpy(&m->buf[addr], &buf[2], block_size);
  }

  return n_bytes;
}


/*
 * Read pages of flash/EEPROM, generic HV mode
 */
static int stk500hv_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                               unsigned int page_size,
                               unsigned int addr, unsigned int n_bytes,
                               enum hvmode mode)
{
  unsigned int block_size, hiaddr, addrshift, use_ext_addr;
  unsigned int maxaddr = addr + n_bytes;
  unsigned char commandbuf[3], buf[266];
  int result;

  DEBUG("STK500V2: stk500hv_paged_load(..,%s,%u,%u,%u)\n",
        m->desc, page_size, addr, n_bytes);

  page_size = m->readsize;

  hiaddr = UINT_MAX;
  addrshift = 0;
  use_ext_addr = 0;

  // determine which command is to be used
  if (strcmp(m->desc, "flash") == 0) {
    commandbuf[0] = mode == PPMODE? CMD_READ_FLASH_PP: CMD_READ_FLASH_HVSP;
    addrshift = 1;
    /*
     * If bit 31 is set, this indicates that the following read/write
     * operation will be performed on a memory that is larger than
     * 64KBytes. This is an indication to STK500 that a load extended
     * address must be executed.
     */
    if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) {
      use_ext_addr = (1U << 31);
    }
  }
  else if (strcmp(m->desc, "eeprom") == 0) {
    commandbuf[0] = mode == PPMODE? CMD_READ_EEPROM_PP: CMD_READ_EEPROM_HVSP;
  }

  for (; addr < maxaddr; addr += page_size) {
    if ((maxaddr - addr) < page_size)
      block_size = maxaddr - addr;
    else
      block_size = page_size;
    DEBUG("block_size at addr %d is %d\n",addr,block_size);

    memcpy(buf, commandbuf, sizeof(commandbuf));

    buf[1] = block_size >> 8;
    buf[2] = block_size & 0xff;

    // Ensure a new "load extended address" will be issued
    // when crossing a 64 KB boundary in flash.
    if (hiaddr != (addr & ~0xFFFF)) {
      hiaddr = addr & ~0xFFFF;
      if (stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)) < 0)
        return -1;
    }

    result = stk500v2_command(pgm, buf, 3, sizeof(buf));
    if (result < 0) {
      avrdude_message(MSG_INFO, "%s: stk500hv_paged_load: read command failed\n",
                      progname);
      return -1;
    }
#if 0
    for (i = 0; i < page_size; i++) {
      avrdude_message(MSG_INFO, "%02X", buf[2 + i]);
      if (i % 16 == 15) avrdude_message(MSG_INFO, "\n");
    }
#endif

    memcpy(&m->buf[addr], &buf[2], block_size);
  }

  return n_bytes;
}

/*
 * Read pages of flash/EEPROM, PP mode
 */
static int stk500pp_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                               unsigned int page_size,
                               unsigned int addr, unsigned int n_bytes)
{
  return stk500hv_paged_load(pgm, p, m, page_size, addr, n_bytes, PPMODE);
}

/*
 * Read pages of flash/EEPROM, HVSP mode
 */
static int stk500hvsp_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                 unsigned int page_size,
                                 unsigned int addr, unsigned int n_bytes)
{
  return stk500hv_paged_load(pgm, p, m, page_size, addr, n_bytes, HVSPMODE);
}


static int stk500v2_page_erase(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                               unsigned int addr)
{
  avrdude_message(MSG_INFO, "%s: stk500v2_page_erase(): this function must never be called\n",
                  progname);
  return -1;
}

static int stk500v2_set_vtarget(PROGRAMMER * pgm, double v)
{
  unsigned char uaref, utarg;

  utarg = (unsigned)((v + 0.049) * 10);

  if (stk500v2_getparm(pgm, PARAM_VADJUST, &uaref) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): cannot obtain V[aref]\n",
                    progname);
    return -1;
  }

  if (uaref > utarg) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): reducing V[aref] from %.1f to %.1f\n",
                    progname, uaref / 10.0, v);
    if (stk500v2_setparm(pgm, PARAM_VADJUST, utarg)
	!= 0)
      return -1;
  }
  return stk500v2_setparm(pgm, PARAM_VTARGET, utarg);
}


static int stk500v2_set_varef(PROGRAMMER * pgm, unsigned int chan /* unused */,
                              double v)
{
  unsigned char uaref, utarg;

  uaref = (unsigned)((v + 0.049) * 10);

  if (stk500v2_getparm(pgm, PARAM_VTARGET, &utarg) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_varef(): cannot obtain V[target]\n",
                    progname);
    return -1;
  }

  if (uaref > utarg) {
    avrdude_message(MSG_INFO, "%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, PARAM_VADJUST, uaref);
}


static int stk500v2_set_fosc(PROGRAMMER * pgm, double v)
{
  int fosc;
  unsigned char prescale, cmatch;
  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";
      avrdude_message(MSG_INFO, "%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 = (int)v;

    for (idx = 0; idx < sizeof(ps) / sizeof(ps[0]); idx++) {
      if (fosc >= (int)(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])) {
      avrdude_message(MSG_INFO, "%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, PARAM_OSC_PSCALE, prescale)) != 0
      || (rc = stk500v2_setparm(pgm, PARAM_OSC_CMATCH, cmatch)) != 0)
    return rc;

  return 0;
}

/* The list of SCK frequencies supported by the AVRISP mkII, as listed
 * in AVR069 */
static double avrispmkIIfreqs[] = {
	8000000, 4000000, 2000000, 1000000, 500000, 250000, 125000,
	96386, 89888, 84211, 79208, 74767, 70797, 67227, 64000,
	61069, 58395, 55945, 51613, 49690, 47905, 46243, 43244,
	41885, 39409, 38278, 36200, 34335, 32654, 31129, 29740,
	28470, 27304, 25724, 24768, 23461, 22285, 21221, 20254,
	19371, 18562, 17583, 16914, 16097, 15356, 14520, 13914,
	13224, 12599, 12031, 11511, 10944, 10431, 9963, 9468,
	9081, 8612, 8239, 7851, 7498, 7137, 6809, 6478, 6178,
	5879, 5607, 5359, 5093, 4870, 4633, 4418, 4209, 4019,
	3823, 3645, 3474, 3310, 3161, 3011, 2869, 2734, 2611,
	2484, 2369, 2257, 2152, 2052, 1956, 1866, 1779, 1695,
	1615, 1539, 1468, 1398, 1333, 1271, 1212, 1155, 1101,
	1049, 1000, 953, 909, 866, 826, 787, 750, 715, 682,
	650, 619, 590, 563, 536, 511, 487, 465, 443, 422,
	402, 384, 366, 349, 332, 317, 302, 288, 274, 261,
	249, 238, 226, 216, 206, 196, 187, 178, 170, 162,
	154, 147, 140, 134, 128, 122, 116, 111, 105, 100,
	95.4, 90.9, 86.6, 82.6, 78.7, 75.0, 71.5, 68.2,
	65.0, 61.9, 59.0, 56.3, 53.6, 51.1
};

static int stk500v2_set_sck_period_mk2(PROGRAMMER * pgm, double v)
{
  int i;

  for (i = 0; i < sizeof(avrispmkIIfreqs); i++) {
    if (1 / avrispmkIIfreqs[i] >= v)
      break;
  }

  avrdude_message(MSG_NOTICE2, "Using p = %.2f us for SCK (param = %d)\n",
	    1000000 / avrispmkIIfreqs[i], i);

  return stk500v2_setparm(pgm, PARAM_SCK_DURATION, i);
}

/*
 * Return the "mode" value for the parallel and HVSP modes that
 * corresponds to the pagesize.
 */
static unsigned int stk500v2_mode_for_pagesize(unsigned int pagesize)
{
  switch (pagesize)
    {
    case 256:  return 0u << 1;
    case 2:    return 1u << 1;
    case 4:    return 2u << 1;
    case 8:    return 3u << 1;
    case 16:   return 4u << 1;
    case 32:   return 5u << 1;
    case 64:   return 6u << 1;
    case 128:  return 7u << 1;
    }
  avrdude_message(MSG_INFO, "%s: stk500v2_mode_for_pagesize(): invalid pagesize: %u\n",
                  progname, pagesize);
  return 0;
}

/*
 * See pseudo-code in AVR068
 *
 * This algorithm only fits for the STK500 itself.  For the (old)
 * AVRISP, the resulting ISP clock is only half.  While this would be
 * easy to fix in the algorithm, we'd need to add another
 * configuration flag for this to the config file.  Given the old
 * AVRISP devices are virtually no longer around (and the AVRISPmkII
 * uses a different algorithm below), it's probably not worth the
 * hassle.
 */
static int stk500v2_set_sck_period(PROGRAMMER * pgm, double v)
{
  unsigned int d;
  unsigned char dur;
  double f = 1 / v;

  if (f >= 1.8432E6)
    d = 0;
  else if (f > 460.8E3)
    d = 1;
  else if (f > 115.2E3)
    d = 2;
  else if (f > 57.6E3)
    d = 3;
  else
    d = (unsigned int)ceil(1 / (24 * f / (double)STK500V2_XTAL) - 10.0 / 12.0);
  if (d >= 255)
    d = 254;
  dur = d;

  return stk500v2_setparm(pgm, PARAM_SCK_DURATION, dur);
}

static double stk500v2_sck_to_us(PROGRAMMER * pgm, unsigned char dur)
{
  double x;

  if (dur == 0)
    return 0.5425;
  if (dur == 1)
    return 2.17;
  if (dur == 2)
    return 8.68;
  if (dur == 3)
    return 17.36;

  x = (double)dur + 10.0 / 12.0;
  x = 1.0 / x;
  x /= 24.0;
  x *= (double)STK500V2_XTAL;
  return 1E6 / x;
}


static int stk600_set_vtarget(PROGRAMMER * pgm, double v)
{
  unsigned char utarg;
  unsigned int uaref;
  int rv;

  utarg = (unsigned)((v + 0.049) * 10);

  if (stk500v2_getparm2(pgm, PARAM2_AREF0, &uaref) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): cannot obtain V[aref][0]\n",
                    progname);
    return -1;
  }

  if (uaref > (unsigned)utarg * 10) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): reducing V[aref][0] from %.2f to %.1f\n",
                    progname, uaref / 100.0, v);
    uaref = 10 * (unsigned)utarg;
    if (stk500v2_setparm2(pgm, PARAM2_AREF0, uaref)
	!= 0)
      return -1;
  }

  if (stk500v2_getparm2(pgm, PARAM2_AREF1, &uaref) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): cannot obtain V[aref][1]\n",
                    progname);
    return -1;
  }

  if (uaref > (unsigned)utarg * 10) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_vtarget(): reducing V[aref][1] from %.2f to %.1f\n",
                    progname, uaref / 100.0, v);
    uaref = 10 * (unsigned)utarg;
    if (stk500v2_setparm2(pgm, PARAM2_AREF1, uaref)
	!= 0)
      return -1;
  }

  /*
   * Vtarget on the STK600 can only be adjusted while not being in
   * programming mode.
   */
  if (PDATA(pgm)->lastpart)
      pgm->disable(pgm);
  rv = stk500v2_setparm(pgm, PARAM_VTARGET, utarg);
  if (PDATA(pgm)->lastpart)
      pgm->program_enable(pgm, PDATA(pgm)->lastpart);

  return rv;
}


static int stk600_set_varef(PROGRAMMER * pgm, unsigned int chan, double v)
{
  unsigned char utarg;
  unsigned int uaref;

  uaref = (unsigned)((v + 0.0049) * 100);

  if (stk500v2_getparm(pgm, PARAM_VTARGET, &utarg) != 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_varef(): cannot obtain V[target]\n",
                    progname);
    return -1;
  }

  if (uaref > (unsigned)utarg * 10) {
    avrdude_message(MSG_INFO, "%s: stk500v2_set_varef(): V[aref] must not be greater than "
                    "V[target] = %.1f\n",
                    progname, utarg / 10.0);
    return -1;
  }

  switch (chan)
  {
  case 0:
    return stk500v2_setparm2(pgm, PARAM2_AREF0, uaref);

  case 1:
    return stk500v2_setparm2(pgm, PARAM2_AREF1, uaref);

  default:
    avrdude_message(MSG_INFO, "%s: stk500v2_set_varef(): invalid channel %d\n",
                    progname, chan);
    return -1;
  }
}


static int stk600_set_fosc(PROGRAMMER * pgm, double v)
{
  unsigned int oct, dac;

  oct = (unsigned)(1.443 * log(v / 1039.0));
  dac = (unsigned)(2048.0 - (2078.0 * pow(2, (double)(10 + oct))) / v);

  return stk500v2_setparm2(pgm, PARAM2_CLOCK_CONF, (oct << 12) | (dac << 2));
}

static int stk600_set_sck_period(PROGRAMMER * pgm, double v)
{
  unsigned int sck;

  sck = (unsigned)ceil((16e6 / (2 * 1.0 / v)) - 1);

  if (sck >= 4096)
    sck = 4095;

  return stk500v2_setparm2(pgm, PARAM2_SCK_DURATION, sck);
}

static int stk500v2_jtag3_set_sck_period(PROGRAMMER * pgm, double v)
{
  unsigned char value[3];
  unsigned int sck;

  if (v < 1E-6)
    sck = 0x400;
  else if (v > 1E-3)
    sck = 1;
  else
    sck = (unsigned)(1.0 / (1000.0 * v));

  value[0] = CMD_SET_SCK;
  value[1] = sck & 0xff;
  value[2] = (sck >> 8) & 0xff;

  if (stk500v2_jtag3_send(pgm, value, 3) < 0)
    return -1;
  if (stk500v2_jtag3_recv(pgm, value, 3) < 0)
    return -1;
  return 0;
}

static int stk500v2_getparm(PROGRAMMER * pgm, unsigned char parm, unsigned char * value)
{
  unsigned char buf[32];

  buf[0] = CMD_GET_PARAMETER;
  buf[1] = parm;

  if (stk500v2_command(pgm, buf, 2, sizeof(buf)) < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_getparm(): failed to get parameter 0x%02x\n",
            progname, parm);
    return -1;
  }

  *value = buf[2];

  return 0;
}

static int stk500v2_setparm_real(PROGRAMMER * pgm, unsigned char parm, unsigned char value)
{
  unsigned char buf[32];

  buf[0] = CMD_SET_PARAMETER;
  buf[1] = parm;
  buf[2] = value;

  if (stk500v2_command(pgm, buf, 3, sizeof(buf)) < 0) {
    avrdude_message(MSG_INFO, "\n%s: stk500v2_setparm(): failed to set parameter 0x%02x\n",
            progname, parm);
    return -1;
  }

  return 0;
}

static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned char value)
{
  unsigned char current_value = 0;
  int res;

  res = stk500v2_getparm(pgm, parm, &current_value);
  if (res < 0)
    avrdude_message(MSG_INFO, "%s: Unable to get parameter 0x%02x\n", progname, parm);

  // don't issue a write if the correct value is already set.
  if (value == current_value) {
    avrdude_message(MSG_NOTICE2, "%s: Skipping parameter write; parameter value already set.\n", progname);
    return 0;
  }

  return stk500v2_setparm_real(pgm, parm, value);
}

static int stk500v2_getparm2(PROGRAMMER * pgm, unsigned char parm, unsigned int * value)
{
  unsigned char buf[32];

  buf[0] = CMD_GET_PARAMETER;
  buf[1] = parm;

  if (stk500v2_command(pgm, buf, 2, sizeof(buf)) < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_getparm2(): failed to get parameter 0x%02x\n",
            progname, parm);
    return -1;
  }

  *value = ((unsigned)buf[2] << 8) | buf[3];

  return 0;
}

static int stk500v2_setparm2(PROGRAMMER * pgm, unsigned char parm, unsigned int value)
{
  unsigned char buf[32];

  buf[0] = CMD_SET_PARAMETER;
  buf[1] = parm;
  buf[2] = value >> 8;
  buf[3] = value;

  if (stk500v2_command(pgm, buf, 4, sizeof(buf)) < 0) {
    avrdude_message(MSG_INFO, "\n%s: stk500v2_setparm2(): failed to set parameter 0x%02x\n",
            progname, parm);
    return -1;
  }

  return 0;
}

static const char *stk600_get_cardname(const struct carddata *table,
				       size_t nele, int id)
{
  const struct carddata *cdp;

  if (id == 0xFF)
    /* 0xFF means this card is not present at all. */
    return "Not present";

  for (cdp = table; nele > 0; cdp++, nele--)
    if (cdp->id == id)
      return cdp->name;

  return "Unknown";
}


static void stk500v2_display(PROGRAMMER * pgm, const char * p)
{
  unsigned char maj = 0;
  unsigned char min = 0;
  unsigned char hdw = 0;
  unsigned char topcard = 0;
  unsigned char maj_s1 = 0;
  unsigned char min_s1 = 0;
  unsigned char maj_s2 = 0;
  unsigned char min_s2 = 0;
  unsigned int rev = 0;
  const char *topcard_name, *pgmname;

  switch (PDATA(pgm)->pgmtype) {
    case PGMTYPE_UNKNOWN:     pgmname = "Unknown"; break;
    case PGMTYPE_STK500:      pgmname = "STK500"; break;
    case PGMTYPE_AVRISP:      pgmname = "AVRISP"; break;
    case PGMTYPE_AVRISP_MKII: pgmname = "AVRISP mkII"; break;
    case PGMTYPE_STK600:      pgmname = "STK600"; break;
    default:                  pgmname = "None";
  }
  if (PDATA(pgm)->pgmtype != PGMTYPE_JTAGICE_MKII &&
      PDATA(pgm)->pgmtype != PGMTYPE_JTAGICE3) {
    avrdude_message(MSG_INFO, "%sProgrammer Model: %s\n", p, pgmname);
    stk500v2_getparm(pgm, PARAM_HW_VER, &hdw);
    stk500v2_getparm(pgm, PARAM_SW_MAJOR, &maj);
    stk500v2_getparm(pgm, PARAM_SW_MINOR, &min);
    avrdude_message(MSG_INFO, "%sHardware Version: %d\n", p, hdw);
    avrdude_message(MSG_INFO, "%sFirmware Version Master : %d.%02d\n", p, maj, min);
    if (PDATA(pgm)->pgmtype == PGMTYPE_STK600) {
      stk500v2_getparm(pgm, PARAM_SW_MAJOR_SLAVE1, &maj_s1);
      stk500v2_getparm(pgm, PARAM_SW_MINOR_SLAVE1, &min_s1);
      stk500v2_getparm(pgm, PARAM_SW_MAJOR_SLAVE2, &maj_s2);
      stk500v2_getparm(pgm, PARAM_SW_MINOR_SLAVE2, &min_s2);
      avrdude_message(MSG_INFO, "%sFirmware Version Slave 1: %d.%02d\n", p, maj_s1, min_s1);
      avrdude_message(MSG_INFO, "%sFirmware Version Slave 2: %d.%02d\n", p, maj_s2, min_s2);
    }
  }

  if (PDATA(pgm)->pgmtype == PGMTYPE_STK500) {
    stk500v2_getparm(pgm, PARAM_TOPCARD_DETECT, &topcard);
    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;
    }
    avrdude_message(MSG_INFO, "%sTopcard         : %s\n", p, topcard_name);
  } else if (PDATA(pgm)->pgmtype == PGMTYPE_STK600) {
    stk500v2_getparm(pgm, PARAM_ROUTINGCARD_ID, &topcard);
    avrdude_message(MSG_INFO, "%sRouting card    : %s\n", p,
	    stk600_get_cardname(routing_cards,
				sizeof routing_cards / sizeof routing_cards[0],
				topcard));
    stk500v2_getparm(pgm, PARAM_SOCKETCARD_ID, &topcard);
    avrdude_message(MSG_INFO, "%sSocket card     : %s\n", p,
	    stk600_get_cardname(socket_cards,
				sizeof socket_cards / sizeof socket_cards[0],
				topcard));
    stk500v2_getparm2(pgm, PARAM2_RC_ID_TABLE_REV, &rev);
    avrdude_message(MSG_INFO, "%sRC_ID table rev : %d\n", p, rev);
    stk500v2_getparm2(pgm, PARAM2_EC_ID_TABLE_REV, &rev);
    avrdude_message(MSG_INFO, "%sEC_ID table rev : %d\n", p, rev);
  }
  stk500v2_print_parms1(pgm, p);

  return;
}

static double
f_to_kHz_MHz(double f, const char **unit)
{
  if (f > 1e6) {
    f /= 1e6;
    *unit = "MHz";
  } else if (f > 1e3) {
    f /= 1000;
    *unit = "kHz";
  } else
    *unit = "Hz";

  return f;
}

static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p)
{
  unsigned char vtarget = 0;
  unsigned char vadjust = 0;
  unsigned char sck_duration = 0;
  unsigned char osc_pscale = 0;
  unsigned char osc_cmatch = 0;
  unsigned varef = 0;
  unsigned sck_stk600 = 0;
  unsigned clock_conf = 0;
  unsigned dac, oct;
  // unsigned char vtarget_jtag[4];
  int prescale;
  double f;
  const char *unit;
  // void *mycookie;

  if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII) {
    return;
    // mycookie = pgm->cookie;
    // pgm->cookie = PDATA(pgm)->chained_pdata;
    // jtagmkII_getparm(pgm, PAR_OCD_VTARGET, vtarget_jtag);
    // pgm->cookie = mycookie;
    // avrdude_message(MSG_INFO, "%sVtarget         : %.1f V\n", p,
	   //  b2_to_u16(vtarget_jtag) / 1000.0);
  } else if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE3) {
    return;
    // mycookie = pgm->cookie;
    // pgm->cookie = PDATA(pgm)->chained_pdata;
    // jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_VTARGET, vtarget_jtag, 2);
    // pgm->cookie = mycookie;
    // avrdude_message(MSG_INFO, "%sVtarget         : %.1f V\n", p,
	   //  b2_to_u16(vtarget_jtag) / 1000.0);

  } else {
    stk500v2_getparm(pgm, PARAM_VTARGET, &vtarget);
    avrdude_message(MSG_INFO, "%sVtarget         : %.1f V\n", p, vtarget / 10.0);
  }

  switch (PDATA(pgm)->pgmtype) {
  case PGMTYPE_STK500:
    stk500v2_getparm(pgm, PARAM_SCK_DURATION, &sck_duration);
    stk500v2_getparm(pgm, PARAM_VADJUST, &vadjust);
    stk500v2_getparm(pgm, PARAM_OSC_PSCALE, &osc_pscale);
    stk500v2_getparm(pgm, PARAM_OSC_CMATCH, &osc_cmatch);
    avrdude_message(MSG_INFO, "%sSCK period      : %.1f us\n", p,
	    stk500v2_sck_to_us(pgm, sck_duration));
    avrdude_message(MSG_INFO, "%sVaref           : %.1f V\n", p, vadjust / 10.0);
    avrdude_message(MSG_INFO, "%sOscillator      : ", p);
    if (osc_pscale == 0)
      avrdude_message(MSG_INFO, "Off\n");
    else {
      prescale = 1;
      f = STK500V2_XTAL / 2;

      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);
      f = f_to_kHz_MHz(f, &unit);
      avrdude_message(MSG_INFO, "%.3f %s\n", f, unit);
    }
    break;

  case PGMTYPE_AVRISP_MKII:
  case PGMTYPE_JTAGICE_MKII:
    stk500v2_getparm(pgm, PARAM_SCK_DURATION, &sck_duration);
    avrdude_message(MSG_INFO, "%sSCK period      : %.2f us\n", p,
	    (float) 1000000 / avrispmkIIfreqs[sck_duration]);
    break;

  case PGMTYPE_JTAGICE3:
    {
      unsigned char cmd[4];

      cmd[0] = CMD_GET_SCK;
      if (stk500v2_jtag3_send(pgm, cmd, 1) >= 0 &&
	  stk500v2_jtag3_recv(pgm, cmd, 4) >= 2) {
	unsigned int sck = cmd[1] | (cmd[2] << 8);
	avrdude_message(MSG_INFO, "%sSCK period      : %.2f us\n", p,
		(float)(1E6 / (1000.0 * sck)));
      }
    }
    break;

  case PGMTYPE_STK600:
    stk500v2_getparm2(pgm, PARAM2_AREF0, &varef);
    avrdude_message(MSG_INFO, "%sVaref 0         : %.2f V\n", p, varef / 100.0);
    stk500v2_getparm2(pgm, PARAM2_AREF1, &varef);
    avrdude_message(MSG_INFO, "%sVaref 1         : %.2f V\n", p, varef / 100.0);
    stk500v2_getparm2(pgm, PARAM2_SCK_DURATION, &sck_stk600);
    avrdude_message(MSG_INFO, "%sSCK period      : %.2f us\n", p,
	    (float) (sck_stk600 + 1) / 8.0);
    stk500v2_getparm2(pgm, PARAM2_CLOCK_CONF, &clock_conf);
    oct = (clock_conf & 0xf000) >> 12u;
    dac = (clock_conf & 0x0ffc) >> 2u;
    f = pow(2, (double)oct) * 2078.0 / (2 - (double)dac / 1024.0);
    f = f_to_kHz_MHz(f, &unit);
    avrdude_message(MSG_INFO, "%sOscillator      : %.3f %s\n",
            p, f, unit);
    break;

  default:
    avrdude_message(MSG_INFO, "%sSCK period      : %.1f us\n", p,
	  sck_duration * 8.0e6 / STK500V2_XTAL + 0.05);
    break;
  }

  return;
}


static void stk500v2_print_parms(PROGRAMMER * pgm)
{
  stk500v2_print_parms1(pgm, "");
}

static int stk500v2_perform_osccal(PROGRAMMER * pgm)
{
  unsigned char buf[32];
  int rv;

  buf[0] = CMD_OSCCAL;

  rv = stk500v2_command(pgm, buf, 1, sizeof(buf));
  if (rv < 0) {
    avrdude_message(MSG_INFO, "%s: stk500v2_perform_osccal(): failed\n",
            progname);
    return -1;
  }

  return 0;
}

/*
 * Wrapper functions for the JTAG ICE mkII in ISP mode.  This mode
 * uses the normal JTAG ICE mkII packet stream to communicate with the
 * ICE, but then encapsulates AVRISP mkII commands using
 * CMND_ISP_PACKET.
 */

/*
 * Open a JTAG ICE mkII in ISP mode.
 */
static int stk500v2_jtagmkII_open(PROGRAMMER * pgm, char * port)
{
  return -1;
//   union pinfo pinfo;
//   void *mycookie;
//   int rv;

//   avrdude_message(MSG_NOTICE2, "%s: stk500v2_jtagmkII_open()\n", progname);

//   /*
//    * The JTAG ICE mkII always starts with a baud rate of 19200 Bd upon
//    * attaching.  If the config file or command-line parameters specify
//    * a higher baud rate, we switch to it later on, after establishing
//    * the connection with the ICE.
//    */
//   pinfo.baud = 19200;

//   /*
//    * If the port name starts with "usb", divert the serial routines
//    * to the USB ones.  The serial_open() function for USB overrides
//    * the meaning of the "baud" parameter to be the USB device ID to
//    * search for.
//    */
//   if (strncmp(port, "usb", 3) == 0) {
// #if defined(HAVE_LIBUSB)
//     serdev = &usb_serdev;
//     pinfo.usbinfo.vid = USB_VENDOR_ATMEL;
//     pinfo.usbinfo.flags = 0;
//     pinfo.usbinfo.pid = USB_DEVICE_JTAGICEMKII;
//     pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_MKII;
//     pgm->fd.usb.rep = USBDEV_BULK_EP_READ_MKII;
//     pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_MKII;
//     pgm->fd.usb.eep = 0;           /* no seperate EP for events */
// #else
//     avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
//     return -1;
// #endif
//   }

//   strcpy(pgm->port, port);
//   if (serial_open(port, pinfo, &pgm->fd)==-1) {
//     return -1;
//   }

//   /*
//    * drain any extraneous input
//    */
//   stk500v2_drain(pgm, 0);

//   mycookie = pgm->cookie;
//   pgm->cookie = PDATA(pgm)->chained_pdata;
//   if ((rv = jtagmkII_getsync(pgm, EMULATOR_MODE_SPI)) != 0) {
//     if (rv != JTAGII_GETSYNC_FAIL_GRACEFUL)
//         avrdude_message(MSG_INFO, "%s: failed to sync with the JTAG ICE mkII in ISP mode\n",
//                         progname);
//     pgm->cookie = mycookie;
//     return -1;
//   }
//   pgm->cookie = mycookie;

//   PDATA(pgm)->pgmtype = PGMTYPE_JTAGICE_MKII;

//   if (pgm->bitclock != 0.0) {
//     if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
//       return -1;
//   }

//   return 0;
}


/*
 * Close an AVR Dragon or JTAG ICE mkII in ISP/HVSP/PP mode.
 */
static void stk500v2_jtagmkII_close(PROGRAMMER * pgm)
{
  // void *mycookie;

  // avrdude_message(MSG_NOTICE2, "%s: stk500v2_jtagmkII_close()\n", progname);

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // jtagmkII_close(pgm);
  // pgm->cookie = mycookie;
}


/*
 * Close JTAGICE3.
 */
static void stk500v2_jtag3_close(PROGRAMMER * pgm)
{
  // void *mycookie;

  // avrdude_message(MSG_NOTICE2, "%s: stk500v2_jtag3_close()\n", progname);

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // jtag3_close(pgm);
  // pgm->cookie = mycookie;
}


/*
 * Wrapper functions for the AVR Dragon in ISP mode.  This mode
 * uses the normal JTAG ICE mkII packet stream to communicate with the
 * ICE, but then encapsulates AVRISP mkII commands using
 * CMND_ISP_PACKET.
 */

/*
 * Open an AVR Dragon in ISP mode.
 */
static int stk500v2_dragon_isp_open(PROGRAMMER * pgm, char * port)
{
  return -1;
//   union pinfo pinfo;
//   void *mycookie;

//   avrdude_message(MSG_NOTICE2, "%s: stk500v2_dragon_isp_open()\n", progname);

//   /*
//    * The JTAG ICE mkII always starts with a baud rate of 19200 Bd upon
//    * attaching.  If the config file or command-line parameters specify
//    * a higher baud rate, we switch to it later on, after establishing
//    * the connection with the ICE.
//    */
//   pinfo.baud = 19200;

//   /*
//    * If the port name starts with "usb", divert the serial routines
//    * to the USB ones.  The serial_open() function for USB overrides
//    * the meaning of the "baud" parameter to be the USB device ID to
//    * search for.
//    */
//   if (strncmp(port, "usb", 3) == 0) {
// #if defined(HAVE_LIBUSB)
//     serdev = &usb_serdev;
//     pinfo.usbinfo.vid = USB_VENDOR_ATMEL;
//     pinfo.usbinfo.flags = 0;
//     pinfo.usbinfo.pid = USB_DEVICE_AVRDRAGON;
//     pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_MKII;
//     pgm->fd.usb.rep = USBDEV_BULK_EP_READ_MKII;
//     pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_MKII;
//     pgm->fd.usb.eep = 0;           /* no seperate EP for events */
// #else
//     avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
//     return -1;
// #endif
//   }

//   strcpy(pgm->port, port);
//   if (serial_open(port, pinfo, &pgm->fd)==-1) {
//     return -1;
//   }

//   /*
//    * drain any extraneous input
//    */
//   stk500v2_drain(pgm, 0);

//   mycookie = pgm->cookie;
//   pgm->cookie = PDATA(pgm)->chained_pdata;
//   if (jtagmkII_getsync(pgm, EMULATOR_MODE_SPI) != 0) {
//     avrdude_message(MSG_INFO, "%s: failed to sync with the AVR Dragon in ISP mode\n",
//             progname);
//     pgm->cookie = mycookie;
//     return -1;
//   }
//   pgm->cookie = mycookie;

//   PDATA(pgm)->pgmtype = PGMTYPE_JTAGICE_MKII;

//   if (pgm->bitclock != 0.0) {
//     if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
//       return -1;
//   }

//   return 0;
}


/*
 * Wrapper functions for the AVR Dragon in HV mode.  This mode
 * uses the normal JTAG ICE mkII packet stream to communicate with the
 * ICE, but then encapsulates AVRISP mkII commands using
 * CMND_ISP_PACKET.
 */

/*
 * Open an AVR Dragon in HV mode (HVSP or parallel).
 */
static int stk500v2_dragon_hv_open(PROGRAMMER * pgm, char * port)
{
  return -1;
//   union pinfo pinfo;
//   void *mycookie;

//   avrdude_message(MSG_NOTICE2, "%s: stk500v2_dragon_hv_open()\n", progname);

//   /*
//    * The JTAG ICE mkII always starts with a baud rate of 19200 Bd upon
//    * attaching.  If the config file or command-line parameters specify
//    * a higher baud rate, we switch to it later on, after establishing
//    * the connection with the ICE.
//    */
//   pinfo.baud = 19200;

//   /*
//    * If the port name starts with "usb", divert the serial routines
//    * to the USB ones.  The serial_open() function for USB overrides
//    * the meaning of the "baud" parameter to be the USB device ID to
//    * search for.
//    */
//   if (strncmp(port, "usb", 3) == 0) {
// #if defined(HAVE_LIBUSB)
//     serdev = &usb_serdev;
//     pinfo.usbinfo.vid = USB_VENDOR_ATMEL;
//     pinfo.usbinfo.flags = 0;
//     pinfo.usbinfo.pid = USB_DEVICE_AVRDRAGON;
//     pgm->fd.usb.max_xfer = USBDEV_MAX_XFER_MKII;
//     pgm->fd.usb.rep = USBDEV_BULK_EP_READ_MKII;
//     pgm->fd.usb.wep = USBDEV_BULK_EP_WRITE_MKII;
//     pgm->fd.usb.eep = 0;           /* no seperate EP for events */
// #else
//     avrdude_message(MSG_INFO, "avrdude was compiled without usb support.\n");
//     return -1;
// #endif
//   }

//   strcpy(pgm->port, port);
//   if (serial_open(port, pinfo, &pgm->fd)==-1) {
//     return -1;
//   }

//   /*
//    * drain any extraneous input
//    */
//   stk500v2_drain(pgm, 0);

//   mycookie = pgm->cookie;
//   pgm->cookie = PDATA(pgm)->chained_pdata;
//   if (jtagmkII_getsync(pgm, EMULATOR_MODE_HV) != 0) {
//     avrdude_message(MSG_INFO, "%s: failed to sync with the AVR Dragon in HV mode\n",
//             progname);
//     pgm->cookie = mycookie;
//     return -1;
//   }
//   pgm->cookie = mycookie;

//   PDATA(pgm)->pgmtype = PGMTYPE_JTAGICE_MKII;

//   if (pgm->bitclock != 0.0) {
//     if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
//       return -1;
//   }

//   return 0;
}

/*
 * Wrapper functions for the JTAGICE3 in ISP mode.  This mode
 * uses the normal JTAGICE3 packet stream to communicate with the
 * ICE, but then encapsulates AVRISP mkII commands using
 * scope AVRISP.
 */

/*
 * Open a JTAGICE3 in ISP mode.
 */
static int stk500v2_jtag3_open(PROGRAMMER * pgm, char * port)
{
  return -1;
  // void *mycookie;
  // int rv;

  // avrdude_message(MSG_NOTICE2, "%s: stk500v2_jtag3_open()\n", progname);

  // if (jtag3_open_common(pgm, port) < 0)
  //   return -1;

  // mycookie = pgm->cookie;
  // pgm->cookie = PDATA(pgm)->chained_pdata;
  // if ((rv = jtag3_getsync(pgm, 42)) != 0) {
  //   if (rv != JTAGII_GETSYNC_FAIL_GRACEFUL)
  //       avrdude_message(MSG_INFO, "%s: failed to sync with the JTAGICE3 in ISP mode\n",
  //                       progname);
  //   pgm->cookie = mycookie;
  //   return -1;
  // }
  // pgm->cookie = mycookie;

  // PDATA(pgm)->pgmtype = PGMTYPE_JTAGICE3;

  // if (pgm->bitclock != 0.0) {
  //   if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
  //     return -1;
  // }

  // return 0;
}


/*
 * XPROG wrapper
 */
static int stk600_xprog_command(PROGRAMMER * pgm, unsigned char *b,
                                unsigned int cmdsize, unsigned int responsesize)
{
    unsigned char *newb;
    unsigned int s;
    int rv;

    if (cmdsize < responsesize)
        s = responsesize;
    else
        s = cmdsize;

    if ((newb = malloc(s + 1)) == 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_cmd(): out of memory\n",
                progname);
        return -1;
    }

    newb[0] = CMD_XPROG;
    memcpy(newb + 1, b, cmdsize);
    rv = stk500v2_command(pgm, newb, cmdsize + 1, responsesize + 1);
    if (rv == 0) {
        memcpy(b, newb + 1, responsesize);
    }

    free(newb);

    return rv;
}


/*
 * issue the 'program enable' command to the AVR device, XPROG version
 */
static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
    unsigned char buf[16];
    unsigned int eepagesize = 42;
    unsigned int nvm_base;
    AVRMEM *mem = NULL;
    int use_tpi;

    use_tpi = (p->flags & AVRPART_HAS_TPI) != 0;

    if (!use_tpi) {
        if (p->nvm_base == 0) {
            avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): no nvm_base parameter for PDI device\n",
                            progname);
            return -1;
        }
        if ((mem = avr_locate_mem(p, "eeprom")) != NULL) {
            if (mem->page_size == 0) {
                avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): no EEPROM page_size parameter for PDI device\n",
                                progname);
                return -1;
            }
            eepagesize = mem->page_size;
        }
    }

    buf[0] = CMD_XPROG_SETMODE;
    buf[1] = use_tpi? XPRG_MODE_TPI: XPRG_MODE_PDI;
    if (stk500v2_command(pgm, buf, 2, sizeof(buf)) < 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): CMD_XPROG_SETMODE(XPRG_MODE_%s) failed\n",
                        progname, use_tpi? "TPI": "PDI");
        return -1;
    }

    buf[0] = XPRG_CMD_ENTER_PROGMODE;
    if (stk600_xprog_command(pgm, buf, 1, 2) < 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): XPRG_CMD_ENTER_PROGMODE failed\n",
                        progname);
        return -1;
    }

    if (use_tpi) {
        /*
         * Whatever all that might mean, it matches what AVR Studio
         * does.
         */
        if (stk500v2_setparm_real(pgm, PARAM_DISCHARGEDELAY, 232) < 0)
            return -1;

        buf[0] = XPRG_CMD_SET_PARAM;
        buf[1] = XPRG_PARAM_TPI_3;
        buf[2] = 51;
        if (stk600_xprog_command(pgm, buf, 3, 2) < 0) {
            avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_TPI_3) failed\n",
                            progname);
            return -1;
        }

        buf[0] = XPRG_CMD_SET_PARAM;
        buf[1] = XPRG_PARAM_TPI_4;
        buf[2] = 50;
        if (stk600_xprog_command(pgm, buf, 3, 2) < 0) {
            avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_TPI_4) failed\n",
                            progname);
            return -1;
        }
    } else {
        buf[0] = XPRG_CMD_SET_PARAM;
        buf[1] = XPRG_PARAM_NVMBASE;
        nvm_base = p->nvm_base;
        /*
         * The 0x01000000 appears to be an indication to the programmer
         * that the respective address is located in IO (i.e., SRAM)
         * memory address space rather than flash.  This is not documented
         * anywhere in AVR079 but matches what AVR Studio does.
         */
        nvm_base |= 0x01000000;
        buf[2] = nvm_base >> 24;
        buf[3] = nvm_base >> 16;
        buf[4] = nvm_base >> 8;
        buf[5] = nvm_base;
        if (stk600_xprog_command(pgm, buf, 6, 2) < 0) {
            avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_NVMBASE) failed\n",
                            progname);
            return -1;
        }

        if (mem != NULL) {
            buf[0] = XPRG_CMD_SET_PARAM;
            buf[1] = XPRG_PARAM_EEPPAGESIZE;
            buf[2] = eepagesize >> 8;
            buf[3] = eepagesize;
            if (stk600_xprog_command(pgm, buf, 4, 2) < 0) {
                avrdude_message(MSG_INFO, "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_EEPPAGESIZE) failed\n",
                                progname);
                return -1;
            }
        }
    }

    return 0;
}

static unsigned char stk600_xprog_memtype(PROGRAMMER * pgm, unsigned long addr)
{
    if (addr >= PDATA(pgm)->boot_start)
        return XPRG_MEM_TYPE_BOOT;
    else
        return XPRG_MEM_TYPE_APPL;
}


static void stk600_xprog_disable(PROGRAMMER * pgm)
{
    unsigned char buf[2];

    buf[0] = XPRG_CMD_LEAVE_PROGMODE;
    if (stk600_xprog_command(pgm, buf, 1, 2) < 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_program_disable(): XPRG_CMD_LEAVE_PROGMODE failed\n",
                        progname);
    }
}

static int stk600_xprog_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
				   unsigned long addr, unsigned char data)
{
    unsigned char b[9 + 256];
    int need_erase = 0;
    unsigned char write_size = 1;
    unsigned char memcode;

    memset(b, 0, sizeof(b));

    if (strcmp(mem->desc, "flash") == 0) {
        memcode = stk600_xprog_memtype(pgm, addr);
    } else if (strcmp(mem->desc, "application") == 0 ||
               strcmp(mem->desc, "apptable") == 0) {
        memcode = XPRG_MEM_TYPE_APPL;
    } else if (strcmp(mem->desc, "boot") == 0) {
        memcode = XPRG_MEM_TYPE_BOOT;
    } else if (strcmp(mem->desc, "eeprom") == 0) {
        memcode = XPRG_MEM_TYPE_EEPROM;
    } else if (strncmp(mem->desc, "lock", strlen("lock")) == 0) {
        memcode = XPRG_MEM_TYPE_LOCKBITS;
    } else if (strncmp(mem->desc, "fuse", strlen("fuse")) == 0) {
        memcode = XPRG_MEM_TYPE_FUSE;
        if (p->flags & AVRPART_HAS_TPI)
            /*
             * TPI devices need a mystic erase prior to writing their
             * fuses.
             */
            need_erase = 1;
    } else if (strcmp(mem->desc, "usersig") == 0) {
        memcode = XPRG_MEM_TYPE_USERSIG;
    } else {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_write_byte(): unknown memory \"%s\"\n",
                        progname, mem->desc);
        return -1;
    }
    addr += mem->offset;

    if (need_erase) {
        b[0] = XPRG_CMD_ERASE;
        b[1] = XPRG_ERASE_CONFIG;
        b[2] = mem->offset >> 24;
        b[3] = mem->offset >> 16;
        b[4] = mem->offset >> 8;
        b[5] = mem->offset + 1;
        if (stk600_xprog_command(pgm, b, 6, 2) < 0) {
	    avrdude_message(MSG_INFO, "%s: stk600_xprog_chip_erase(): XPRG_CMD_ERASE(XPRG_ERASE_CONFIG) failed\n",
                            progname);
	    return -1;
	}
    }

    if (p->flags & AVRPART_HAS_TPI) {
        /*
         * Some TPI memories (configuration aka. fuse) require a
         * larger write block size.  We record that as a blocksize in
         * avrdude.conf.
         */
        if (mem->blocksize != 0)
            write_size = mem->blocksize;
    }

    b[0] = XPRG_CMD_WRITE_MEM;
    b[1] = memcode;
    b[2] = 0;			/* pagemode: non-paged write */
    b[3] = (char)(addr >> 24);
    b[4] = (char)(addr >> 16);
    b[5] = (char)(addr >> 8);
    b[6] = (char)addr;
    b[7] = 0;
    b[8] = write_size;
    b[9] = data;
    if (stk600_xprog_command(pgm, b, 9 + write_size, 2) < 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_write_byte(): XPRG_CMD_WRITE_MEM failed\n",
                        progname);
        return -1;
    }
    return 0;
}


static int stk600_xprog_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                                  unsigned long addr, unsigned char * value)
{
    unsigned char b[8];

    if (strcmp(mem->desc, "flash") == 0) {
        b[1] = stk600_xprog_memtype(pgm, addr);
    } else if (strcmp(mem->desc, "application") == 0 ||
               strcmp(mem->desc, "apptable") == 0) {
        b[1] = XPRG_MEM_TYPE_APPL;
    } else if (strcmp(mem->desc, "boot") == 0) {
        b[1] = XPRG_MEM_TYPE_BOOT;
    } else if (strcmp(mem->desc, "eeprom") == 0) {
        b[1] = XPRG_MEM_TYPE_EEPROM;
    } else if (strcmp(mem->desc, "signature") == 0) {
        b[1] = XPRG_MEM_TYPE_APPL;
    } else if (strncmp(mem->desc, "fuse", strlen("fuse")) == 0) {
        b[1] = XPRG_MEM_TYPE_FUSE;
    } else if (strncmp(mem->desc, "lock", strlen("lock")) == 0) {
        b[1] = XPRG_MEM_TYPE_LOCKBITS;
    } else if (strcmp(mem->desc, "calibration") == 0) {
        b[1] = XPRG_MEM_TYPE_FACTORY_CALIBRATION;
    } else if (strcmp(mem->desc, "usersig") == 0) {
        b[1] = XPRG_MEM_TYPE_USERSIG;
    } else {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_read_byte(): unknown memory \"%s\"\n",
                        progname, mem->desc);
        return -1;
    }
    addr += mem->offset;

    b[0] = XPRG_CMD_READ_MEM;
    b[2] = (char)(addr >> 24);
    b[3] = (char)(addr >> 16);
    b[4] = (char)(addr >> 8);
    b[5] = (char)addr;
    b[6] = 0;
    b[7] = 1;
    if (stk600_xprog_command(pgm, b, 8, 3) < 0) {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_read_byte(): XPRG_CMD_READ_MEM failed\n",
                        progname);
        return -1;
    }
    *value = b[2];
    return 0;
}


static int stk600_xprog_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                                   unsigned int page_size,
                                   unsigned int addr, unsigned int n_bytes)
{
    unsigned char *b;
    unsigned int offset;
    unsigned char memtype;
    int n_bytes_orig = n_bytes, dynamic_memtype = 0;
    unsigned long use_ext_addr = 0;

    /*
     * The XPROG read command supports at most 256 bytes in one
     * transfer.
     */
    if (page_size > 256)
	page_size = 256;	/* not really a page size anymore */

    /*
     * Fancy offsets everywhere.
     * This is probably what AVR079 means when writing about the
     * "TIF address space".
     */
    if (strcmp(mem->desc, "flash") == 0) {
        memtype = 0;
        dynamic_memtype = 1;
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "application") == 0 ||
               strcmp(mem->desc, "apptable") == 0) {
        memtype = XPRG_MEM_TYPE_APPL;
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "boot") == 0) {
        memtype = XPRG_MEM_TYPE_BOOT;
        // Do we have to consider the total amount of flash
        // instead to decide whether to use extended addressing?
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "eeprom") == 0) {
        memtype = XPRG_MEM_TYPE_EEPROM;
    } else if (strcmp(mem->desc, "signature") == 0) {
        memtype = XPRG_MEM_TYPE_APPL;
    } else if (strncmp(mem->desc, "fuse", strlen("fuse")) == 0) {
        memtype = XPRG_MEM_TYPE_FUSE;
    } else if (strncmp(mem->desc, "lock", strlen("lock")) == 0) {
        memtype = XPRG_MEM_TYPE_LOCKBITS;
    } else if (strcmp(mem->desc, "calibration") == 0) {
        memtype = XPRG_MEM_TYPE_FACTORY_CALIBRATION;
    } else if (strcmp(mem->desc, "usersig") == 0) {
        memtype = XPRG_MEM_TYPE_USERSIG;
    } else {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_load(): unknown paged memory \"%s\"\n",
                        progname, mem->desc);
        return -1;
    }
    offset = addr;
    addr += mem->offset;

    if ((b = malloc(page_size + 2)) == NULL) {
	avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_load(): out of memory\n",
                        progname);
        return -1;
    }

    if (stk500v2_loadaddr(pgm, use_ext_addr) < 0) {
        free(b);
        return -1;
    }

    while (n_bytes != 0) {
	if (dynamic_memtype)
	    memtype = stk600_xprog_memtype(pgm, addr - mem->offset);

	b[0] = XPRG_CMD_READ_MEM;
	b[1] = memtype;
	b[2] = addr >> 24;
	b[3] = addr >> 16;
	b[4] = addr >> 8;
	b[5] = addr;
	b[6] = page_size >> 8;
	b[7] = page_size;
	if (stk600_xprog_command(pgm, b, 8, page_size + 2) < 0) {
	    avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_load(): XPRG_CMD_READ_MEM failed\n",
                            progname);
	    free(b);
	    return -1;
	}
	memcpy(mem->buf + offset, b + 2, page_size);
	if (n_bytes < page_size) {
	    n_bytes = page_size;
	}
	offset += page_size;
	addr += page_size;
	n_bytes -= page_size;
    }
    free(b);

    return n_bytes_orig;
}

static int stk600_xprog_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                                    unsigned int page_size,
                                    unsigned int addr, unsigned int n_bytes)
{
    unsigned char *b;
    unsigned int offset;
    unsigned char memtype;
    int n_bytes_orig = n_bytes, dynamic_memtype = 0;
    size_t writesize;
    unsigned long use_ext_addr = 0;
    unsigned char writemode;

    /*
     * The XPROG read command supports at most 256 bytes in one
     * transfer.
     */
    if (page_size > 512) {
	avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): cannot handle page size > 512\n",
                        progname);
	return -1;
    }

    /*
     * Fancy offsets everywhere.
     * This is probably what AVR079 means when writing about the
     * "TIF address space".
     */
    if (strcmp(mem->desc, "flash") == 0) {
        memtype = 0;
        dynamic_memtype = 1;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "application") == 0 ||
               strcmp(mem->desc, "apptable") == 0) {
        memtype = XPRG_MEM_TYPE_APPL;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "boot") == 0) {
        memtype = XPRG_MEM_TYPE_BOOT;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
        // Do we have to consider the total amount of flash
        // instead to decide whether to use extended addressing?
        if (mem->size > 64 * 1024)
            use_ext_addr = (1UL << 31);
    } else if (strcmp(mem->desc, "eeprom") == 0) {
        memtype = XPRG_MEM_TYPE_EEPROM;
        writemode = (1 << XPRG_MEM_WRITE_WRITE) | (1 << XPRG_MEM_WRITE_ERASE);
    } else if (strcmp(mem->desc, "signature") == 0) {
        memtype = XPRG_MEM_TYPE_APPL;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
    } else if (strncmp(mem->desc, "fuse", strlen("fuse")) == 0) {
        memtype = XPRG_MEM_TYPE_FUSE;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
    } else if (strncmp(mem->desc, "lock", strlen("lock")) == 0) {
        memtype = XPRG_MEM_TYPE_LOCKBITS;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
    } else if (strcmp(mem->desc, "calibration") == 0) {
        memtype = XPRG_MEM_TYPE_FACTORY_CALIBRATION;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
    } else if (strcmp(mem->desc, "usersig") == 0) {
        memtype = XPRG_MEM_TYPE_USERSIG;
        writemode = (1 << XPRG_MEM_WRITE_WRITE);
    } else {
        avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): unknown paged memory \"%s\"\n",
                        progname, mem->desc);
        return -1;
    }
    offset = addr;
    addr += mem->offset;

    if ((b = malloc(page_size + 9)) == NULL) {
	avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): out of memory\n",
                        progname);
        return -1;
    }

    if (stk500v2_loadaddr(pgm, use_ext_addr) < 0) {
        free(b);
        return -1;
    }

    while (n_bytes != 0) {

	if (dynamic_memtype)
	    memtype = stk600_xprog_memtype(pgm, addr - mem->offset);

	if (page_size > 256) {
	    /*
	     * AVR079 is not quite clear.  While it suggests that
	     * downloading up to 512 bytes (256 words) were OK, it
	     * obviously isn't -- 512-byte pages on the ATxmega128A1
	     * are getting corrupted when written as a single piece.
	     * It writes random junk somewhere beyond byte 256.
	     * Splitting it into 256 byte chunks, and only setting the
	     * erase page / write page bits in the final chunk helps.
	     */
	    if (page_size % 256 != 0) {
		avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): page size not multiple of 256\n",
                                progname);
		free(b);
		return -1;
	    }
	    unsigned int chunk;
	    for (chunk = 0; chunk < page_size; chunk += 256) {
                if (n_bytes < 256) {
                    memset(b + 9 + n_bytes, 0xff, 256 - n_bytes);
                    writesize = n_bytes;
                } else {
                    writesize = 256;
                }
		b[0] = XPRG_CMD_WRITE_MEM;
		b[1] = memtype;
		b[2] = writemode;
		b[3] = addr >> 24;
		b[4] = addr >> 16;
		b[5] = addr >> 8;
		b[6] = addr;
		b[7] = 1;
		b[8] = 0;
		memcpy(b + 9, mem->buf + offset, writesize);
		if (stk600_xprog_command(pgm, b, 256 + 9, 2) < 0) {
		    avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): XPRG_CMD_WRITE_MEM failed\n",
                                    progname);
		    free(b);
		    return -1;
		}
		if (n_bytes < 256)
		    n_bytes = 256;

		offset += 256;
		addr += 256;
		n_bytes -= 256;
	    }
	} else {
	    if (n_bytes < page_size) {
		/*
		 * This can easily happen if the input file was not a
		 * multiple of the page size.
		 */
		memset(b + 9 + n_bytes, 0xff, page_size - n_bytes);
                writesize = n_bytes;
            } else {
                writesize = page_size;
            }
	    b[0] = XPRG_CMD_WRITE_MEM;
	    b[1] = memtype;
	    b[2] = writemode;
	    b[3] = addr >> 24;
	    b[4] = addr >> 16;
	    b[5] = addr >> 8;
	    b[6] = addr;
	    b[7] = page_size >> 8;
	    b[8] = page_size;
	    memcpy(b + 9, mem->buf + offset, writesize);
	    if (stk600_xprog_command(pgm, b, page_size + 9, 2) < 0) {
		avrdude_message(MSG_INFO, "%s: stk600_xprog_paged_write(): XPRG_CMD_WRITE_MEM failed\n",
                                progname);
		free(b);
		return -1;
	    }
	    if (n_bytes < page_size)
		n_bytes = page_size;

	    offset += page_size;
	    addr += page_size;
	    n_bytes -= page_size;
	}
    }
    free(b);

    return n_bytes_orig;
}

static int stk600_xprog_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
    unsigned char b[6];
    AVRMEM *mem;
    unsigned int addr = 0;

    if (p->flags & AVRPART_HAS_TPI) {
        if ((mem = avr_locate_mem(p, "flash")) == NULL) {
            avrdude_message(MSG_INFO, "%s: stk600_xprog_chip_erase(): no FLASH definition found for TPI device\n",
                            progname);
            return -1;
        }
        addr = mem->offset + 1;
    }

    b[0] = XPRG_CMD_ERASE;
    b[1] = XPRG_ERASE_CHIP;
    b[2] = addr >> 24;
    b[3] = addr >> 16;
    b[4] = addr >> 8;
    b[5] = addr;
    if (stk600_xprog_command(pgm, b, 6, 2) < 0) {
	    avrdude_message(MSG_INFO, "%s: stk600_xprog_chip_erase(): XPRG_CMD_ERASE(XPRG_ERASE_CHIP) failed\n",
                            progname);
	    return -1;
	}
    return 0;
}

static int stk600_xprog_page_erase(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                   unsigned int addr)
{
    unsigned char b[6];

    if (strcmp(m->desc, "flash") == 0) {
      b[1] = stk600_xprog_memtype(pgm, addr) == XPRG_MEM_TYPE_APPL?
        XPRG_ERASE_APP_PAGE: XPRG_ERASE_BOOT_PAGE;
    } else if (strcmp(m->desc, "application") == 0 ||
               strcmp(m->desc, "apptable") == 0) {
      b[1] = XPRG_ERASE_APP_PAGE;
    } else if (strcmp(m->desc, "boot") == 0) {
      b[1] = XPRG_ERASE_BOOT_PAGE;
    } else if (strcmp(m->desc, "eeprom") == 0) {
      b[1] = XPRG_ERASE_EEPROM_PAGE;
    } else if (strcmp(m->desc, "usersig") == 0) {
      b[1] = XPRG_ERASE_USERSIG;
    } else {
      avrdude_message(MSG_INFO, "%s: stk600_xprog_page_erase(): unknown paged memory \"%s\"\n",
                      progname, m->desc);
      return -1;
    }
    addr += m->offset;
    b[0] = XPRG_CMD_ERASE;
    b[2] = addr >> 24;
    b[3] = addr >> 16;
    b[4] = addr >> 8;
    b[5] = addr;
    if (stk600_xprog_command(pgm, b, 6, 2) < 0) {
	    avrdude_message(MSG_INFO, "%s: stk600_xprog_page_erase(): XPRG_CMD_ERASE(%d) failed\n",
                            progname, b[1]);
	    return -1;
	}
    return 0;
}

/*
 * Modify pgm's methods for XPROG operation.
 */
static void stk600_setup_xprog(PROGRAMMER * pgm)
{
    pgm->program_enable = stk600_xprog_program_enable;
    pgm->disable = stk600_xprog_disable;
    pgm->read_byte = stk600_xprog_read_byte;
    pgm->write_byte = stk600_xprog_write_byte;
    pgm->paged_load = stk600_xprog_paged_load;
    pgm->paged_write = stk600_xprog_paged_write;
    pgm->page_erase = stk600_xprog_page_erase;
    pgm->chip_erase = stk600_xprog_chip_erase;
}


/*
 * Modify pgm's methods for ISP operation.
 */
static void stk600_setup_isp(PROGRAMMER * pgm)
{
    pgm->program_enable = stk500v2_program_enable;
    pgm->disable = stk500v2_disable;
    pgm->read_byte = stk500isp_read_byte;
    pgm->write_byte = stk500isp_write_byte;
    pgm->paged_load = stk500v2_paged_load;
    pgm->paged_write = stk500v2_paged_write;
    pgm->page_erase = stk500v2_page_erase;
    pgm->chip_erase = stk500v2_chip_erase;
}

const char stk500v2_desc[] = "Atmel STK500 Version 2.x firmware";

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;
  pgm->read_byte      = stk500isp_read_byte;
  pgm->write_byte     = stk500isp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500v2_paged_write;
  pgm->paged_load     = stk500v2_paged_load;
  pgm->page_erase     = stk500v2_page_erase;
  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->perform_osccal = stk500v2_perform_osccal;
  pgm->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
  pgm->set_upload_size= stk500v2_set_upload_size;
}

const char stk500pp_desc[] = "Atmel STK500 V2 in parallel programming mode";

void stk500pp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "STK500PP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500pp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500pp_disable;
  pgm->program_enable = stk500pp_program_enable;
  pgm->chip_erase     = stk500pp_chip_erase;
  pgm->open           = stk500v2_open;
  pgm->close          = stk500v2_close;
  pgm->read_byte      = stk500pp_read_byte;
  pgm->write_byte     = stk500pp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500pp_paged_write;
  pgm->paged_load     = stk500pp_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->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
}

const char stk500hvsp_desc[] = "Atmel STK500 V2 in high-voltage serial programming mode";

void stk500hvsp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "STK500HVSP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500hvsp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500hvsp_disable;
  pgm->program_enable = stk500hvsp_program_enable;
  pgm->chip_erase     = stk500hvsp_chip_erase;
  pgm->open           = stk500v2_open;
  pgm->close          = stk500v2_close;
  pgm->read_byte      = stk500hvsp_read_byte;
  pgm->write_byte     = stk500hvsp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500hvsp_paged_write;
  pgm->paged_load     = stk500hvsp_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->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
}

const char stk500v2_jtagmkII_desc[] = "Atmel JTAG ICE mkII in ISP mode";

void stk500v2_jtagmkII_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "JTAGMKII_ISP");

  /*
   * 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_jtagmkII_open;
  pgm->close          = stk500v2_jtagmkII_close;
  pgm->read_byte      = stk500isp_read_byte;
  pgm->write_byte     = stk500isp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500v2_paged_write;
  pgm->paged_load     = stk500v2_paged_load;
  pgm->page_erase     = stk500v2_page_erase;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_sck_period = stk500v2_set_sck_period_mk2;
  pgm->perform_osccal = stk500v2_perform_osccal;
  pgm->setup          = stk500v2_jtagmkII_setup;
  pgm->teardown       = stk500v2_jtagmkII_teardown;
  pgm->page_size      = 256;
}

const char stk500v2_dragon_isp_desc[] = "Atmel AVR Dragon in ISP mode";

void stk500v2_dragon_isp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "DRAGON_ISP");

  /*
   * 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_dragon_isp_open;
  pgm->close          = stk500v2_jtagmkII_close;
  pgm->read_byte      = stk500isp_read_byte;
  pgm->write_byte     = stk500isp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500v2_paged_write;
  pgm->paged_load     = stk500v2_paged_load;
  pgm->page_erase     = stk500v2_page_erase;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_sck_period = stk500v2_set_sck_period_mk2;
  pgm->setup          = stk500v2_jtagmkII_setup;
  pgm->teardown       = stk500v2_jtagmkII_teardown;
  pgm->page_size      = 256;
}

const char stk500v2_dragon_pp_desc[] = "Atmel AVR Dragon in PP mode";

void stk500v2_dragon_pp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "DRAGON_PP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500pp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500pp_disable;
  pgm->program_enable = stk500pp_program_enable;
  pgm->chip_erase     = stk500pp_chip_erase;
  pgm->open           = stk500v2_dragon_hv_open;
  pgm->close          = stk500v2_jtagmkII_close;
  pgm->read_byte      = stk500pp_read_byte;
  pgm->write_byte     = stk500pp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500pp_paged_write;
  pgm->paged_load     = stk500pp_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_mk2;
  pgm->setup          = stk500v2_jtagmkII_setup;
  pgm->teardown       = stk500v2_jtagmkII_teardown;
  pgm->page_size      = 256;
}

const char stk500v2_dragon_hvsp_desc[] = "Atmel AVR Dragon in HVSP mode";

void stk500v2_dragon_hvsp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "DRAGON_HVSP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500hvsp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500hvsp_disable;
  pgm->program_enable = stk500hvsp_program_enable;
  pgm->chip_erase     = stk500hvsp_chip_erase;
  pgm->open           = stk500v2_dragon_hv_open;
  pgm->close          = stk500v2_jtagmkII_close;
  pgm->read_byte      = stk500hvsp_read_byte;
  pgm->write_byte     = stk500hvsp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500hvsp_paged_write;
  pgm->paged_load     = stk500hvsp_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_mk2;
  pgm->setup          = stk500v2_jtagmkII_setup;
  pgm->teardown       = stk500v2_jtagmkII_teardown;
  pgm->page_size      = 256;
}

const char stk600_desc[] = "Atmel STK600";

void stk600_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "STK600");

  /*
   * 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           = stk600_open;
  pgm->close          = stk500v2_close;
  pgm->read_byte      = stk500isp_read_byte;
  pgm->write_byte     = stk500isp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500v2_paged_write;
  pgm->paged_load     = stk500v2_paged_load;
  pgm->page_erase     = stk500v2_page_erase;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_vtarget    = stk600_set_vtarget;
  pgm->set_varef      = stk600_set_varef;
  pgm->set_fosc       = stk600_set_fosc;
  pgm->set_sck_period = stk600_set_sck_period;
  pgm->perform_osccal = stk500v2_perform_osccal;
  pgm->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
}

const char stk600pp_desc[] = "Atmel STK600 in parallel programming mode";

void stk600pp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "STK600PP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500pp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500pp_disable;
  pgm->program_enable = stk500pp_program_enable;
  pgm->chip_erase     = stk500pp_chip_erase;
  pgm->open           = stk600_open;
  pgm->close          = stk500v2_close;
  pgm->read_byte      = stk500pp_read_byte;
  pgm->write_byte     = stk500pp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500pp_paged_write;
  pgm->paged_load     = stk500pp_paged_load;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_vtarget    = stk600_set_vtarget;
  pgm->set_varef      = stk600_set_varef;
  pgm->set_fosc       = stk600_set_fosc;
  pgm->set_sck_period = stk600_set_sck_period;
  pgm->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
}

const char stk600hvsp_desc[] = "Atmel STK600 in high-voltage serial programming mode";

void stk600hvsp_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "STK600HVSP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500hvsp_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500hvsp_disable;
  pgm->program_enable = stk500hvsp_program_enable;
  pgm->chip_erase     = stk500hvsp_chip_erase;
  pgm->open           = stk600_open;
  pgm->close          = stk500v2_close;
  pgm->read_byte      = stk500hvsp_read_byte;
  pgm->write_byte     = stk500hvsp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500hvsp_paged_write;
  pgm->paged_load     = stk500hvsp_paged_load;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_vtarget    = stk600_set_vtarget;
  pgm->set_varef      = stk600_set_varef;
  pgm->set_fosc       = stk600_set_fosc;
  pgm->set_sck_period = stk600_set_sck_period;
  pgm->setup          = stk500v2_setup;
  pgm->teardown       = stk500v2_teardown;
  pgm->page_size      = 256;
}

const char stk500v2_jtag3_desc[] = "Atmel JTAGICE3 in ISP mode";

void stk500v2_jtag3_initpgm(PROGRAMMER * pgm)
{
  strcpy(pgm->type, "JTAG3_ISP");

  /*
   * mandatory functions
   */
  pgm->initialize     = stk500v2_jtag3_initialize;
  pgm->display        = stk500v2_display;
  pgm->enable         = stk500v2_enable;
  pgm->disable        = stk500v2_jtag3_disable;
  pgm->program_enable = stk500v2_program_enable;
  pgm->chip_erase     = stk500v2_chip_erase;
  pgm->cmd            = stk500v2_jtag3_cmd;
  pgm->open           = stk500v2_jtag3_open;
  pgm->close          = stk500v2_jtag3_close;
  pgm->read_byte      = stk500isp_read_byte;
  pgm->write_byte     = stk500isp_write_byte;

  /*
   * optional functions
   */
  pgm->paged_write    = stk500v2_paged_write;
  pgm->paged_load     = stk500v2_paged_load;
  pgm->page_erase     = stk500v2_page_erase;
  pgm->print_parms    = stk500v2_print_parms;
  pgm->set_sck_period = stk500v2_jtag3_set_sck_period;
  pgm->perform_osccal = stk500v2_perform_osccal;
  pgm->setup          = stk500v2_jtag3_setup;
  pgm->teardown       = stk500v2_jtag3_teardown;
  pgm->page_size      = 256;
}

void stk500v2_set_upload_size(PROGRAMMER * pgm, int size)
{
	unsigned char buf[16];
	buf[0] = CMD_SET_UPLOAD_SIZE_PRUSA3D;
	buf[1] = size & 0xff;
	buf[2] = size >> 8;
	buf[3] = size >> 16;
	stk500v2_command(pgm, buf, 4, sizeof(buf));
}

