2012-08-15 18:34:53 +00:00
|
|
|
/*
|
|
|
|
* 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 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
|
2012-11-20 14:03:50 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2012-08-15 18:34:53 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* $Id: pickit2.c 2010-05-03 dbrown$ */
|
|
|
|
/* Based on Id: stk500v2.c 836 2009-07-10 22:39:37Z joerg_wunsch */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* avrdude interface for PicKit2 programmer
|
|
|
|
*
|
|
|
|
* The PicKit2 programmer is a cheap device capable
|
|
|
|
* of 2 (bidirectional data line), 3, 4 wire SPI comms
|
|
|
|
*
|
|
|
|
* The PICkit2 software license doesn't allow the source to be
|
|
|
|
* modified to program other devices - nor can we distribute
|
|
|
|
* their source code. This program is not derived from nor does it
|
|
|
|
* contain any of the pickit2 source and should be exempt from any
|
|
|
|
* licensing issues.
|
|
|
|
*
|
|
|
|
* ISP Pinout (AVR - PICKit2 (pin)):
|
|
|
|
* RST - VPP/MCLR (1)
|
|
|
|
* VDD - VDD Target (2) -- possibly optional if AVR self powered
|
|
|
|
* GND - GND (3)
|
|
|
|
* MISO - PGD (4)
|
|
|
|
* SCLK - PDC (5)
|
|
|
|
* MOSI - AUX (6)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ac_cfg.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <inttypes.h>
|
2013-09-02 20:30:09 +00:00
|
|
|
#include <unistd.h>
|
2012-08-15 18:34:53 +00:00
|
|
|
|
|
|
|
#include "avrdude.h"
|
2014-05-19 10:01:59 +00:00
|
|
|
#include "libavrdude.h"
|
2012-12-03 20:50:11 +00:00
|
|
|
|
|
|
|
#if defined(HAVE_LIBUSB) || (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
2012-08-15 18:34:53 +00:00
|
|
|
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
#include <windows.h>
|
|
|
|
#if defined(HAVE_DDK_HIDSDI_H)
|
|
|
|
# include <ddk/hidsdi.h>
|
|
|
|
#else
|
|
|
|
# include "my_ddk_hidsdi.h"
|
|
|
|
#endif
|
|
|
|
#include <setupapi.h>
|
|
|
|
#else
|
|
|
|
#if defined(HAVE_USB_H)
|
|
|
|
# include <usb.h>
|
|
|
|
#elif defined(HAVE_LUSB0_USB_H)
|
|
|
|
# include <lusb0_usb.h>
|
|
|
|
#else
|
|
|
|
# error "libusb needs either <usb.h> or <lusb0_usb.h>"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
2014-07-16 20:02:01 +00:00
|
|
|
#define DEBUG(...) do { avrdude_message(MSG_DEBUG, __VA_ARGS__); } while(0)
|
2012-08-15 18:34:53 +00:00
|
|
|
#else
|
|
|
|
#define DEBUG(...) ((void)0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
2014-07-16 20:02:01 +00:00
|
|
|
#define DEBUGRECV(...) do { avrdude_message(MSG_DEBUG, __VA_ARGS__); } while(0)
|
2012-08-15 18:34:53 +00:00
|
|
|
#else
|
|
|
|
#define DEBUGRECV(...) ((void)0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define PICKIT2_VID 0x04d8
|
|
|
|
#define PICKIT2_PID 0x0033
|
|
|
|
|
|
|
|
#define SPI_MAX_CHUNK (64 - 10) // max packet size less the command overhead
|
|
|
|
|
|
|
|
// win32native only:
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
static HANDLE open_hid(unsigned short vid, unsigned short pid);
|
|
|
|
const char *usb_strerror()
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int usb_open_device(struct usb_dev_handle **dev, int vid, int pid);
|
|
|
|
//#define INVALID_HANDLE_VALUE NULL
|
|
|
|
#define USB_ERROR_NONE 0
|
|
|
|
#define USB_ERROR_ACCESS 1
|
|
|
|
#define USB_ERROR_NOTFOUND 2
|
|
|
|
#define USB_ERROR_BUSY 16
|
|
|
|
#define USB_ERROR_IO 5
|
|
|
|
#endif // WIN32NATIVE
|
|
|
|
|
2014-07-16 20:02:01 +00:00
|
|
|
static int pickit2_write_report(PROGRAMMER *pgm, const unsigned char report[65]);
|
2012-08-15 18:34:53 +00:00
|
|
|
static int pickit2_read_report(PROGRAMMER *pgm, unsigned char report[65]);
|
|
|
|
|
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Private data for this programmer.
|
|
|
|
*/
|
|
|
|
struct pdata
|
|
|
|
{
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
HANDLE usb_handle, write_event, read_event;
|
|
|
|
#else
|
|
|
|
struct usb_dev_handle *usb_handle; // LIBUSB STUFF
|
|
|
|
#endif
|
|
|
|
uint8_t clock_period; // SPI clock period in us
|
|
|
|
int transaction_timeout; // usb trans timeout in ms
|
|
|
|
};
|
|
|
|
|
|
|
|
#define PDATA(pgm) ((struct pdata *)(pgm->cookie))
|
|
|
|
|
|
|
|
#define CMD_NOP 0x5A
|
|
|
|
#define CMD_GET_VERSION 0x76
|
|
|
|
#define CMD_SET_VDD_4(v) 0xA0, (uint8_t)((v)*2048+672), (uint8_t)(((v)*2048+672)/256), (uint8_t)((v)*36)
|
|
|
|
#define CMD_SET_VPP_4(v) 0xA1, 0x40, (uint8_t)((v)*18.61), (uint8_t)((v)*13)
|
|
|
|
#define CMD_READ_VDD_VPP 0xA3
|
|
|
|
#define CMD_EXEC_SCRIPT_2(len) 0xA6, (len)
|
|
|
|
#define CMD_CLR_DLOAD_BUFF 0xA7
|
|
|
|
#define CMD_DOWNLOAD_DATA_2(len) 0xA8, (len)
|
|
|
|
#define CMD_CLR_ULOAD_BUFF 0xA9
|
|
|
|
#define CMD_UPLOAD_DATA 0xAA
|
|
|
|
#define CMD_UPLOAD_DATA_NO_LEN 0xAC
|
|
|
|
#define CMD_END_OF_BUFFER 0xAD
|
|
|
|
|
|
|
|
#define SCR_VDD_ON 0xFF
|
|
|
|
#define SCR_VDD_OFF 0xFE
|
|
|
|
#define SCR_VPP_ON 0xFB
|
|
|
|
#define SCR_VPP_OFF 0xFA
|
|
|
|
#define SCR_VPP_PWM_ON 0xF9
|
|
|
|
#define SCR_VPP_PWM_OFF 0xF8
|
|
|
|
#define SCR_MCLR_GND_ON 0xF7
|
|
|
|
#define SCR_MCLR_GND_OFF 0xF6
|
|
|
|
#define SCR_BUSY_LED_ON 0xF5
|
|
|
|
#define SCR_BUSY_LED_OFF 0xF4
|
|
|
|
#define SCR_SET_ICSP_DELAY_2(us) 0xEA,(us)
|
|
|
|
#define SCR_SET_PINS_2(dd, cd, dv, cv) 0xF3, (((cd)!=0) | (((dd)!=0)<<1) | (((cv)!=0)<<2) | (((dv)!=0)<<3))
|
|
|
|
#define SCR_GET_PINS 0xDC
|
|
|
|
#define SCR_LOOP_3(rel, cnt) 0xE9, rel, cnt
|
|
|
|
#define SCR_DELAY_2(sec) ((sec)>0.0054528?0xE8:0xE7), (uint8_t)((sec)>0.0054528?(.999+(sec)/.00546):(.999+(sec)/.0000213))
|
|
|
|
#define SCR_SET_AUX_2(ad, av) 0xCF, (((ad)!=0) | (((av)!=0)<<1))
|
|
|
|
#define SCR_SPI_SETUP_PINS_4 SCR_SET_PINS_2(1,0,0,0), SCR_SET_AUX_2(0,0)
|
|
|
|
#define SCR_SPI 0xC3
|
|
|
|
#define SCR_SPI_LIT_2(v) 0xC7,(v)
|
|
|
|
|
|
|
|
static void pickit2_setup(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: pickit2_setup(): Out of memory allocating private data\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname);
|
2012-08-15 18:34:53 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
memset(pgm->cookie, 0, sizeof(struct pdata));
|
|
|
|
|
|
|
|
PDATA(pgm)->transaction_timeout = 1500; // default value, may be overridden with -x timeout=ms
|
|
|
|
PDATA(pgm)->clock_period = 10; // default value, may be overridden with -x clockrate=us or -B or -i
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_teardown(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
free(pgm->cookie);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_open(PROGRAMMER * pgm, char * port)
|
|
|
|
{
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
PDATA(pgm)->usb_handle = open_hid(PICKIT2_VID, PICKIT2_PID);
|
|
|
|
|
|
|
|
if (PDATA(pgm)->usb_handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
/* no PICkit2 found */
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: error: could not find PICkit2 with vid=0x%x pid=0x%x\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, PICKIT2_VID, PICKIT2_PID);
|
2014-05-16 15:52:25 +00:00
|
|
|
return -1;
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get the device description while we're at it
|
2014-07-16 20:02:01 +00:00
|
|
|
short buff[PGM_DESCLEN-1], i;
|
|
|
|
HidD_GetProductString(PDATA(pgm)->usb_handle, buff, PGM_DESCLEN-1);
|
2012-08-15 18:34:53 +00:00
|
|
|
|
2014-07-16 20:02:01 +00:00
|
|
|
// convert from wide chars, but do not overwrite trailing '\0'
|
2020-09-16 21:13:24 +00:00
|
|
|
memset(&(pgm->desc), 0, PGM_DESCLEN);
|
2014-07-16 20:02:01 +00:00
|
|
|
for (i = 0; i < (PGM_DESCLEN-1) && buff[i]; i++)
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
2020-09-16 21:13:24 +00:00
|
|
|
pgm->desc[i] = (char)buff[i]; // TODO what about little/big endian???
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (usb_open_device(&(PDATA(pgm)->usb_handle), PICKIT2_VID, PICKIT2_PID) < 0)
|
|
|
|
{
|
|
|
|
/* no PICkit2 found */
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: error: could not find PICkit2 with vid=0x%x pid=0x%x\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, PICKIT2_VID, PICKIT2_PID);
|
2014-05-16 15:52:25 +00:00
|
|
|
return -1;
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pgm->ispdelay > 0)
|
|
|
|
{
|
|
|
|
PDATA(pgm)->clock_period = MIN(pgm->ispdelay, 255);
|
|
|
|
}
|
|
|
|
else if (pgm->bitclock > 0.0)
|
|
|
|
{
|
|
|
|
PDATA(pgm)->clock_period = MIN(pgm->bitclock * 1e6, 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void pickit2_close(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
CloseHandle(PDATA(pgm)->usb_handle);
|
|
|
|
CloseHandle(PDATA(pgm)->read_event);
|
|
|
|
CloseHandle(PDATA(pgm)->write_event);
|
|
|
|
#else
|
|
|
|
usb_close(PDATA(pgm)->usb_handle);
|
|
|
|
#endif // WIN32NATIVE
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int pickit2_initialize(PROGRAMMER * pgm, AVRPART * p)
|
|
|
|
{
|
|
|
|
unsigned char temp[4];
|
|
|
|
memset(temp, 0, sizeof(temp));
|
|
|
|
|
|
|
|
int errorCode = 0;
|
|
|
|
|
|
|
|
/* set sck period */
|
|
|
|
if (pgm->set_sck_period)
|
|
|
|
pgm->set_sck_period(pgm, pgm->bitclock);
|
|
|
|
|
|
|
|
/* connect to target device -- we'll just ask for the firmware version */
|
2014-07-16 20:02:01 +00:00
|
|
|
static const unsigned char report[65] = {0, CMD_GET_VERSION, CMD_END_OF_BUFFER};
|
2012-08-15 18:34:53 +00:00
|
|
|
if ((errorCode = pickit2_write_report(pgm, report)) > 0)
|
|
|
|
{
|
2014-07-16 20:02:01 +00:00
|
|
|
unsigned char report[65] = {0};
|
|
|
|
//memset(report, 0, sizeof(report));
|
2012-08-15 18:34:53 +00:00
|
|
|
if ((errorCode = pickit2_read_report(pgm, report)) >= 4)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_NOTICE, "%s: %s firmware version %d.%d.%d\n", progname, pgm->desc, (int)report[1], (int)report[2], (int)report[3]);
|
2012-08-15 18:34:53 +00:00
|
|
|
|
|
|
|
// set the pins, apply reset,
|
|
|
|
// TO DO: apply vtarget (if requested though -x option)
|
2014-07-16 20:02:01 +00:00
|
|
|
unsigned char report[65] =
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
0, CMD_SET_VDD_4(5),
|
|
|
|
CMD_SET_VPP_4(5),
|
|
|
|
CMD_EXEC_SCRIPT_2(24),
|
|
|
|
SCR_SPI_SETUP_PINS_4, // SDO, SDI, SCK
|
|
|
|
SCR_SET_ICSP_DELAY_2(PDATA(pgm)->clock_period), // slow down the SPI
|
|
|
|
SCR_VDD_ON,
|
|
|
|
SCR_MCLR_GND_OFF, // let reset float high
|
|
|
|
SCR_VPP_PWM_ON,
|
|
|
|
SCR_DELAY_2(.1),
|
|
|
|
SCR_VPP_ON,
|
|
|
|
SCR_DELAY_2(.1),
|
|
|
|
SCR_VPP_OFF,
|
|
|
|
SCR_DELAY_2(.01),
|
|
|
|
|
|
|
|
SCR_MCLR_GND_ON, // reset low - programming mode
|
|
|
|
SCR_DELAY_2(.1),
|
|
|
|
|
|
|
|
SCR_BUSY_LED_ON,
|
|
|
|
SCR_DELAY_2(.3),
|
|
|
|
SCR_BUSY_LED_OFF,
|
|
|
|
|
|
|
|
CMD_CLR_DLOAD_BUFF,
|
|
|
|
CMD_CLR_ULOAD_BUFF,
|
|
|
|
|
|
|
|
CMD_END_OF_BUFFER
|
|
|
|
};
|
|
|
|
|
|
|
|
if (pickit2_write_report(pgm, report) < 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "pickit2_read_report failed (ec %d). %s\n", errorCode, usb_strerror());
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "pickit2_read_report failed (ec %d). %s\n", errorCode, usb_strerror());
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "pickit2_write_report failed (ec %d). %s\n", errorCode, usb_strerror());
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pgm->program_enable)
|
|
|
|
return pgm->program_enable(pgm, p);
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_disable(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
/* make sure all pins are floating & all voltages are off */
|
2014-07-16 20:02:01 +00:00
|
|
|
static const unsigned char report[65] =
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
0, CMD_EXEC_SCRIPT_2(8),
|
|
|
|
SCR_SET_PINS_2(1,1,0,0),
|
|
|
|
SCR_SET_AUX_2(1,0),
|
|
|
|
SCR_MCLR_GND_OFF,
|
|
|
|
SCR_VPP_OFF,
|
|
|
|
SCR_VDD_OFF,
|
|
|
|
SCR_BUSY_LED_OFF,
|
|
|
|
CMD_END_OF_BUFFER
|
|
|
|
};
|
|
|
|
|
|
|
|
pickit2_write_report(pgm, report);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_enable(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
/* Do nothing. */
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_display(PROGRAMMER * pgm, const char * p)
|
|
|
|
{
|
|
|
|
DEBUG( "%s: Found \"%s\" version %d.%d.%d\n", progname, p, 1, 1, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define sendReport(x)
|
|
|
|
#define readReport(x) 0
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static int pickit2_rdy_led (struct programmer_t * pgm, int value)
|
|
|
|
{
|
|
|
|
// no rdy led
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_err_led(struct programmer_t * pgm, int value)
|
|
|
|
{
|
|
|
|
// there is no error led, so just flash the busy led a few times
|
|
|
|
uint8_t report[65] =
|
|
|
|
{
|
|
|
|
0, CMD_EXEC_SCRIPT_2(9),
|
|
|
|
SCR_BUSY_LED_ON,
|
|
|
|
SCR_DELAY_2(.2),
|
|
|
|
SCR_BUSY_LED_OFF,
|
|
|
|
SCR_DELAY_2(.2),
|
|
|
|
SCR_LOOP_3(6, 9),
|
|
|
|
CMD_END_OF_BUFFER
|
|
|
|
};
|
|
|
|
|
|
|
|
// busy stops flashing by itself, so just return
|
|
|
|
if (!value)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pickit2_write_report(pgm, report) != -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int pickit2_pgm_led (struct programmer_t * pgm, int value)
|
|
|
|
{
|
|
|
|
// script to set busy led appropriately
|
|
|
|
uint8_t report[65] = {0, CMD_EXEC_SCRIPT_2(1),
|
|
|
|
value ? SCR_BUSY_LED_ON : SCR_BUSY_LED_OFF,
|
|
|
|
CMD_END_OF_BUFFER
|
|
|
|
};
|
|
|
|
|
|
|
|
return pickit2_write_report(pgm, report) != -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_vfy_led (struct programmer_t * pgm, int value)
|
|
|
|
{
|
|
|
|
// no such thing - maybe just call pgm_led
|
|
|
|
|
|
|
|
return pgm->pgm_led(pgm, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_powerup(struct programmer_t * pgm)
|
|
|
|
{
|
|
|
|
// turn vdd on?
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pickit2_powerdown(struct programmer_t * pgm)
|
|
|
|
{
|
|
|
|
// do what?
|
|
|
|
pgm->disable(pgm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_program_enable(struct programmer_t * pgm, AVRPART * p)
|
|
|
|
{
|
|
|
|
unsigned char cmd[4];
|
|
|
|
unsigned char res[4];
|
|
|
|
|
|
|
|
if (p->op[AVR_OP_PGM_ENABLE] == NULL)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "program enable instruction not defined for part \"%s\"\n",
|
2012-08-15 18:34:53 +00:00
|
|
|
p->desc);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
|
|
|
|
pgm->cmd(pgm, cmd, res);
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
2014-07-16 20:02:01 +00:00
|
|
|
avrdude_message(MSG_DEBUG, "program_enable(): sending command. Resp = ");
|
2012-08-15 18:34:53 +00:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
2014-07-16 20:02:01 +00:00
|
|
|
avrdude_message(MSG_DEBUG, "%x ", (int)res[i]);
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
2014-07-16 20:02:01 +00:00
|
|
|
avrdude_message(MSG_DEBUG, "\n");
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// check for sync character
|
|
|
|
if (res[2] != cmd[1])
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_chip_erase(struct programmer_t * pgm, AVRPART * p)
|
|
|
|
{
|
|
|
|
unsigned char cmd[4];
|
|
|
|
unsigned char res[4];
|
|
|
|
|
|
|
|
if (p->op[AVR_OP_CHIP_ERASE] == NULL)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "chip erase instruction not defined for part \"%s\"\n",
|
2012-08-15 18:34:53 +00:00
|
|
|
p->desc);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, ON);
|
|
|
|
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
|
|
|
|
avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
|
|
|
|
pgm->cmd(pgm, cmd, res);
|
|
|
|
usleep(p->chip_erase_delay);
|
|
|
|
pgm->initialize(pgm, p);
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, OFF);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
|
|
|
unsigned int page_size, unsigned int addr, unsigned int n_bytes)
|
|
|
|
{
|
|
|
|
// only supporting flash & eeprom page reads
|
|
|
|
if ((!mem->paged || page_size <= 1) || (strcmp(mem->desc, "flash") != 0 && strcmp(mem->desc, "eeprom") != 0))
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG( "paged read ps %d, mem %s\n", page_size, mem->desc);
|
|
|
|
|
|
|
|
OPCODE *readop = 0, *lext = mem->op[AVR_OP_LOAD_EXT_ADDR];
|
|
|
|
uint8_t data = 0, cmd[SPI_MAX_CHUNK], res[SPI_MAX_CHUNK];
|
|
|
|
unsigned int addr_base;
|
|
|
|
unsigned int max_addr = addr + n_bytes;
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, ON);
|
|
|
|
|
|
|
|
for (addr_base = addr; addr_base < max_addr; )
|
|
|
|
{
|
|
|
|
if ((addr_base == 0 || (addr_base % /*ext_address_boundary*/ 65536) == 0)
|
|
|
|
&& lext != NULL)
|
|
|
|
{
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
|
|
|
|
avr_set_bits(lext, cmd);
|
|
|
|
avr_set_addr(lext, cmd, addr_base);
|
|
|
|
pgm->cmd(pgm, cmd, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
// bytes to send in the next packet -- not necessary as pickit2_spi() handles breaking up
|
|
|
|
// the data into packets -- but we need to keep transfers frequent so that we can update the
|
|
|
|
// status indicator bar
|
|
|
|
uint32_t blockSize = MIN(65536 - (addr_base % 65536), MIN(max_addr - addr_base, SPI_MAX_CHUNK / 4));
|
|
|
|
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
memset(res, 0, sizeof(res));
|
|
|
|
|
|
|
|
uint8_t addr_off;
|
|
|
|
for (addr_off = 0; addr_off < blockSize; addr_off++)
|
|
|
|
{
|
|
|
|
int addr = addr_base + addr_off, caddr = addr;
|
|
|
|
|
|
|
|
if (mem->op[AVR_OP_READ_LO] != NULL && mem->op[AVR_OP_READ_HI] != NULL)
|
|
|
|
{
|
|
|
|
if (addr & 0x00000001)
|
|
|
|
readop = mem->op[AVR_OP_READ_HI];
|
|
|
|
else
|
|
|
|
readop = mem->op[AVR_OP_READ_LO];
|
|
|
|
|
|
|
|
caddr /= 2;
|
|
|
|
}
|
|
|
|
else if (mem->op[AVR_OP_READ] != NULL)
|
|
|
|
{
|
|
|
|
readop = mem->op[AVR_OP_READ];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "no read command specified\n");
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
avr_set_bits(readop, &cmd[addr_off*4]);
|
|
|
|
avr_set_addr(readop, &cmd[addr_off*4], caddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytes_read = pgm->spi(pgm, cmd, res, blockSize*4);
|
|
|
|
|
|
|
|
if (bytes_read < 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Failed @ pgm->spi()\n");
|
2012-08-15 18:34:53 +00:00
|
|
|
pgm->err_led(pgm, ON);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG( "\npaged_load @ %X, wrote: %d, read: %d bytes\n", addr_base, blockSize*4, bytes_read);
|
|
|
|
|
|
|
|
for (addr_off = 0; addr_off < bytes_read / 4; addr_off++)
|
|
|
|
{
|
|
|
|
data = 0;
|
|
|
|
avr_get_output(readop, &res[addr_off*4], &data);
|
|
|
|
mem->buf[addr_base + addr_off] = data;
|
|
|
|
|
|
|
|
DEBUG( "%2X(%c)", (int)data, data<0x20?'.':data);
|
|
|
|
}
|
|
|
|
DEBUG( "\n");
|
|
|
|
|
|
|
|
addr_base += blockSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, OFF);
|
|
|
|
|
|
|
|
return n_bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int pickit2_commit_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
|
|
|
OPCODE * wp, * lext;
|
|
|
|
|
|
|
|
wp = mem->op[AVR_OP_WRITEPAGE];
|
|
|
|
if (wp == NULL)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "pickit2_commit_page(): memory \"%s\" not configured for page writes\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
mem->desc);
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjust the address if this memory is word-addressable
|
|
|
|
if ((mem->op[AVR_OP_LOADPAGE_LO]) || (mem->op[AVR_OP_READ_LO]))
|
|
|
|
addr /= 2;
|
|
|
|
|
|
|
|
unsigned char cmd[8];
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
|
|
|
|
// use the "load extended address" command, if available
|
|
|
|
lext = mem->op[AVR_OP_LOAD_EXT_ADDR];
|
|
|
|
if (lext != NULL)
|
|
|
|
{
|
|
|
|
avr_set_bits(lext, cmd);
|
|
|
|
avr_set_addr(lext, cmd, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// make up the write page command in the 2nd cmd position
|
|
|
|
avr_set_bits(wp, &cmd[4]);
|
|
|
|
avr_set_addr(wp, &cmd[4], addr);
|
|
|
|
|
|
|
|
if (lext != NULL)
|
|
|
|
{
|
|
|
|
// write the load extended address cmd && the write_page cmd
|
|
|
|
pgm->spi(pgm, cmd, NULL, 8);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// write just the write_page cmd
|
|
|
|
pgm->spi(pgm, &cmd[4], NULL, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// just delay the max (we could do the delay in the PICkit2 if we wanted)
|
|
|
|
usleep(mem->max_write_delay);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not actually a paged write, but a bulk/batch write
|
|
|
|
static int pickit2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
|
|
|
unsigned int page_size, unsigned int addr, unsigned int n_bytes)
|
|
|
|
{
|
|
|
|
// only paged write for flash implemented
|
|
|
|
if (strcmp(mem->desc, "flash") != 0 && strcmp(mem->desc, "eeprom") != 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Part does not support %d paged write of %s\n", page_size, mem->desc);
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG( "page size %d mem %s supported: %d\n", page_size, mem->desc, mem->paged);
|
|
|
|
DEBUG( "loadpagehi %x, loadpagelow %x, writepage %x\n", (int)mem->op[AVR_OP_LOADPAGE_HI], (int)mem->op[AVR_OP_LOADPAGE_LO], (int)mem->op[AVR_OP_WRITEPAGE]);
|
|
|
|
|
|
|
|
OPCODE *writeop;
|
|
|
|
uint8_t cmd[SPI_MAX_CHUNK], res[SPI_MAX_CHUNK];
|
|
|
|
unsigned int addr_base;
|
|
|
|
unsigned int max_addr = addr + n_bytes;
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, ON);
|
|
|
|
|
|
|
|
for (addr_base = addr; addr_base < max_addr; )
|
|
|
|
{
|
|
|
|
uint32_t blockSize;
|
|
|
|
|
|
|
|
if (mem->paged)
|
|
|
|
{
|
|
|
|
blockSize = MIN(page_size - (addr_base % page_size), MIN(max_addr - addr_base, SPI_MAX_CHUNK/4) ); // bytes remaining in page
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blockSize = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(cmd, 0, sizeof(cmd));
|
|
|
|
memset(res, 0, sizeof(res));
|
|
|
|
|
|
|
|
uint8_t addr_off;
|
|
|
|
for (addr_off = 0; addr_off < blockSize; addr_off++)
|
|
|
|
{
|
|
|
|
int addr = addr_base + addr_off;
|
|
|
|
int caddr = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* determine which memory opcode to use
|
|
|
|
*/
|
|
|
|
if (mem->paged && mem->op[AVR_OP_LOADPAGE_HI] && mem->op[AVR_OP_LOADPAGE_LO])
|
|
|
|
{
|
|
|
|
if (addr & 0x01)
|
|
|
|
writeop = mem->op[AVR_OP_LOADPAGE_HI];
|
|
|
|
else
|
|
|
|
writeop = mem->op[AVR_OP_LOADPAGE_LO];
|
|
|
|
caddr = addr / 2;
|
|
|
|
}
|
|
|
|
else if (mem->paged && mem->op[AVR_OP_LOADPAGE_LO])
|
|
|
|
{
|
|
|
|
writeop = mem->op[AVR_OP_LOADPAGE_LO];
|
|
|
|
caddr = addr;
|
|
|
|
}
|
|
|
|
else if (mem->op[AVR_OP_WRITE_LO])
|
|
|
|
{
|
|
|
|
writeop = mem->op[AVR_OP_WRITE_LO];
|
|
|
|
caddr = addr; // maybe this should divide by 2 & use the write_high opcode also
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Error AVR_OP_WRITE_LO defined only (where's the HIGH command?)\n");
|
2012-08-15 18:34:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
writeop = mem->op[AVR_OP_WRITE];
|
|
|
|
caddr = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writeop == NULL)
|
|
|
|
{
|
|
|
|
pgm->err_led(pgm, ON);
|
|
|
|
// not supported!
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
avr_set_bits(writeop, &cmd[addr_off*4]);
|
|
|
|
avr_set_addr(writeop, &cmd[addr_off*4], caddr);
|
|
|
|
avr_set_input(writeop, &cmd[addr_off*4], mem->buf[addr]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytes_read = pgm->spi(pgm, cmd, res, blockSize*4);
|
|
|
|
|
|
|
|
if (bytes_read < 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Failed @ pgm->spi()\n");
|
2012-08-15 18:34:53 +00:00
|
|
|
pgm->err_led(pgm, ON);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_base += blockSize;
|
|
|
|
|
|
|
|
// write the page - this function looks after extended address also
|
|
|
|
if (mem->paged && (((addr_base % page_size) == 0) || (addr_base == max_addr)))
|
|
|
|
{
|
|
|
|
DEBUG( "Calling pickit2_commit_page()\n");
|
|
|
|
pickit2_commit_page(pgm, p, mem, addr_base-1);
|
|
|
|
}
|
|
|
|
else if (!mem->paged)
|
|
|
|
{
|
|
|
|
usleep(mem->max_write_delay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pgm->pgm_led(pgm, OFF);
|
|
|
|
|
|
|
|
return n_bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-02 20:22:53 +00:00
|
|
|
static int pickit2_cmd(struct programmer_t * pgm, const unsigned char *cmd,
|
|
|
|
unsigned char *res)
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
return pgm->spi(pgm, cmd, res, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// breaks up the cmd[] data into packets & sends to the pickit2. Data shifted in is stored in res[].
|
2013-09-02 20:22:53 +00:00
|
|
|
static int pickit2_spi(struct programmer_t * pgm, const unsigned char *cmd,
|
|
|
|
unsigned char *res, int n_bytes)
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
int retval = 0, temp1 = 0, temp2 = 0, count = n_bytes;
|
|
|
|
|
|
|
|
while (count > 0)
|
|
|
|
{
|
|
|
|
uint8_t i, blockSize = MIN(count, SPI_MAX_CHUNK);
|
|
|
|
uint8_t report[65] = {0, CMD_DOWNLOAD_DATA_2(blockSize)};
|
|
|
|
uint8_t *repptr = report + 3;
|
|
|
|
|
|
|
|
memset(report + 3, CMD_END_OF_BUFFER, sizeof(report) - 3);
|
|
|
|
|
|
|
|
// append some data to write to SPI
|
|
|
|
for (i = 0; i < blockSize; i++)
|
|
|
|
{
|
|
|
|
*repptr++ = *cmd++;
|
|
|
|
count--; // 1 less byte to pack
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blockSize == 1)
|
|
|
|
{
|
|
|
|
*repptr++ = 0xa6; //CMD_EXECUTE_SCRIPT;
|
|
|
|
*repptr++ = 1;
|
|
|
|
*repptr++ = SCR_SPI;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*repptr++ = 0xa6; //CMD_EXECUTE_SCRIPT_2;
|
|
|
|
*repptr++ = 4;
|
|
|
|
*repptr++ = SCR_SPI;
|
|
|
|
*repptr++ = 0xe9; //SCR_LOOP_3;
|
|
|
|
*repptr++ = 1;
|
|
|
|
*repptr++ = blockSize - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// request the data read to be sent to us
|
|
|
|
*repptr++ = CMD_UPLOAD_DATA;
|
|
|
|
|
|
|
|
// check return values
|
|
|
|
if ((temp1=pickit2_write_report(pgm, report)) < 0 ||
|
|
|
|
(temp2=pickit2_read_report(pgm, report)) < 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}/*
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
DEBUG( "in spi. wrote %d, read %d\n", temp1, temp2);
|
|
|
|
|
|
|
|
for (i = 0; i < temp2; i++)
|
|
|
|
{
|
|
|
|
DEBUG( "%2.2x ", report[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG( "\n");
|
|
|
|
}*/
|
|
|
|
|
|
|
|
retval = report[1]; // upload-length field
|
|
|
|
repptr = &report[2]; // actual data starts here
|
|
|
|
|
|
|
|
if (res) // copy data if user has specified a storage location
|
|
|
|
{
|
|
|
|
memcpy(res, repptr, retval);
|
|
|
|
res += retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if (defined(WIN32NATIVE) && defined(HAVE_LIBHID))
|
|
|
|
/*
|
|
|
|
Func: open_hid()
|
|
|
|
Desc: finds & opens device having specified VID & PID.
|
|
|
|
Retn: Handle of open device or INVALID_HANDLE_VALUE on fail
|
|
|
|
|
|
|
|
Note this routine is a modified function from:
|
|
|
|
usbhidiocDlg.cpp : implementation file
|
|
|
|
Project: usbhidioc.cpp
|
|
|
|
Version: 3.0
|
|
|
|
Date: 7/18/05
|
|
|
|
by Jan Axelson (jan@Lvr.com)
|
|
|
|
*/
|
|
|
|
static HANDLE open_hid(unsigned short vid, unsigned short pid)
|
|
|
|
{
|
|
|
|
//Use a series of API calls to find a HID with a specified Vendor IF and Product ID.
|
|
|
|
HANDLE returnHandle = INVALID_HANDLE_VALUE;
|
|
|
|
HIDD_ATTRIBUTES Attributes;
|
|
|
|
// DWORD DeviceUsage;
|
|
|
|
SP_DEVICE_INTERFACE_DATA devInfoData;
|
|
|
|
BOOL LastDevice = FALSE;
|
|
|
|
int MemberIndex = 0;
|
|
|
|
LONG Result;
|
|
|
|
|
|
|
|
// were global, now just local scrap
|
2014-07-16 20:02:01 +00:00
|
|
|
DWORD Length = 0;
|
2012-08-15 18:34:53 +00:00
|
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
|
|
|
|
HANDLE DeviceHandle=NULL;
|
|
|
|
GUID HidGuid;
|
|
|
|
HANDLE hDevInfo;
|
|
|
|
ULONG Required;
|
|
|
|
BOOL MyDeviceDetected = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
API function: HidD_GetHidGuid
|
|
|
|
Get the GUID for all system HIDs.
|
|
|
|
Returns: the GUID in HidGuid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
HidD_GetHidGuid(&HidGuid);
|
|
|
|
DEBUG("\nHidD_GetHidGuid returned.\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
API function: SetupDiGetClassDevs
|
|
|
|
Returns: a handle to a device information set for all installed devices.
|
|
|
|
Requires: the GUID returned by GetHidGuid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
hDevInfo=SetupDiGetClassDevs
|
|
|
|
(&HidGuid,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
|
|
|
|
|
|
|
|
DEBUG("\nSetupDiGetClassDevs returned 0x%x\n", hDevInfo);
|
|
|
|
devInfoData.cbSize = sizeof(devInfoData);
|
|
|
|
|
|
|
|
//Step through the available devices looking for the one we want.
|
|
|
|
//Quit on detecting the desired device or checking all available devices without success.
|
|
|
|
|
|
|
|
MemberIndex = 0;
|
|
|
|
LastDevice = FALSE;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
API function: SetupDiEnumDeviceInterfaces
|
|
|
|
On return, MyDeviceInterfaceData contains the handle to a
|
|
|
|
SP_DEVICE_INTERFACE_DATA structure for a detected device.
|
|
|
|
Requires:
|
|
|
|
The DeviceInfoSet returned in SetupDiGetClassDevs.
|
|
|
|
The HidGuid returned in GetHidGuid.
|
|
|
|
An index to specify a device.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
Result=SetupDiEnumDeviceInterfaces
|
|
|
|
(hDevInfo,
|
|
|
|
0,
|
|
|
|
&HidGuid,
|
|
|
|
MemberIndex,
|
|
|
|
&devInfoData);
|
|
|
|
|
|
|
|
DEBUG("\nSetupDiEnumDeviceInterfaces returned 0x%x\n", Result);
|
|
|
|
|
|
|
|
if (Result != 0)
|
|
|
|
{
|
|
|
|
//A device has been detected, so get more information about it.
|
|
|
|
|
|
|
|
/*
|
|
|
|
API function: SetupDiGetDeviceInterfaceDetail
|
|
|
|
Returns: an SP_DEVICE_INTERFACE_DETAIL_DATA structure
|
|
|
|
containing information about a device.
|
|
|
|
To retrieve the information, call this function twice.
|
|
|
|
The first time returns the size of the structure in Length.
|
|
|
|
The second time returns a pointer to the data in DeviceInfoSet.
|
|
|
|
Requires:
|
|
|
|
A DeviceInfoSet returned by SetupDiGetClassDevs
|
|
|
|
The SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces.
|
|
|
|
|
|
|
|
The final parameter is an optional pointer to an SP_DEV_INFO_DATA structure.
|
|
|
|
This application doesn't retrieve or use the structure.
|
|
|
|
If retrieving the structure, set
|
|
|
|
MyDeviceInfoData.cbSize = length of MyDeviceInfoData.
|
|
|
|
and pass the structure's address.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Get the Length value.
|
|
|
|
//The call will return with a "buffer too small" error which can be ignored.
|
|
|
|
Result = SetupDiGetDeviceInterfaceDetail
|
|
|
|
(hDevInfo,
|
|
|
|
&devInfoData,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
&Length,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
DEBUG("\nSetupDiGetDeviceInterfaceDetail returned 0x%x\n", Result);
|
|
|
|
|
|
|
|
//Allocate memory for the hDevInfo structure, using the returned Length.
|
|
|
|
|
|
|
|
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
|
|
|
|
|
|
|
|
//Set cbSize in the detailData structure.
|
|
|
|
|
|
|
|
detailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
|
|
|
|
|
|
//Call the function again, this time passing it the returned buffer size.
|
|
|
|
|
|
|
|
Result = SetupDiGetDeviceInterfaceDetail
|
|
|
|
(hDevInfo,
|
|
|
|
&devInfoData,
|
|
|
|
detailData,
|
|
|
|
Length,
|
|
|
|
&Required,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
// Open a handle to the device.
|
|
|
|
// To enable retrieving information about a system mouse or keyboard,
|
|
|
|
// don't request Read or Write access for this handle.
|
|
|
|
|
|
|
|
/*
|
|
|
|
API function: CreateFile
|
|
|
|
Returns: a handle that enables reading and writing to the device.
|
|
|
|
Requires:
|
|
|
|
The DevicePath in the detailData structure
|
|
|
|
returned by SetupDiGetDeviceInterfaceDetail.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DeviceHandle=CreateFile
|
|
|
|
(detailData->DevicePath,
|
|
|
|
0,
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
(LPSECURITY_ATTRIBUTES)NULL,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
DEBUG("CreateFile(): %s\n", detailData->DevicePath);
|
|
|
|
/*
|
|
|
|
API function: HidD_GetAttributes
|
|
|
|
Requests information from the device.
|
|
|
|
Requires: the handle returned by CreateFile.
|
|
|
|
Returns: a HIDD_ATTRIBUTES structure containing
|
|
|
|
the Vendor ID, Product ID, and Product Version Number.
|
|
|
|
Use this information to decide if the detected device is
|
|
|
|
the one we're looking for.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Set the Size to the number of bytes in the structure.
|
|
|
|
|
|
|
|
Attributes.Size = sizeof(Attributes);
|
|
|
|
|
|
|
|
Result = HidD_GetAttributes
|
|
|
|
(DeviceHandle,
|
|
|
|
&Attributes);
|
|
|
|
|
|
|
|
DEBUG("HidD_GetAttributes returned 0x%x\n", Result);
|
|
|
|
DEBUG("VID: %.4X PID: %.4X\n", Attributes.VendorID, Attributes.ProductID);
|
|
|
|
|
|
|
|
//Is it the desired device?
|
|
|
|
MyDeviceDetected = FALSE;
|
|
|
|
if (Attributes.VendorID == vid)
|
|
|
|
{
|
|
|
|
if (Attributes.ProductID == pid)
|
|
|
|
{
|
|
|
|
//Both the Vendor ID and Product ID match.
|
|
|
|
|
|
|
|
MyDeviceDetected = TRUE;
|
|
|
|
|
|
|
|
// Get a handle for us to use.
|
|
|
|
|
|
|
|
returnHandle = CreateFile
|
|
|
|
(detailData->DevicePath,
|
|
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
(LPSECURITY_ATTRIBUTES)NULL,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_FLAG_OVERLAPPED,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
} //if (Attributes.ProductID == ProductID)
|
|
|
|
|
|
|
|
else
|
|
|
|
//The Product ID doesn't match.
|
|
|
|
|
|
|
|
CloseHandle(DeviceHandle);
|
|
|
|
|
|
|
|
} //if (Attributes.VendorID == VendorID)
|
|
|
|
|
|
|
|
else
|
|
|
|
//The Vendor ID doesn't match.
|
|
|
|
|
|
|
|
CloseHandle(DeviceHandle);
|
|
|
|
|
|
|
|
//Free the memory used by the detailData structure (no longer needed).
|
|
|
|
|
|
|
|
free(detailData);
|
|
|
|
|
|
|
|
} //if (Result != 0)
|
|
|
|
|
|
|
|
else
|
|
|
|
//SetupDiEnumDeviceInterfaces returned 0, so there are no more devices to check.
|
|
|
|
|
|
|
|
LastDevice=TRUE;
|
|
|
|
|
|
|
|
//If we haven't found the device yet, and haven't tried every available device,
|
|
|
|
//try the next one.
|
|
|
|
|
|
|
|
MemberIndex = MemberIndex + 1;
|
|
|
|
|
|
|
|
} //do
|
|
|
|
|
|
|
|
while ((LastDevice == FALSE) && (MyDeviceDetected == FALSE));
|
|
|
|
|
|
|
|
if (MyDeviceDetected == FALSE)
|
|
|
|
DEBUG("Device not detected\n");
|
|
|
|
else
|
|
|
|
DEBUG("Device detected\n");
|
|
|
|
|
|
|
|
//Free the memory reserved for hDevInfo by SetupDiClassDevs.
|
|
|
|
|
|
|
|
DEBUG("Calling SetupDiDestroyDeviceInfoList\n");
|
|
|
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
|
|
|
|
|
|
return returnHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
// simple read with timeout
|
|
|
|
static int usb_read_interrupt(PROGRAMMER *pgm, void *buff, int size, int timeout)
|
|
|
|
{
|
|
|
|
OVERLAPPED ovr;
|
|
|
|
DWORD bytesRead = 0;
|
|
|
|
|
|
|
|
if (PDATA(pgm)->read_event == NULL)
|
|
|
|
{
|
|
|
|
PDATA(pgm)->read_event = CreateEvent(0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ovr, 0, sizeof(ovr));
|
|
|
|
ovr.hEvent = PDATA(pgm)->read_event;
|
|
|
|
|
|
|
|
ReadFile(PDATA(pgm)->usb_handle, buff, size, &bytesRead, &ovr);
|
|
|
|
if (WaitForSingleObject(PDATA(pgm)->read_event, timeout) == WAIT_TIMEOUT)
|
|
|
|
{
|
|
|
|
CancelIo(PDATA(pgm)->usb_handle);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetOverlappedResult(PDATA(pgm)->usb_handle, &ovr, &bytesRead, 0);
|
|
|
|
|
|
|
|
return bytesRead > 0 ? bytesRead : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// simple write with timeout
|
2014-07-16 20:02:01 +00:00
|
|
|
static int usb_write_interrupt(PROGRAMMER *pgm, const void *buff, int size, int timeout)
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
OVERLAPPED ovr;
|
|
|
|
DWORD bytesWritten = 0;
|
|
|
|
|
|
|
|
if (PDATA(pgm)->write_event == NULL)
|
|
|
|
{
|
|
|
|
PDATA(pgm)->write_event = CreateEvent(0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ovr, 0, sizeof(ovr));
|
|
|
|
ovr.hEvent = PDATA(pgm)->write_event;
|
|
|
|
|
|
|
|
WriteFile(PDATA(pgm)->usb_handle, buff, size, &bytesWritten, &ovr);
|
|
|
|
if (WaitForSingleObject(PDATA(pgm)->write_event, timeout) == WAIT_TIMEOUT)
|
|
|
|
{
|
|
|
|
CancelIo(PDATA(pgm)->usb_handle);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetOverlappedResult(PDATA(pgm)->usb_handle, &ovr, &bytesWritten, 0);
|
|
|
|
|
|
|
|
return bytesWritten > 0 ? bytesWritten : -1;
|
|
|
|
}
|
|
|
|
|
2014-07-16 20:02:01 +00:00
|
|
|
static int pickit2_write_report(PROGRAMMER * pgm, const unsigned char report[65])
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
2014-07-16 20:02:01 +00:00
|
|
|
return usb_write_interrupt(pgm, report, 65, PDATA(pgm)->transaction_timeout); // XXX
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_read_report(PROGRAMMER * pgm, unsigned char report[65])
|
|
|
|
{
|
|
|
|
return usb_read_interrupt(pgm, report, 65, PDATA(pgm)->transaction_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // WIN32NATIVE
|
|
|
|
/* taken (modified) from avrdude usbasp.c */
|
|
|
|
static int usb_open_device(struct usb_dev_handle **device, int vendor, int product)
|
|
|
|
{
|
|
|
|
struct usb_bus *bus;
|
|
|
|
struct usb_device *dev;
|
|
|
|
usb_dev_handle *handle = NULL;
|
|
|
|
int errorCode = USB_ERROR_NOTFOUND;
|
|
|
|
static int didUsbInit = 0;
|
|
|
|
|
|
|
|
if (!didUsbInit)
|
|
|
|
{
|
|
|
|
didUsbInit = 1;
|
|
|
|
usb_init();
|
|
|
|
}
|
|
|
|
usb_find_busses();
|
|
|
|
usb_find_devices();
|
|
|
|
for (bus=usb_get_busses(); bus; bus=bus->next)
|
|
|
|
{
|
|
|
|
for (dev=bus->devices; dev; dev=dev->next)
|
|
|
|
{
|
|
|
|
DEBUG( "Enumerating device list.. VID: 0x%4.4x, PID: 0x%4.4x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
|
|
|
|
if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product)
|
|
|
|
{
|
|
|
|
/* we need to open the device in order to query strings */
|
|
|
|
handle = usb_open(dev);
|
|
|
|
if (handle == NULL)
|
|
|
|
{
|
|
|
|
errorCode = USB_ERROR_ACCESS;
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: Warning: cannot open USB device: %s\n", progname, usb_strerror());
|
2012-08-15 18:34:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return with opened device handle
|
|
|
|
else
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_NOTICE, "Device %p seemed to open OK.\n", handle);
|
2012-08-15 18:34:53 +00:00
|
|
|
|
|
|
|
if ((errorCode = usb_set_configuration(handle, 1)) < 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Could not set configuration. Error code %d, %s.\n"
|
2012-08-15 18:34:53 +00:00
|
|
|
"You may need to run avrdude as root or set up correct usb port permissions.", errorCode, usb_strerror());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((errorCode = usb_claim_interface(handle, 0)) < 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "Could not claim interface. Error code %d, %s\n"
|
2012-08-15 18:34:53 +00:00
|
|
|
"You may need to run avrdude as root or set up correct usb port permissions.", errorCode, usb_strerror());
|
|
|
|
}
|
|
|
|
|
|
|
|
errorCode = 0;
|
|
|
|
*device = handle;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-16 20:02:01 +00:00
|
|
|
static int pickit2_write_report(PROGRAMMER * pgm, const unsigned char report[65])
|
2012-08-15 18:34:53 +00:00
|
|
|
{
|
|
|
|
// endpoint 1 OUT??
|
2014-07-16 20:02:01 +00:00
|
|
|
return usb_interrupt_write(PDATA(pgm)->usb_handle, USB_ENDPOINT_OUT | 1, (const char*)(report+1), 64, PDATA(pgm)->transaction_timeout);
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pickit2_read_report(PROGRAMMER * pgm, unsigned char report[65])
|
|
|
|
{
|
|
|
|
// endpoint 1 IN??
|
2014-07-16 20:02:01 +00:00
|
|
|
return usb_interrupt_read(PDATA(pgm)->usb_handle, USB_ENDPOINT_IN | 1, (char*)(report+1), 64, PDATA(pgm)->transaction_timeout);
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
#endif // WIN323NATIVE
|
|
|
|
|
|
|
|
static int pickit2_parseextparams(struct programmer_t * pgm, LISTID extparms)
|
|
|
|
{
|
|
|
|
LNODEID ln;
|
|
|
|
const char *extended_param;
|
|
|
|
int rv = 0;
|
|
|
|
|
|
|
|
for (ln = lfirst(extparms); ln; ln = lnext(ln))
|
|
|
|
{
|
|
|
|
extended_param = ldata(ln);
|
|
|
|
|
|
|
|
if (strncmp(extended_param, "clockrate=", strlen("clockrate=")) == 0)
|
|
|
|
{
|
|
|
|
int clock_rate;
|
|
|
|
if (sscanf(extended_param, "clockrate=%i", &clock_rate) != 1 || clock_rate <= 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: pickit2_parseextparms(): invalid clockrate '%s'\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, extended_param);
|
2012-08-15 18:34:53 +00:00
|
|
|
rv = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int clock_period = MIN(1000000 / clock_rate, 255); // max period is 255
|
|
|
|
clock_rate = (int)(1000000 / (clock_period + 5e-7)); // assume highest speed is 2MHz - should probably check this
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_NOTICE2, "%s: pickit2_parseextparms(): clockrate set to 0x%02x\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, clock_rate);
|
2012-08-15 18:34:53 +00:00
|
|
|
PDATA(pgm)->clock_period = clock_period;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(extended_param, "timeout=", strlen("timeout=")) == 0)
|
|
|
|
{
|
|
|
|
int timeout;
|
|
|
|
if (sscanf(extended_param, "timeout=%i", &timeout) != 1 || timeout <= 0)
|
|
|
|
{
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: pickit2_parseextparms(): invalid timeout '%s'\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, extended_param);
|
2012-08-15 18:34:53 +00:00
|
|
|
rv = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_NOTICE2, "%s: pickit2_parseextparms(): usb timeout set to 0x%02x\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, timeout);
|
2012-08-15 18:34:53 +00:00
|
|
|
PDATA(pgm)->transaction_timeout = timeout;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO, "%s: pickit2_parseextparms(): invalid extended parameter '%s'\n",
|
2014-05-18 08:41:46 +00:00
|
|
|
progname, extended_param);
|
2012-08-15 18:34:53 +00:00
|
|
|
rv = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void pickit2_initpgm (PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* mandatory functions - these are called without checking to see
|
|
|
|
* whether they are assigned or not
|
|
|
|
*/
|
|
|
|
|
|
|
|
pgm->initialize = pickit2_initialize;
|
|
|
|
pgm->display = pickit2_display;
|
|
|
|
pgm->enable = pickit2_enable;
|
|
|
|
pgm->disable = pickit2_disable;
|
|
|
|
pgm->powerup = pickit2_powerup;
|
|
|
|
pgm->powerdown = pickit2_powerdown;
|
|
|
|
pgm->program_enable = pickit2_program_enable;
|
|
|
|
pgm->chip_erase = pickit2_chip_erase;
|
|
|
|
pgm->open = pickit2_open;
|
|
|
|
pgm->close = pickit2_close;
|
|
|
|
|
|
|
|
pgm->read_byte = avr_read_byte_default;
|
|
|
|
pgm->write_byte = avr_write_byte_default;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* predefined functions - these functions have a valid default
|
|
|
|
* implementation. Hence, they don't need to be defined in
|
|
|
|
* the programmer.
|
|
|
|
*/
|
|
|
|
//pgm->rdy_led = pickit2_rdy_led;
|
|
|
|
//pgm->err_led = pickit2_err_led;
|
|
|
|
pgm->pgm_led = pickit2_pgm_led;
|
|
|
|
pgm->vfy_led = pickit2_vfy_led;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* optional functions - these are checked to make sure they are
|
|
|
|
* assigned before they are called
|
|
|
|
*/
|
|
|
|
|
|
|
|
pgm->cmd = pickit2_cmd;
|
|
|
|
pgm->spi = pickit2_spi;
|
|
|
|
pgm->paged_write = pickit2_paged_write;
|
|
|
|
pgm->paged_load = pickit2_paged_load;
|
|
|
|
//pgm->write_setup = NULL;
|
|
|
|
//pgm->read_sig_bytes = NULL;
|
|
|
|
//pgm->set_vtarget = NULL;//pickit2_vtarget;
|
|
|
|
//pgm->set_varef = NULL;
|
|
|
|
//pgm->set_fosc = NULL;
|
|
|
|
//pgm->perform_osccal = NULL;
|
|
|
|
|
|
|
|
pgm->parseextparams = pickit2_parseextparams;
|
|
|
|
|
|
|
|
pgm->setup = pickit2_setup;
|
|
|
|
pgm->teardown = pickit2_teardown;
|
|
|
|
// pgm->page_size = 256; // not sure what this does... maybe the max page size that the page read/write function can handle
|
|
|
|
|
|
|
|
strncpy(pgm->type, "pickit2", sizeof(pgm->type));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int pickit2_nousb_open (struct programmer_t *pgm, char * name) {
|
2014-06-13 20:07:40 +00:00
|
|
|
avrdude_message(MSG_INFO,
|
2012-08-15 18:34:53 +00:00
|
|
|
#ifdef WIN32NATIVE
|
|
|
|
"%s: error: no usb or hid support. Please compile again with libusb or HID support from Win32 DDK installed.\n",
|
|
|
|
#else
|
|
|
|
"%s: error: no usb support. Please compile again with libusb installed.\n",
|
|
|
|
#endif
|
|
|
|
progname);
|
2014-06-17 20:08:28 +00:00
|
|
|
|
|
|
|
return -1;
|
2012-08-15 18:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pickit2_initpgm (PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* mandatory functions - these are called without checking to see
|
|
|
|
* whether they are assigned or not
|
|
|
|
*/
|
|
|
|
|
|
|
|
pgm->open = pickit2_nousb_open;
|
|
|
|
|
|
|
|
strncpy(pgm->type, "pickit2", sizeof(pgm->type));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* defined(HAVE_LIBUSB) || (defined(WIN32NATIVE) && defined(HAVE_LIBHID)) */
|
|
|
|
|
|
|
|
const char pickit2_desc[] = "Microchip's PICkit2 Programmer";
|
|
|
|
|