From 7d1ea872cddf06429189d277cb0b43765fda7ebe Mon Sep 17 00:00:00 2001
From: mludvig <mludvig@81a1dc3b-b13d-400b-aceb-764788c761c2>
Date: Mon, 9 Nov 2009 02:18:40 +0000
Subject: [PATCH] * buspirate.c: Implemented reset= and speed= extended
 parameters. * avrdude.1: Document the change.

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@879 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 ChangeLog   |   5 ++
 avrdude.1   |  45 ++++++++++++--
 buspirate.c | 171 +++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 180 insertions(+), 41 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index ec87efad..9a422c73 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-11-09  Michal Ludvig  <mludvig@logix.net.nz>
+
+	* buspirate.c: Implemented reset= and speed= extended parameters.
+	* avrdude.1: Document the change.
+
 2009-11-04  Michal Ludvig  <mludvig@logix.net.nz>
 
 	* configure.ac, Makefile.am: Test if GCC accepts -Wno-pointer-sign
diff --git a/avrdude.1 b/avrdude.1
index c212f295..f03f1c49 100644
--- a/avrdude.1
+++ b/avrdude.1
@@ -111,10 +111,10 @@ The Arduino (which is very similar to the STK500 1.x) is supported via
 its own programmer type specification ``arduino''.
 .Pp
 The BusPirate is a versatile tool that can also be used as an AVR programmer.
-Older BusPirate firmwares are supported in ASCII mode, for newer
-firmwares (2.7+) avrdude automatically selects binary mode. Should there
-be a problem with the binary mode use '-x ascii' to prefer ascii
-mode with newer firmwares.
+A single BusPirate can be connected to up to 3 independent AVRs. See
+the section on
+.Em extended parameters
+below for details.
 .Pp
 Atmel's STK600 programmer is supported in ISP and high-voltage
 programming modes, and connects through the USB.
@@ -851,10 +851,45 @@ programmer creates errors during initial sequence.
 .El
 .It Ar buspirate
 .Bl -tag -offset indent -width indent
+.It Ar reset={cs,aux,aux2}
+The default setup assumes the BusPirate's CS output pin connected to 
+the RESET pin on AVR side. It is however possible to have multiple AVRs
+connected to the same BP with MISO, MOSI and SCK lines common for all of them.
+In such a case one AVR should have its RESET connected to BusPirate's
+.Pa CS
+pin, second AVR's RESET connected to BusPirate's
+.Pa AUX
+pin and if your BusPirate has an
+.Pa AUX2
+pin (only available on BusPirate version v1a with firmware 3.0 or newer)
+use that to activate RESET on the third AVR.
+.Pp
+It may be a good idea to decouple the BusPirate and the AVR's SPI buses from 
+each other using a 3-state bus buffer. For example 74HC125 or 74HC244 are some
+good candidates with the latches driven by the appropriate reset pin (cs, 
+aux or aux2). Otherwise the SPI traffic in one active circuit may interfere
+with programming the AVR in the other design.
+.It Ar speed=<0..7>
+BusPirate to AVR SPI speed:
+.Bd -literal
+0 ..  30 kHz   (default)
+1 .. 125 kHz
+2 .. 250 kHz
+3 ..   1 MHz
+4 ..   2 MHz
+5 ..   2.6 MHz
+6 ..   4 MHz
+7 ..   8 MHz
+.Ed
 .It Ar ascii
 Use ASCII mode even when the firmware supports BinMode (binary mode). 
 BinMode is supported in firmware 2.7 and newer, older FW's either don't
-have BinMode or their BinMode is buggy.
+have BinMode or their BinMode is buggy. ASCII mode is slower and makes 
+the above
+.Ar reset=
+and 
+.Ar speed=
+parameters unavailable. 
 .El
 .El
 .Sh FILES
diff --git a/buspirate.c b/buspirate.c
index edc8a658..0aa3cc81 100644
--- a/buspirate.c
+++ b/buspirate.c
@@ -46,20 +46,47 @@
 #include "serial.h"
 
 /* ====== Private data structure ====== */
+/* CS and AUX pin bitmasks in 
+ * 0100wxyz – Configure peripherals command */
+#define BP_RESET_CS     0x01
+#define BP_RESET_AUX    0x02
+#define BP_RESET_AUX2   0x04
+
+#define BP_FLAG_IN_BINMODE          (1<<0)
+#define BP_FLAG_XPARM_FORCE_ASCII   (1<<1)
+#define BP_FLAG_XPARM_RESET         (1<<2)
+#define BP_FLAG_XPARM_SPIFREQ       (1<<3)
+
 struct pdata
 {
 	char	hw_version[10];
 	int		fw_version;		/* = 100*fw_major + fw_minor */
 	int		binmode_version;
 	int		bin_spi_version;
-	int		in_binmode;
-	int		force_ascii;
+	int		current_peripherals_config;
+	int		spifreq;		/* 0..7 - see buspirate manual for what freq each value means */
+	int		reset;			/* See BP_RESET_* above */
 };
 #define PDATA(pgm) ((struct pdata *)(pgm->cookie))
 
 /* Binary mode is available from firmware v2.7 on */
 #define FW_BINMODE_VER	207
 
+/* ====== Feature checks ====== */
+static inline int
+buspirate_has_aux2(struct programmer_t *pgm)
+{
+	return ((PDATA(pgm)->fw_version >= 300) &&
+			strcmp(PDATA(pgm)->hw_version, "v1a") == 0);
+}
+
+static inline int
+buspirate_uses_ascii(struct programmer_t *pgm)
+{
+	return (pgm->flag & BP_FLAG_XPARM_FORCE_ASCII) ||
+		(PDATA(pgm)->fw_version < FW_BINMODE_VER);
+}
+
 /* ====== Serial talker functions - binmode ====== */
 
 static void dump_mem(unsigned char *buf, size_t len)
@@ -113,7 +140,7 @@ static int buspirate_expect_bin(struct programmer_t *pgm,
 								char *expect_data, size_t expect_len)
 {
 	char *recv_buf = alloca(expect_len);
-	if (!PDATA(pgm)->in_binmode) {
+	if (!pgm->flag & BP_FLAG_IN_BINMODE) {
 		fprintf(stderr, "BusPirate: Internal error: buspirate_send_bin() called from ascii mode");
 		exit(1);
 	}
@@ -138,7 +165,7 @@ static int buspirate_getc(struct programmer_t *pgm)
 	int rc;
 	unsigned char ch = 0;
 
-	if (PDATA(pgm)->in_binmode) {
+	if (pgm->flag & BP_FLAG_IN_BINMODE) {
 		fprintf(stderr, "BusPirate: Internal error: buspirate_getc() called from binmode");
 		exit(1);
 	}
@@ -197,7 +224,7 @@ static int buspirate_send(struct programmer_t *pgm, char *str)
 	if (verbose)
 		fprintf(stderr, "%s: buspirate_send(): %s", progname, str);
 
-	if (PDATA(pgm)->in_binmode) {
+	if (pgm->flag & BP_FLAG_IN_BINMODE) {
 		fprintf(stderr, "BusPirate: Internal error: buspirate_send() called from binmode");
 		exit(1);
 	}
@@ -244,6 +271,85 @@ static void buspirate_dummy_6(struct programmer_t *pgm,
 {
 }
 
+/* ====== Config / parameters handling functions ====== */
+static int
+buspirate_parseextparms(struct programmer_t *pgm, LISTID extparms)
+{
+	LNODEID ln;
+	const char *extended_param;
+	char reset[10];
+	char *preset = reset;	/* for strtok() */
+	int spifreq;
+
+	for (ln = lfirst(extparms); ln; ln = lnext(ln)) {
+    	extended_param = ldata(ln);
+		if (strcmp(extended_param, "ascii") == 0) {
+			pgm->flag |= BP_FLAG_XPARM_FORCE_ASCII;
+			continue;
+		}
+		if (sscanf(extended_param, "spifreq=%d", &spifreq) == 1) {
+			if (spifreq & (~0x07)) {
+				fprintf(stderr, "BusPirate: spifreq must be between 0 and 7.\n");
+				fprintf(stderr, "BusPirate: see BusPirate manual for details.\n");
+				return -1;
+			}
+			PDATA(pgm)->spifreq = spifreq;
+			pgm->flag |= BP_FLAG_XPARM_SPIFREQ;
+			continue;
+		}
+
+		if (sscanf(extended_param, "reset=%s", reset) == 1) {
+			char *resetpin;
+			while ((resetpin = strtok(preset, ","))) {
+				preset = NULL;	/* for subsequent strtok() calls */
+				if (strcasecmp(resetpin, "cs") == 0)
+					PDATA(pgm)->reset |= BP_RESET_CS;
+				else if (strcasecmp(resetpin, "aux") == 0 || strcasecmp(reset, "aux1") == 0)
+					PDATA(pgm)->reset |= BP_RESET_AUX;
+				else if (strcasecmp(resetpin, "aux2") == 0)
+					PDATA(pgm)->reset |= BP_RESET_AUX2;
+				else {
+					fprintf(stderr, "BusPirate: reset must be either CS or AUX.\n");
+					return -1;
+				}
+			}
+			pgm->flag |= BP_FLAG_XPARM_RESET;
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+static int
+buspirate_verifyconfig(struct programmer_t *pgm)
+{
+	/* Default reset pin is CS */
+	if (PDATA(pgm)->reset == 0x00)
+		PDATA(pgm)->reset |= BP_RESET_CS;
+
+	/* reset=AUX2 is only available on HW=v1a and FW>=3.0 */
+	if ((PDATA(pgm)->reset & BP_RESET_AUX2) && !buspirate_has_aux2(pgm)) {
+		fprintf(stderr, "BusPirate: Pin AUX2 is only available in binary mode\n");
+		fprintf(stderr, "BusPirate: with hardware==v1a && firmware>=3.0\n");
+		fprintf(stderr, "BusPirate: Your hardware==%s and firmware==%d.%d\n", 
+				PDATA(pgm)->hw_version, PDATA(pgm)->fw_version/100, PDATA(pgm)->fw_version%100);
+		return -1;
+	}
+
+	if ((PDATA(pgm)->reset != BP_RESET_CS) && buspirate_uses_ascii(pgm)) {
+		fprintf(stderr, "BusPirate: RESET pin other than CS is not supported in ASCII mode\n");
+		return -1;
+	}
+
+	if ((pgm->flag & BP_FLAG_XPARM_SPIFREQ) && buspirate_uses_ascii(pgm)) {
+		fprintf(stderr, "BusPirate: SPI speed selection is not supported in ASCII mode\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 /* ====== Programmer methods ======= */
 static int buspirate_open(struct programmer_t *pgm, char * port)
 {
@@ -276,7 +382,7 @@ static void buspirate_reset_from_binmode(struct programmer_t *pgm)
 	buf[0] = 0x0F;	/* BinMode: reset */
 	buspirate_send_bin(pgm, buf, 1);
 
-	PDATA(pgm)->in_binmode = 0;
+	pgm->flag &= ~BP_FLAG_IN_BINMODE;
 	while(1) {
 		buspirate_readline(pgm, buf, sizeof(buf) - 1);
 		if (buspirate_is_prompt(buf))
@@ -304,7 +410,7 @@ static int buspirate_start_spi_mode_bin(struct programmer_t *pgm)
 	if (verbose)
 		printf("BusPirate binmode version: %d\n", PDATA(pgm)->binmode_version);
 
-	PDATA(pgm)->in_binmode = 1;
+	pgm->flag |= BP_FLAG_IN_BINMODE;
 
 	/* == Enter SPI mode == */
 	buf[0] = 0x01;	/* Enter raw SPI mode */
@@ -319,16 +425,21 @@ static int buspirate_start_spi_mode_bin(struct programmer_t *pgm)
 	if (verbose)
 		printf("BusPirate SPI version: %d\n", PDATA(pgm)->bin_spi_version);
 
-	/* 0b0100wxyz – Configure peripherals w=power, x=pull-ups, y=AUX, z=CS
-	 * we want power and CS -- 0b01001001 = 0x49 */
-	buspirate_expect_bin_byte(pgm, 0x49, 0x01);
+	/* 0b0100wxyz – Configure peripherals w=power, x=pull-ups/aux2, y=AUX, z=CS
+	 * we want power (0x48) and all reset pins high. */
+	PDATA(pgm)->current_peripherals_config  = 0x48;
+	PDATA(pgm)->current_peripherals_config |= BP_RESET_CS;
+	PDATA(pgm)->current_peripherals_config |= BP_RESET_AUX;
+	if (buspirate_has_aux2(pgm))
+		PDATA(pgm)->current_peripherals_config |= BP_RESET_AUX2;
+	buspirate_expect_bin_byte(pgm, PDATA(pgm)->current_peripherals_config, 0x01);
 	usleep(50000);	// sleep for 50ms after power up
 
 	/* 01100xxx -  SPI speed
 	 * xxx = 000=30kHz, 001=125kHz, 010=250kHz, 011=1MHz,
 	 *       100=2MHz, 101=2.6MHz, 110=4MHz, 111=8MHz
 	 * use 30kHz = 0x60 */
-	buspirate_expect_bin_byte(pgm, 0x60, 0x01);
+	buspirate_expect_bin_byte(pgm, 0x60 | PDATA(pgm)->spifreq, 0x01);
 
 	/* 1000wxyz – SPI config, w=HiZ(0)/3.3v(1), x=CLK idle, y=CLK edge, z=SMP sample
 	 * we want: 3.3V(1), idle low(0), data change on trailing edge (1), 
@@ -417,14 +528,15 @@ static void buspirate_enable(struct programmer_t *pgm)
 		exit(1);
 	}
 
-	printf("BusPirate: firmware %d.%d\n", fw_v1, fw_v2);
+	if (buspirate_verifyconfig(pgm) < 0)
+		exit(1);
 
-	if (PDATA(pgm)->fw_version >= FW_BINMODE_VER && !PDATA(pgm)->force_ascii) {
+	if (PDATA(pgm)->fw_version >= FW_BINMODE_VER && !(pgm->flag & BP_FLAG_XPARM_FORCE_ASCII)) {
 		printf("BusPirate: using BINARY mode\n");
 		if (buspirate_start_spi_mode_bin(pgm) < 0)
 			fprintf(stderr, "%s: Failed to start binary SPI mode\n", progname);
 	}
-	if (! PDATA(pgm)->in_binmode) {
+	if (!pgm->flag & BP_FLAG_IN_BINMODE) {
 		printf("BusPirate: using ASCII mode\n");
 		if (buspirate_start_spi_mode_ascii(pgm) < 0) {
 			fprintf(stderr, "%s: Failed to start ascii SPI mode\n", progname);
@@ -435,7 +547,7 @@ static void buspirate_enable(struct programmer_t *pgm)
 
 static void buspirate_disable(struct programmer_t *pgm)
 {
-	if (PDATA(pgm)->in_binmode)
+	if (pgm->flag & BP_FLAG_IN_BINMODE)
 		buspirate_reset_from_binmode(pgm);
 	else
 		buspirate_expect(pgm, "#\n", "RESET", 1);
@@ -450,7 +562,7 @@ static int buspirate_initialize(struct programmer_t *pgm, AVRPART * p)
 
 static void buspirate_powerup(struct programmer_t *pgm)
 {
-	if (PDATA(pgm)->in_binmode) {
+	if (pgm->flag & BP_FLAG_IN_BINMODE) {
 		/* Powerup in BinMode is handled in SPI init */
 		return;
 	} else 
@@ -463,7 +575,7 @@ static void buspirate_powerup(struct programmer_t *pgm)
 
 static void buspirate_powerdown(struct programmer_t *pgm)
 {
-	if (PDATA(pgm)->in_binmode) {
+	if (pgm->flag & BP_FLAG_IN_BINMODE) {
 		/* 0b0100wxyz – Configure peripherals w=power, x=pull-ups, y=AUX, z=CS
 		 * we want everything off -- 0b01000000 = 0x40 */
 		if (buspirate_expect_bin_byte(pgm, 0x40, 0x01))
@@ -527,7 +639,7 @@ static int buspirate_cmd(struct programmer_t *pgm,
 						 unsigned char cmd[4],
 						 unsigned char res[4])
 {
-	if (PDATA(pgm)->in_binmode)
+	if (pgm->flag & BP_FLAG_IN_BINMODE)
 		return buspirate_cmd_bin(pgm, cmd, res);
 	else
 		return buspirate_cmd_ascii(pgm, cmd, res);
@@ -538,10 +650,11 @@ static int buspirate_program_enable(struct programmer_t *pgm, AVRPART * p)
 	unsigned char cmd[4];
 	unsigned char res[4];
 
-	if (PDATA(pgm)->in_binmode)
-		/* 0000001x – CS high (1) or low (0)
-		 * we want CS low -> 0x02 */
-		buspirate_expect_bin_byte(pgm, 0x02, 0x01);
+	if (pgm->flag & BP_FLAG_IN_BINMODE) {
+		/* Clear configured reset pin(s): CS and/or AUX and/or AUX2 */
+		PDATA(pgm)->current_peripherals_config &= ~PDATA(pgm)->reset;
+		buspirate_expect_bin_byte(pgm, PDATA(pgm)->current_peripherals_config, 0x01);
+	}
 	else
 		buspirate_expect(pgm, "{\n", "CS ENABLED", 1);
 
@@ -588,20 +701,6 @@ static int buspirate_chip_erase(struct programmer_t *pgm, AVRPART * p)
 	return 0;
 }
 
-static int buspirate_parseextparms(struct programmer_t *pgm, LISTID extparms)
-{
-	LNODEID ln;
-	const char *extended_param;
-
-	for (ln = lfirst(extparms); ln; ln = lnext(ln)) {
-    	extended_param = ldata(ln);
-		if (strcmp(extended_param, "ascii") == 0)
-			PDATA(pgm)->force_ascii = 1;
-	}
-
-	return 0;
-}
-
 void buspirate_initpgm(struct programmer_t *pgm)
 {
 	strcpy(pgm->type, "BusPirate");