Index: serjtag.c
===================================================================
--- serjtag.c	(revision 0)
+++ serjtag.c	(revision 0)
@@ -0,0 +1,731 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004  Theodore A. Roth  <troth@openavr.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* $Id$ */
+
+/* serjtag -- Serial Jtag Cable (using AVR) */
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "avr.h"
+#include "pgm.h"
+#include "serjtag.h"
+#include "serial.h"
+
+#define SERJTAG_DEBUG	0
+
+extern char * progname;
+extern int do_cycles;
+extern int ovsigck;
+extern int verbose;
+
+static int serjtag_send(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  return serial_send(&pgm->fd, (unsigned char *)buf, len);
+}
+
+
+static int serjtag_recv(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  int rv;
+
+  rv = serial_recv(&pgm->fd, (unsigned char *)buf, len);
+  if (rv < 0) {
+    fprintf(stderr,
+	    "%s: serjtag_recv(): programmer is not responding\n",
+	    progname);
+    exit(1);
+  }
+  return 0;
+}
+
+
+static int serjtag_drain(PROGRAMMER * pgm, int display)
+{
+  return serial_drain(&pgm->fd, display);
+}
+
+static int serjtag_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+
+  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  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);
+
+  return 0;
+}
+
+#if SERJTAG_DEBUG == 1
+static void  serjtag_check(PROGRAMMER * pgm) {
+  int i,bytes;
+  char buf[128];
+
+fprintf(stderr,"check response\n");
+fflush(stderr);
+  i=0;
+  buf[i++] = 'r'; 
+  buf[i++] = 0;
+  buf[i++] = 0;
+  serjtag_send(pgm, buf, i);
+  serjtag_recv(pgm, buf, 1);
+fprintf(stderr,"\tCommonad = 0x%02x %c\n",buf[0],buf[0]);
+  serjtag_recv(pgm, buf+1, 1);
+fprintf(stderr,"\tFlags = 0x%02x\n",buf[1]);
+  serjtag_recv(pgm, buf+2, 1);
+fprintf(stderr,"\tBytes = 0x%02x\n",buf[2]);
+  bytes = buf[2];
+  if (buf[1] & JTAG_BITS) bytes++;
+  if (bytes) {
+	fprintf(stderr,"\t");
+  	for (i=0; i<bytes; i++) {
+  		serjtag_recv(pgm, buf+2+i, 1);
+		fprintf(stderr,"%02x ",buf[2+i]);
+  	}
+	fprintf(stderr,"\n");
+  }
+}
+#endif
+
+static int serjtag_recv_j(PROGRAMMER * pgm, char * buf, size_t len, int verbose)
+{
+  int bytes;
+  serjtag_recv(pgm, buf, 3);	/* header */
+#if 0
+  if (verbose) {
+	fprintf(stderr,"recv_j(0) %c 0x%02x %d :",buf[0],buf[1],buf[2]);
+  }
+#endif
+  bytes = buf[2];
+  if (buf[1] & JTAG_BITS) {
+	  bytes++;
+  }
+  len -= 3;
+  if (len >= bytes) {
+    serjtag_recv(pgm, buf+3, bytes);
+  } else {
+	char tmp[1];
+	int i;
+  	serjtag_recv(pgm, buf+3, len);
+	for (i=0; i< bytes - len; i++) {
+  		serjtag_recv(pgm, tmp, 1);
+	}
+  }
+  if (verbose) {
+	int i;
+	fprintf(stderr,"recv_j %c 0x%02x %d :",buf[0],buf[1],buf[2]);
+	for (i=0; i<bytes; i++) {
+		fprintf(stderr,"%02x ",buf[3+i]&0xff);
+	}
+	fprintf(stderr,"\n");
+  }
+  return 0;
+}
+
+static int delay_param = 3;
+static int use_delay = JTAG_USE_DELAY;
+static unsigned char saved_signature[3];
+
+static void serjtag_set_delay(PROGRAMMER * pgm) {
+   use_delay = JTAG_USE_DELAY;
+  if (pgm->bitclock == 0.0) { // using default
+	delay_param = 3;
+  } else if (pgm->bitclock >= 4.0) {
+	delay_param = 0;
+	use_delay = 0;
+  } else {
+  	delay_param =  (int)((2.26/3.0) / pgm->bitclock);
+  	if (delay_param > 15) delay_param = 15;
+  }
+  if ((verbose>=1) || SERJTAG_DEBUG) {
+	fprintf(stderr," serjtag:delay %d (%s) ( bitclk %.3f )\n"
+		,delay_param, use_delay? "enabled":"disabled", pgm->bitclock);
+  }
+}
+
+/*
+ * issue the 'program enable' command to the AVR device
+ */
+static int serjtag_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+  char buf[128];
+  int i;
+  int retry_count = 0;
+
+   serjtag_set_delay(pgm);
+
+  serjtag_send(pgm, "j", 1); // enter jtag mode
+  serjtag_recv(pgm, buf, 1);
+  if (buf[0] != 'Y') {
+	  return -1;
+  }
+#if SERJTAG_DEBUG == 1
+fprintf(stderr," Enter jtag mode .. success \n");
+#endif
+
+retry:
+  i=0;
+  buf[i++] = 's';		/* Set Port */
+  buf[i++] = 0;			/* flags */
+  buf[i++] = 1;			/* bytes */
+  buf[i++] = 0;  /* TDI(MOSI) = 0, TMS(RESET) = 0, TCK(SCK) = 0 */
+  serjtag_send(pgm, buf, i);
+  usleep(5000); // 5ms
+  i=0;
+  buf[i++] = 's'; /* Set Port */
+  buf[i++] = 0;  /* flags */
+  buf[i++] = 1;   /* bytes */
+  buf[i++] = JTAG_SET_TMS;  /* TDI(MOSI) = 0, TMS(RESET) = 1, TCK(SCK) = 0 */
+  serjtag_send(pgm, buf, i);
+  usleep(5000); // 5ms
+  i=0;
+  buf[i++] = 's';		/* Set Port */
+  buf[i++] = 0;			/* flags */
+  buf[i++] = 1;			/* bytes */
+  buf[i++] = delay_param & JTAG_SET_DELAY;  /* TMS(RESET) = 0, set delay */
+  serjtag_send(pgm, buf, i);
+  usleep(5000); // 5ms
+
+  i = 0;
+  buf[i++] = 'd';               /* PUT TDI Stream */
+  buf[i++] = JTAG_RECIEVE | JTAG_USE_DELAY;
+  buf[i++] = 0;			/* bytes : set after*/
+  /* check */
+  buf[i++] = 0xAC;
+  buf[i++] = 0x53;
+  buf[i++] = 0;
+  buf[i++] = 0;
+  /* signature[0] */
+  buf[i++] = 0x30;
+  buf[i++] = 0;
+  buf[i++] = 0;
+  buf[i++] = 0;
+  /* signature[1] */
+  buf[i++] = 0x30;
+  buf[i++] = 0;
+  buf[i++] = 1;
+  buf[i++] = 0;
+  /* signature[2] */
+  buf[i++] = 0x30;
+  buf[i++] = 0;
+  buf[i++] = 2;
+  buf[i++] = 0;
+  buf[2] = i - 3; /* set bytes */
+  buf[i++] = 'r';                /* Request Recieved Data */
+  buf[i++] = 0; 		 /* flags */
+  buf[i++] = 0;			 /* bytes */
+  serjtag_send(pgm, buf, i);
+#if SERJTAG_DEBUG == 1
+  fprintf(stderr,"enabling program delay %d retry %d\n",delay_param,retry_count);
+#endif
+  serjtag_recv_j(pgm, buf, 3+4*4, SERJTAG_DEBUG?1:0);
+
+  saved_signature[0] = buf[3+4*1 +3];
+  saved_signature[1] = buf[3+4*2 +3];
+  saved_signature[2] = buf[3+4*3 +3];
+#if SERJTAG_DEBUG == 1
+  fprintf(stderr,"saved_signature %02x %02x %02x\n"
+		  , saved_signature[0]
+		  , saved_signature[1]
+		  , saved_signature[2]);
+#endif
+  if ((buf[3+2] == 0x53) && (saved_signature[0] == 0x1e)) // success
+  {
+	  return 0;
+  }
+  if (retry_count < 5) {
+	  retry_count++;
+	  goto retry;
+  }
+  return -1;
+}
+
+static int serjtag_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
+{
+   m->buf[0] = saved_signature[0];
+   m->buf[1] = saved_signature[1];
+   m->buf[2] = saved_signature[2];
+   return 3;
+}
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+static int serjtag_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  char id[8];
+  char sw[3];
+
+  /* Get the programmer identifier. Programmer returns exactly 7 chars
+     _without_ the null.*/
+
+  serjtag_send(pgm, "S", 1);
+  serjtag_recv(pgm, id, 7);
+  id[7] = 0;
+
+  /* Get the HW and SW versions to see if the programmer is present. */
+
+  serjtag_send(pgm, "V", 1);
+  serjtag_recv(pgm, sw, 2);
+  sw[2] = 0;
+
+  fprintf(stderr, "Found programmer: Id = \"%s\"; Revison = %s\n", id, sw);
+
+  if (strncmp(id, "SERJTAG", 7) && strncmp(id, "USB910A", 7)
+       && strncmp(id, "USB910A", 7) )
+  {
+     fprintf(stderr, "\tserjtag protocol not supported.\n");
+     exit(1);
+  }
+  return serjtag_program_enable(pgm, p);
+}
+
+static void serjtag_disable(PROGRAMMER * pgm)
+{
+  char buf[2];
+  serjtag_send(pgm, "V", 1);
+  serjtag_recv(pgm, buf, 2);
+
+  return;
+}
+
+
+static void serjtag_enable(PROGRAMMER * pgm)
+{
+  /* Do nothing. */
+
+  return;
+}
+
+
+/*
+ * transmit an AVR device command and return the results; 'cmd' and
+ * 'res' must point to at least a 4 byte data buffer
+ */
+static int serjtag_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                      unsigned char res[4])
+{
+  char buf[10];
+
+  /* FIXME: Insert version check here */
+
+  buf[0] = 'd';                 /* PUT TDI Stream */
+  buf[1] = JTAG_RECIEVE | use_delay;
+  buf[2] = 4;			/* bytes */
+  buf[3] = cmd[0];
+  buf[4] = cmd[1];
+  buf[5] = cmd[2];
+  buf[6] = cmd[3];
+  buf[7] = 'r';                 /* Request Recieved Data */
+  buf[8] = 0;
+  buf[9] = 0;
+
+  serjtag_send (pgm, buf, 10);
+  serjtag_recv (pgm, buf, 7);
+
+  res[0] = 0x00;                /* Dummy value */
+  res[1] = cmd[0];
+  res[2] = cmd[1];
+  res[3] = buf[6];
+
+  return 0;
+}
+
+
+static int serjtag_open(PROGRAMMER * pgm, char * port)
+{
+  /*
+   *  If baudrate was not specified use 19.200 Baud
+   */
+  if(pgm->baudrate == 0) {
+    pgm->baudrate = 19200;
+  }
+
+  strcpy(pgm->port, port);
+  serial_open(port, pgm->baudrate, &pgm->fd);
+
+  /*
+   * drain any extraneous input
+   */
+  serjtag_drain (pgm, 0);
+	
+  return 0;
+}
+
+static void serjtag_close(PROGRAMMER * pgm)
+{
+  serial_close(&pgm->fd);
+  pgm->fd.ifd = -1;
+}
+
+
+static void serjtag_display(PROGRAMMER * pgm, const char * p)
+{
+  return;
+}
+
+static int serjtag_paged_write_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				   unsigned int page_size, unsigned int addr, 
+				   unsigned int n_bytes)
+{
+  unsigned long i, pa;
+  int rc;
+
+  for (i=0; i<n_bytes; i++, addr++) {
+    rc = avr_write_byte_default(pgm, p, m, addr, m->buf[addr]);
+    if (rc != 0) {
+      return -2;
+    }
+
+    if (m->paged) {
+      // Can this piece of code ever be activated?? Do AVRs exist that
+      // have paged non-flash memories? -- REW
+      // XXX Untested code below. 
+      
+      /*
+       * check to see if it is time to flush the page with a page
+       * write
+       */
+      if (((addr % m->page_size) == m->page_size-1) || (i == n_bytes-1)) {
+	pa = addr - (addr % m->page_size);
+        rc = avr_write_page(pgm, p, m, pa);
+        if (rc != 0) {
+	  return -2;
+        }
+      }
+    }
+  }
+  return i;
+}
+
+#define SERJTAG_BUFFERD_WRITE
+
+#ifdef SERJTAG_BUFFERD_WRITE
+#define SERJTAG_BUF_SIZE	1024
+unsigned char *serjtag_buf;
+int serjtag_buf_len;
+
+static void serjtag_flush(PROGRAMMER * pgm) {
+   if (!serjtag_buf || (serjtag_buf_len == 0)) return;
+   serjtag_send(pgm, serjtag_buf, serjtag_buf_len);
+   serjtag_buf_len = 0;
+}
+
+static void serjtag_write(PROGRAMMER * pgm, unsigned char *buf, int size) {
+  if (!serjtag_buf) {
+     serjtag_buf = malloc(SERJTAG_BUF_SIZE);
+     if (!serjtag_buf) {
+       fprintf(stderr, "can't alloc memory\n");
+       exit(1);
+     }
+  }
+  if (SERJTAG_BUF_SIZE < serjtag_buf_len + size) {
+      serjtag_flush(pgm);
+  }
+  memcpy(serjtag_buf + serjtag_buf_len, buf, size);
+  serjtag_buf_len += size;
+}
+#else
+static void serjtag_flush(PROGRAMMER * pgm) {
+}
+static void serjtag_write(PROGRAMMER * pgm, unsigned char *buf, int size) {
+   serjtag_send(pgm, buf, size);
+}
+#endif
+
+#define USE_INLINE_WRITE_PAGE
+
+static int serjtag_paged_write_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+				     unsigned int page_size, unsigned int addr, 
+				     unsigned int n_bytes)
+{
+  unsigned int    i,j;
+  int addr_save,buf_pos,do_page_write;
+  char buf[128];
+
+  //addr = 0;
+  for (i=0; i<n_bytes; ) {
+     addr_save = addr;
+     buf_pos = 0;
+     buf[buf_pos++] = 'd';               /* PUT TDI Stream */
+     buf[buf_pos++] = use_delay; 
+     buf[buf_pos++] = 0; /* bytes : set after */
+     do_page_write = 0;
+     for (j=0; j<(JTAG_BUFSIZE-3)/4; j++) { // 15 bytes
+        buf[buf_pos++] = (addr & 1)?0x48:0x40;
+        buf[buf_pos++] = (addr >> 9) & 0xff;
+        buf[buf_pos++] = (addr >> 1) & 0xff;
+        buf[buf_pos++] = m->buf[addr];
+	addr++;
+	i++;
+	if ( (m->paged) &&
+             (((addr % m->page_size) == 0) || (i == n_bytes))) {
+		do_page_write = 1;
+		break;
+	}
+     }
+     buf[2] = buf_pos - 3;
+#ifdef USE_INLINE_WRITE_PAGE
+     if (do_page_write) {
+	int addr_wk = addr_save - (addr_save % m->page_size);
+        /* If this device has a "load extended address" command, issue it. */
+	if (m->op[AVR_OP_LOAD_EXT_ADDR]) {
+	    OPCODE *lext = m->op[AVR_OP_LOAD_EXT_ADDR];
+
+            buf[buf_pos++] = 'd';               /* PUT TDI Stream */
+            buf[buf_pos++] = JTAG_RECIEVE | use_delay; 
+            buf[buf_pos++] = 4; /* bytes */
+
+	    memset(buf+buf_pos, 0, 4);
+	    avr_set_bits(lext, buf+buf_pos);
+	    avr_set_addr(lext, buf+buf_pos, addr/2);
+            buf_pos += 4;
+	}
+        buf[buf_pos++] = 'd';               /* PUT TDI Stream */
+        buf[buf_pos++] = JTAG_RECIEVE | use_delay; 
+        buf[buf_pos++] = 4; /* bytes */
+        buf[buf_pos++] = 0x4C; /* Issue Page Write */
+        buf[buf_pos++] = (addr_wk >> 9) & 0xff;
+        buf[buf_pos++] = (addr_wk >> 1) & 0xff;
+        buf[buf_pos++] = 0;
+        buf[buf_pos++] = 'r';                /* Request Recieved Data */
+        buf[buf_pos++] = 0;
+        buf[buf_pos++] = 0;
+     }
+#endif
+     serjtag_write(pgm, buf, buf_pos);
+#if 0
+fprintf(stderr, "send addr 0x%04x size %d bufsize %d i %d page_write %d\n",
+		addr_wk,buf[2],buf_pos,i,do_page_write);
+#endif
+     if (do_page_write) {
+#ifdef USE_INLINE_WRITE_PAGE
+        serjtag_flush(pgm);
+        usleep(m->max_write_delay);
+        serjtag_recv_j(pgm, buf, 4 + 3, 0);
+#else
+        int rc;
+        serjtag_flush(pgm);
+	addr_wk = (addr-1) / m->page_size * m->page_size;
+        rc = avr_write_page(pgm, p, m, addr_wk);
+        if (rc != 0) {
+	  return -2;
+        }
+#endif
+     }
+  }
+  serjtag_flush(pgm);
+  return i;
+}
+
+
+static int serjtag_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+			       unsigned int page_size, unsigned int addr, 
+			       unsigned int n_bytes)
+{
+  if (strcmp(m->desc, "flash") == 0) {
+    return serjtag_paged_write_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return serjtag_paged_write_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+
+static int serjtag_paged_load_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				  unsigned int page_size, unsigned int addr, 
+				  unsigned int n_bytes)
+{
+  unsigned char    rbyte;
+  unsigned long    i;
+  int rc;
+
+  for (i=0; i<n_bytes; i++) {
+     rc = avr_read_byte_default(pgm, p, m, addr + i, &rbyte);
+     if (rc != 0) {
+       return -2;
+     }
+     m->buf[addr+i] = rbyte;
+  }
+  return 0;
+}
+
+
+static struct serjtag_request {
+	int addr;
+	int bytes;
+	int n;
+	struct serjtag_request *next;
+} *req_head,*req_tail,*req_pool;
+
+static void put_request(int addr, int bytes, int n)
+{
+  struct serjtag_request *p;
+  if (req_pool) {
+     p = req_pool;
+     req_pool = p->next;
+  } else {
+     p = malloc(sizeof(struct serjtag_request));
+     if (!p) {
+       fprintf(stderr, "can't alloc memory\n");
+       exit(1);
+     }
+  }
+  memset(p, 0, sizeof(struct serjtag_request));
+  p->addr = addr;
+  p->bytes = bytes;
+  p->n = n;
+  if (req_tail) {
+     req_tail->next = p;
+     req_tail = p;
+  } else {
+     req_head = req_tail = p;
+  }
+}
+
+static int do_request(PROGRAMMER * pgm, AVRMEM *m)
+{
+  struct serjtag_request *p;
+  int addr, bytes, j, n;
+  char buf[128];
+
+  if (!req_head) return 0;
+  p = req_head;
+  req_head = p->next;
+  if (!req_head) req_tail = req_head;
+
+  addr = p->addr;
+  bytes = p->bytes;
+  n = p->n;
+  memset(p, 0, sizeof(struct serjtag_request));
+  p->next = req_pool;
+  req_pool = p;
+
+  serjtag_recv_j(pgm, buf, bytes + 3, 0);
+  for (j=0; j<n; j++) {
+     m->buf[addr++] = buf[3 + 4*j + 3];
+  }
+  return 1;
+}
+
+#define REQ_OUTSTANDINGS	10
+static int serjtag_paged_load_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				    unsigned int page_size, unsigned int addr, 
+				    unsigned int n_bytes)
+{
+  unsigned long    i,j,n;
+  //int rc;
+  int addr_save,bytes,buf_pos;
+  int req_count = 0;
+  char buf[128];
+
+  for (i=0; i<n_bytes; ) {
+     buf_pos = 0;
+     buf[buf_pos++] = 'd';               /* PUT TDI Stream */
+     buf[buf_pos++] = JTAG_RECIEVE | use_delay; 
+     buf[buf_pos++] = 0; /* bytes : set after */
+     addr_save = addr;
+     for (j=0; j<(JTAG_BUFSIZE-3*2)/4; j++) { // 14 bytes
+	if (i >= n_bytes) break;
+        buf[buf_pos++] = (addr & 1)?0x28:0x20;
+        buf[buf_pos++] = (addr >> 9) & 0xff;
+        buf[buf_pos++] = (addr >> 1) & 0xff;
+        buf[buf_pos++] = 0;
+	addr ++;
+	i++;
+     }
+     n = j;
+     buf[2] = bytes =  buf_pos - 3;
+     buf[buf_pos++] = 'r';                /* Request Recieved Data */
+     buf[buf_pos++] = 0;
+     buf[buf_pos++] = 0;
+     serjtag_send(pgm, buf, buf_pos);
+     put_request(addr_save, bytes, n);
+     req_count++;
+     if (req_count > REQ_OUTSTANDINGS)
+        do_request(pgm, m);
+
+  }
+  while (do_request(pgm, m))
+	  ;
+  return 0;
+}
+
+static int serjtag_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+			      unsigned int page_size, unsigned int addr,
+			      unsigned int n_bytes)
+{
+  if (strcmp(m->desc, "flash") == 0) {
+    return serjtag_paged_load_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return serjtag_paged_load_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+void serjtag_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "serjtag");
+
+  /*
+   * mandatory functions
+   */
+  pgm->initialize     = serjtag_initialize;
+  pgm->display        = serjtag_display;
+  pgm->enable         = serjtag_enable;
+  pgm->disable        = serjtag_disable;
+  pgm->program_enable = serjtag_program_enable;
+  pgm->chip_erase     = serjtag_chip_erase;
+  pgm->cmd            = serjtag_cmd;
+  pgm->open           = serjtag_open;
+  pgm->close          = serjtag_close;
+  pgm->read_byte      = avr_read_byte_default;
+  pgm->write_byte     = avr_write_byte_default;
+
+  /*
+   * optional functions
+   */
+
+  pgm->paged_write = serjtag_paged_write;
+  pgm->paged_load = serjtag_paged_load;
+  pgm->read_sig_bytes = serjtag_read_sig_bytes;
+}
Index: serjtag.h
===================================================================
--- serjtag.h	(revision 0)
+++ serjtag.h	(revision 0)
@@ -0,0 +1,22 @@
+#ifndef serjtag_h
+#define serjtag_h
+
+#include "avrpart.h"
+
+#define JTAG_BUFSIZE	64
+
+/* flags */
+#define JTAG_RECIEVE	0x80
+#define JTAG_TMS_HIGH	0x40
+#define JTAG_USE_DELAY	0x10
+#define JTAG_BITS	0x7
+
+/* 's' command (JTAG_SET) flags */
+#define JTAG_SET_TDI	0x80
+#define JTAG_SET_TMS	0x40
+#define JTAG_SET_TCK	0x20
+#define JTAG_SET_DELAY	0x0f
+
+void serjtag_initpgm (PROGRAMMER * pgm);
+
+#endif /* serjtag_h */
Index: configure.ac
===================================================================
--- configure.ac	(revision 1039)
+++ configure.ac	(working copy)
@@ -107,7 +107,7 @@
             [Define if FTDI support is enabled via libftdi])
 AC_CHECK_LIB([ftdi], [ftdi_usb_get_strings], [have_libftdi=yes], [], [-lusb])
 if test x$have_libftdi = xyes; then
-   LIBFTDI="-lftdi -lusb"
+   LIBFTDI="-lftdi -lusb -lpthread"
    AC_DEFINE([HAVE_LIBFTDI])
 fi
 AC_SUBST(LIBFTDI, $LIBFTDI)
Index: ft245r.c
===================================================================
--- ft245r.c	(revision 0)
+++ ft245r.c	(revision 0)
@@ -0,0 +1,951 @@
+
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004  Theodore A. Roth  <troth@openavr.org>
+ * some code:
+ * Copyright (C) 2011-2012 Roger E. Wolff <R.E.Wolff@BitWizard.nl> 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/* $Id$ */
+
+/* ft245r -- FT245R/FT232R Synchronous BitBangMode Programmer
+  default pin assign 
+               FT232R / FT245R
+  miso  = 1;  # RxD   / D1
+  sck   = 0;  # RTS   / D0
+  mosi  = 2;  # TxD   / D2
+  reset = 4;  # DTR   / D4
+*/
+
+/*
+  The ft232r is very similar, or even "identical" in the synchronous
+  bitbang mode that we use here.
+
+  This allows boards that have an ft232r for communication and an avr
+  as the processor to function as their own "ICSP". Boards that fit
+  this description include the Arduino Duemilanove, Arduino Diecimila,
+  Arduino NG (http://arduino.cc/it/main/boards) and the BitWizard
+  ftdi_atmega board (http://www.bitwizard.nl/wiki/index.php/FTDI_ATmega)
+
+  The Arduinos have to be patched to bring some of the control lines
+  to the ICSP header. The BitWizard board already has the neccessary
+  wiring on the PCB. 
+
+  How to add the wires to an arduino is documented here: 
+  http://www.geocities.jp/arduino_diecimila/bootloader/index_en.html
+*/
+
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "avr.h"
+#include "pindefs.h"
+#include "pgm.h"
+#include "bitbang.h"
+#include "ft245r.h"
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#define SUPPORT_FT245R
+
+#if defined(_WIN32) || defined(SUPPORT_FT245R)
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include <ftdi.h>
+
+
+#define FT245R_CYCLES	2
+#define FT245R_FRAGMENT_SIZE  512
+#define REQ_OUTSTANDINGS	10
+//#define USE_INLINE_WRITE_PAGE
+
+#define FT245R_DEBUG	1
+
+extern char * progname;
+extern int do_cycles;
+extern int ovsigck;
+extern int verbose;
+
+static struct ftdi_context *handle;
+
+static unsigned char ft245r_ddr;
+static unsigned char ft245r_sck;
+static unsigned char ft245r_mosi;
+static unsigned char ft245r_reset;
+static unsigned char ft245r_miso;
+
+
+#define BUFSIZE 0x2000
+
+// libftdi / libftd2xx compatibility functions. 
+
+pthread_t readerthread, debugthread;
+sem_t buf_data, buf_space;
+char buffer[BUFSIZE];
+int head, tail;
+
+void add_to_buf (c)
+{
+  int nh;
+
+  sem_wait (&buf_space);
+  if (head == (BUFSIZE -1)) nh = 0;
+  else                      nh = head + 1;
+
+  if (nh == tail) {
+    fprintf (stderr, "buffer overflow. Cannot happen!\n");
+    //exit (1);
+  }
+  buffer[head] = c;
+  head = nh;  
+  sem_post (&buf_data);
+}
+
+
+
+void *reader (void *arg)
+{
+  struct ftdi_context *handle = (struct ftdi_context *)(arg);
+  unsigned char buf[0x1000];
+  int br, i;
+
+  //printf ("reader starting %p\n", handle);
+
+  while (1) {
+    //printf ("startin ftdi_read\n");
+    br = ftdi_read_data (handle, buf, 0x1000);
+    //printf ("R%d ", br);fflush (stdout);
+    for (i=0;i<br;i++) 
+      add_to_buf (buf[i]);
+  }
+}
+
+#if 0
+void *debugger (void *arg)
+{
+  while (1) {
+    printf ("Head=%d, tail=%d.\n", head, tail);
+    usleep (100000);
+  }
+}
+#endif
+
+
+
+
+
+
+static inline void setmybit(unsigned char *data, int pinno, int v)
+{
+	if (v) {
+		*data |= (1 << (pinno));
+	} else {
+		*data &= ~(1 <<(pinno));
+	}
+}
+
+
+
+static int ft245r_send(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  int rv;
+
+  //printf ("W%d ", (int)len); fflush (stdout);
+  rv = ftdi_write_data(handle, buf, len);
+  //if (rv < 0) return -1; // is included below
+  if (len != rv) return -1;
+  return 0;
+}
+
+
+#if 0
+static inline int FT_Read (struct ftdi_context *handle, void *buf, int size, 
+			   unsigned int *bytesread)
+{
+  int i;
+  unsigned char *cbuf = buf;
+
+  for (i=0;i<size;i++) {
+    sem_wait (&buf_data);
+    cbuf[i] = buffer[tail];
+    if (tail == (BUFSIZE -1)) tail = 0;
+    else                      tail++;
+    sem_post (&buf_space);
+  }
+
+  //printf ("r%d ", i, size);
+  *bytesread = i;
+  return 0;
+}
+#endif
+
+static int ft245r_recv(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  int i;
+
+  // Copy over data from the circular buffer.. 
+  // XXX This should timeout, and return error if there isn't enough
+  // data. 
+  for (i=0;i<len;i++) {
+    sem_wait (&buf_data);
+    buf[i] = buffer[tail];
+    if (tail == (BUFSIZE -1)) tail = 0;
+    else                      tail++;
+    sem_post (&buf_space);
+  }
+
+  return 0;
+}
+
+
+static int ft245r_drain(PROGRAMMER * pgm, int display)
+{
+  int r;
+  unsigned char t;
+
+  //printf ("Drain!\n");
+  // flush the buffer in the chip by changing the mode..... 
+  r = ftdi_set_bitmode(handle, 0, BITMODE_RESET); 	// reset 
+  if (r) return -1;
+  r = ftdi_set_bitmode(handle, ft245r_ddr, BITMODE_SYNCBB); // set Synchronuse BitBang
+  if (r) return -1;
+
+  // drain our buffer. 
+  while (head != tail) {
+    ft245r_recv (pgm, &t, 1);
+  }
+#if 0
+  r = FT_GetQueueStatushandle, &n);
+  if (r != FT_OK) return -1;
+  if (n) {
+	fprintf(stderr, "ft245r_drain called but queue is not empty %d \n",
+		(int)n);
+  }
+#endif
+  return 0;
+}
+
+static inline int ft245r_sync(PROGRAMMER * pgm)
+{
+  //printf ("sync.\n");
+  // The old code did something that evaluated to a no-op.
+
+  return 0;
+}
+
+static int ft245r_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+
+  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  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);
+
+  return 0;
+}
+
+static unsigned char saved_signature[3];
+
+#if 0
+static int valid_rates[] = {
+   2400, 4800, 9600, 14400, 19200, 38400, 57600, 
+   115200, 230400, 460800, 921600, 3000000
+};
+#endif
+
+static void ft245r_set_bitclock(PROGRAMMER * pgm) 
+{
+  int r;
+  int rate = 0;
+
+#if 0
+  int i;
+
+  if (pgm->bitclock == 0.0) { // using default
+	  rate = 235000.0 /2; 
+  } else if (pgm->bitclock >= 0.50 ) {
+	  rate = 500000.0 /2;
+  } else if (pgm->bitclock <  0.01) {
+	  rate =   1000.0 /2;
+  } else {
+	  rate = pgm->bitclock * 1000000.0 /2;
+  }
+  for (i= sizeof(valid_rates)/sizeof(valid_rates[0]) -1; i>=0; --i) 
+  {
+    if (valid_rates[i] <= rate) {
+		rate = valid_rates[i];
+		break;
+    }
+  }
+  if (i<0) rate = valid_rates[0];
+#else
+  rate = pgm->bitclock * 1000000 / 2;
+#endif
+
+  //printf ("Set baudrate %d\n", rate);
+  r = ftdi_set_baudrate(handle, rate);
+  if (r) {
+    fprintf (stderr, "Set baudrate (%d) failed with error '%s'.\n", 
+	    rate, ftdi_get_error_string (handle));
+    exit (1);
+  }
+  if ((verbose>=1) || FT245R_DEBUG) {
+	fprintf(stderr," ft245r:  bitclk %d -> ft baud %d\n", 
+			rate * 2, rate);
+  }
+}
+
+static int set_reset(PROGRAMMER * pgm, int val)
+{
+  unsigned char buf[1];
+
+  buf[0] = 0;
+  if (val) buf[0] |= ft245r_reset;
+
+  ft245r_send (pgm, buf, 1);
+  ft245r_recv (pgm, buf, 1);
+  return 0;
+}
+
+static int ft245r_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                      unsigned char res[4]);
+/*
+ * issue the 'program enable' command to the AVR device
+ */
+static int ft245r_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+  int retry_count = 0;
+  unsigned char cmd[4];
+  unsigned char res[4];
+  int i,reset_ok;
+
+  ft245r_set_bitclock(pgm);
+
+retry:
+  reset_ok = 0;
+  set_reset(pgm, 0);
+  usleep(5000); // 5ms
+  set_reset(pgm, 1);
+  usleep(5000); // 5ms
+  set_reset(pgm, 0);
+  usleep(5000); // 5ms
+
+  cmd[0] = 0xAC;
+  cmd[1] = 0x53;
+  cmd[2] = 0;
+  cmd[3] = 0;
+  ft245r_cmd(pgm, cmd, res); 
+  if (res[2] == 0x53 ) reset_ok = 1;
+  for (i=0; i<3; i++) {
+     cmd[0] = 0x30;
+     cmd[1] = 0;
+     cmd[2] = i;
+     cmd[3] = 0;
+     ft245r_cmd(pgm, cmd, res); 
+     saved_signature[i] = res[3];
+  }
+  //printf ("ht=(%d,%d)\n", head, tail);
+  if (reset_ok && (saved_signature[0] == 0x1e)) // success
+     return 0;
+
+  if (retry_count < 5) {
+    // printf ("retrying %d (got %02x)\n", retry_count, saved_signature[0]);
+    if (retry_count == 3) {
+      // printf ("ht=(%d,%d)\n", head, tail);
+      ft245r_drain (pgm, 0);
+      tail = head;
+    }
+	  retry_count++;
+	  goto retry;
+  }
+  if ((verbose>=1) || FT245R_DEBUG) {
+     fprintf(stderr,
+	    "%s: ft245r_program_enable: failed\n", progname);
+     fflush(stderr);
+  }
+  return -1;
+}
+
+static int ft245r_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
+{
+   m->buf[0] = saved_signature[0];
+   m->buf[1] = saved_signature[1];
+   m->buf[2] = saved_signature[2];
+   return 3;
+}
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+static int ft245r_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  return ft245r_program_enable(pgm, p);
+}
+
+static void ft245r_disable(PROGRAMMER * pgm)
+{
+  return;
+}
+
+
+static void ft245r_enable(PROGRAMMER * pgm)
+{
+  /* Do nothing. */
+
+  return;
+}
+
+static inline int set_data(unsigned char *buf, unsigned char data)
+{
+    int j;
+    int buf_pos = 0;
+    unsigned char bit = 0x80;
+
+    for (j=0; j<8; j++) {
+    	buf[buf_pos] = 0;
+	if (data & bit) buf[buf_pos] |= ft245r_mosi;
+	buf_pos++;
+
+    	buf[buf_pos] = 0;
+	if (data & bit) buf[buf_pos] |= ft245r_mosi;
+    	buf[buf_pos] |= ft245r_sck;
+	buf_pos++;
+
+	bit >>= 1;
+    }
+    return buf_pos;
+}
+
+static inline unsigned char extract_data(unsigned char *buf, int offset)
+{
+    int j;
+    int buf_pos = 1;
+    unsigned char bit = 0x80;
+    unsigned char r = 0;
+
+    buf += offset * (8 * FT245R_CYCLES);
+    for (j=0; j<8; j++) {
+	if (buf[buf_pos] & ft245r_miso) {
+		  r |= bit;
+	}
+	buf_pos += FT245R_CYCLES;
+	bit >>= 1;
+    }
+    return r;
+}
+
+/* to check data */
+static inline unsigned char extract_data_out(unsigned char *buf, int offset)
+{
+    int j;
+    int buf_pos = 1;
+    unsigned char bit = 0x80;
+    unsigned char r = 0;
+
+    buf += offset * (8 * FT245R_CYCLES);
+    for (j=0; j<8; j++) {
+	if (buf[buf_pos] & ft245r_mosi) {
+		  r |= bit;
+	}
+	buf_pos += FT245R_CYCLES;
+	bit >>= 1;
+    }
+    return r;
+}
+
+ 
+/*
+ * transmit an AVR device command and return the results; 'cmd' and
+ * 'res' must point to at least a 4 byte data buffer
+ */
+static int ft245r_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                      unsigned char res[4])
+{
+  int i,buf_pos;
+  unsigned char buf[128];
+
+  buf_pos = 0;
+  for (i=0; i<4; i++) {
+     buf_pos += set_data(buf+buf_pos, cmd[i]);
+  }
+  buf[buf_pos] = 0;
+  buf_pos++;
+
+  ft245r_send (pgm, buf, buf_pos);
+  ft245r_recv (pgm, buf, buf_pos);
+  res[0] = extract_data(buf, 0);
+  res[1] = extract_data(buf, 1);
+  res[2] = extract_data(buf, 2);
+  res[3] = extract_data(buf, 3);
+
+  return 0;
+}
+
+
+static int ft245r_open(PROGRAMMER * pgm, char * port)
+{
+  int rv;
+  int devnum = -1;
+  strcpy(pgm->port, port);
+
+#ifdef _WIN32
+  if (!strncasecmp("ft", port, 2) && '0' <= port[2] && port[2] <= '9') {
+     devnum = port[2] - '0';
+  } else {
+    fprintf(stderr,
+	    "%s: invalid portname  %s: use ft0 - ft9\n",
+	    progname,port);
+    exit(1);
+  }
+  //rv = ftdi_usb_open_sec_index(devnum, &handle);
+
+#else
+  //printf ("Going to open.... \n");
+  // XXX figure out a devnum!!!
+  devnum = 0;
+
+#endif
+
+  handle = malloc (sizeof (struct ftdi_context));
+  ftdi_init(handle);
+  rv = ftdi_usb_open_desc_index(handle, 0x0403, 0x6001, NULL, NULL, devnum);
+  if (rv) {
+    free (handle);
+    fprintf (stderr, "can't open ftdi device %d. \n", devnum);
+    exit (1);
+  }
+
+
+  /* We start a new thread to read the output from the FTDI. This is
+   * necessary because otherwise we'll deadlock. We cannot finish
+   * writing because the ftdi cannot send the results because we 
+   * haven't provided a read buffer yet. */ 
+
+  sem_init (&buf_data, 0, 0);
+  sem_init (&buf_space, 0, BUFSIZE);
+  pthread_create (&readerthread, NULL, reader, handle);
+
+  //pthread_create (&debugthread, NULL, debugger, NULL);
+  //printf ("thread created %p\n", handle);
+
+  //printf ("Bitmode 4\n");
+  rv = ftdi_set_bitmode(handle, 0, BITMODE_SYNCBB); // set Synchronous BitBang
+
+  if (rv) {
+    fprintf(stderr,
+	    "%s: Synchronous BitBangMode is not supported\n",
+	    progname);
+     exit(1);
+  }
+#if FT245R_DEBUG
+  fprintf(stderr, "%s: BitBang OK \n", progname);
+  fflush(stderr);
+#endif
+
+  ft245r_ddr = 0;
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_SCK], 1);
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_MOSI], 1);
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_RESET], 1);
+  ft245r_sck = 0;
+  setmybit(&ft245r_sck, pgm->pinno[PIN_AVR_SCK], 1);
+  ft245r_mosi = 0;
+  setmybit(&ft245r_mosi, pgm->pinno[PIN_AVR_MOSI], 1);
+  ft245r_reset = 0;
+  setmybit(&ft245r_reset, pgm->pinno[PIN_AVR_RESET], 1);
+  ft245r_miso = 0;
+  setmybit(&ft245r_miso, pgm->pinno[PIN_AVR_MISO], 1);
+  if ((verbose>=1) || FT245R_DEBUG) {
+    fprintf(stderr,
+	    "%s: pin assign miso %d sck %d mosi %d reset %d\n",
+	      progname,
+              pgm->pinno[PIN_AVR_MISO],
+              pgm->pinno[PIN_AVR_SCK],
+              pgm->pinno[PIN_AVR_MOSI],
+              pgm->pinno[PIN_AVR_RESET]);
+  }
+
+  /*
+   * drain any extraneous input
+   */
+  ft245r_drain (pgm, 0);
+#if FT245R_DEBUG
+  fprintf(stderr, "%s: drain OK \n", progname);
+  fflush(stderr);
+#endif
+	
+  return 0;
+}
+
+
+static void ft245r_close(PROGRAMMER * pgm)
+{
+  //printf ("Closing... \n");
+
+  // I think the switch to BB mode and back flushes the buffer. 
+  ftdi_set_bitmode(handle, 0, BITMODE_SYNCBB); // set Synchronous BitBang, all in puts
+  ftdi_set_bitmode(handle, 0, BITMODE_RESET); // disable Synchronous BitBang
+  ftdi_usb_close(handle);  
+}
+
+static void ft245r_display(PROGRAMMER * pgm, const char * p)
+{
+  return;
+}
+
+
+static int ft245r_paged_write_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				  unsigned int page_size, unsigned int addr,  
+				  unsigned int n_bytes)
+{
+  unsigned long i, pa;
+  int rc;
+
+  //printf ("paged_write_gen: %x %x %x (%d)\n", 
+  //  page_size, addr, n_bytes, m->paged);
+  
+  for (i=0; i<n_bytes; i++, addr++) {
+    rc = avr_write_byte_default(pgm, p, m, addr, m->buf[addr]);
+    if (rc != 0) {
+      return -2;
+    }
+
+    if (m->paged) {
+      // Can this piece of code ever be activated?? Do AVRs exist that
+      // have paged non-flash memories? -- REW
+      // XXX Untested code below. 
+      /*
+       * check to see if it is time to flush the page with a page
+       * write
+       */
+      
+      if (((addr % m->page_size) == m->page_size-1) || (i == n_bytes-1)) {
+	pa = addr - (addr % m->page_size);
+
+	// printf ("writing page %x/%x\n", i, pa);
+        rc = avr_write_page(pgm, p, m, pa);
+        if (rc != 0) {
+	  return -2;
+        }
+      }
+    }
+  }
+  return i;
+}
+
+
+static struct ft245r_request {
+	int addr;
+	int bytes;
+	int n;
+	struct ft245r_request *next;
+} *req_head,*req_tail,*req_pool;
+
+static void put_request(int addr, int bytes, int n)
+{
+  struct ft245r_request *p;
+  if (req_pool) {
+     p = req_pool;
+     req_pool = p->next;
+  } else {
+     p = malloc(sizeof(struct ft245r_request));
+     if (!p) {
+       fprintf(stderr, "can't alloc memory\n");
+       exit(1);
+     }
+  }
+  memset(p, 0, sizeof(struct ft245r_request));
+  p->addr = addr;
+  p->bytes = bytes;
+  p->n = n;
+  if (req_tail) {
+     req_tail->next = p;
+     req_tail = p;
+  } else {
+     req_head = req_tail = p;
+  }
+}
+
+static int do_request(PROGRAMMER * pgm, AVRMEM *m)
+{
+  struct ft245r_request *p;
+  int addr, bytes, j, n;
+  char buf[FT245R_FRAGMENT_SIZE+1+128];
+
+  if (!req_head) return 0;
+  p = req_head;
+  req_head = p->next;
+  if (!req_head) req_tail = req_head;
+
+  addr = p->addr;
+  bytes = p->bytes;
+  n = p->n;
+  memset(p, 0, sizeof(struct ft245r_request));
+  p->next = req_pool;
+  req_pool = p;
+
+  ft245r_recv(pgm, buf, bytes);
+  for (j=0; j<n; j++) {
+     m->buf[addr++] = extract_data(buf , (j * 4 + 3));
+  }
+#if 0
+if (n == 0) // paged_write
+fprintf(stderr, "recv addr 0x%04x buf size %d \n",addr, bytes);
+#endif
+  return 1;
+}
+
+static int ft245r_paged_write_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                                    int page_size, int addr, int n_bytes)
+{
+  unsigned int    i,j;
+  int addr_save,buf_pos,do_page_write,req_count;
+  char buf[FT245R_FRAGMENT_SIZE+1+128];
+
+  //printf ("paged_write_flash: %x %x %x\n", page_size, addr, n_bytes);
+
+  req_count = 0;
+  //  addr = 0;
+  for (i=0; i<n_bytes; ) {
+     addr_save = addr;
+     buf_pos = 0;
+     do_page_write = 0;
+     for (j=0; j< FT245R_FRAGMENT_SIZE/8/FT245R_CYCLES/4; j++) {
+        buf_pos += set_data(buf+buf_pos, (addr & 1)?0x48:0x40 ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 9) & 0xff ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 1) & 0xff );
+        buf_pos += set_data(buf+buf_pos, m->buf[addr]);
+	addr ++;
+	i++;
+	if ( (m->paged) &&
+             (((i % m->page_size) == 0) || (i == n_bytes))) {
+		do_page_write = 1;
+		break;
+	}
+     }
+#if defined(USE_INLINE_WRITE_PAGE)
+     if (do_page_write) {
+        int addr_wk = addr_save - (addr_save % m->page_size);
+        /* If this device has a "load extended address" command, issue it. */
+	if (m->op[AVR_OP_LOAD_EXT_ADDR]) {
+	    unsigned char cmd[4];
+	    OPCODE *lext = m->op[AVR_OP_LOAD_EXT_ADDR];
+
+	    memset(cmd, 0, 4);
+	    avr_set_bits(lext, cmd);
+	    avr_set_addr(lext, cmd, addr_wk/2);
+            buf_pos += set_data(buf+buf_pos, cmd[0]);
+            buf_pos += set_data(buf+buf_pos, cmd[1]);
+            buf_pos += set_data(buf+buf_pos, cmd[2]);
+            buf_pos += set_data(buf+buf_pos, cmd[3]);
+	}
+        buf_pos += set_data(buf+buf_pos, 0x4C); /* Issue Page Write */
+        buf_pos += set_data(buf+buf_pos,(addr_wk >> 9) & 0xff); 
+        buf_pos += set_data(buf+buf_pos,(addr_wk >> 1) & 0xff); 
+        buf_pos += set_data(buf+buf_pos, 0);
+     }
+#endif
+     if (i >= n_bytes) {
+        buf[buf_pos++] = 0; // sck down
+     }
+     ft245r_send(pgm, buf, buf_pos);
+     put_request(addr_save, buf_pos, 0);
+     //ft245r_sync(pgm);
+#if 0
+fprintf(stderr, "send addr 0x%04x bufsize %d [%02x %02x] page_write %d\n",
+		addr_save,buf_pos,
+		extract_data_out(buf , (0*4 + 3) ),
+		extract_data_out(buf , (1*4 + 3) ),
+		do_page_write);
+#endif
+     req_count++;
+     if (req_count > REQ_OUTSTANDINGS)
+        do_request(pgm, m);
+     if (do_page_write) {
+#if defined(USE_INLINE_WRITE_PAGE)
+        while (do_request(pgm, m))
+	     ;
+        usleep(m->max_write_delay);
+#else
+        int addr_wk = addr_save - (addr_save % m->page_size);
+	int rc;
+        while (do_request(pgm, m))
+	     ;
+        rc = avr_write_page(pgm, p, m, addr_wk);
+        if (rc != 0) {
+	  return -2;
+        }
+#endif
+        req_count = 0;
+     }
+  }
+  while (do_request(pgm, m))
+	  ;
+  return i;
+}
+
+
+static int ft245r_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+                              unsigned int page_size, unsigned int addr, unsigned int n_bytes)
+{
+  //printf ("paged_write: %x %x %x\n", page_size, addr, n_bytes);
+
+  if (strcmp(m->desc, "flash") == 0) {
+    return ft245r_paged_write_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return ft245r_paged_write_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+static int ft245r_paged_load_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				 unsigned int page_size, unsigned int addr, 
+				 int n_bytes)
+{
+  unsigned char    rbyte;
+  unsigned long    i;
+  int rc;
+
+  for (i=0; i<n_bytes; i++) {
+     rc = avr_read_byte_default(pgm, p, m, i+addr, &rbyte);
+     if (rc != 0) {
+       return -2;
+     }
+     m->buf[i+addr] = rbyte;
+  }
+  return 0;
+}
+
+
+static int ft245r_paged_load_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				   unsigned int page_size, unsigned int addr, 
+				   unsigned int n_bytes)
+{
+  unsigned long    i,j,n;
+  //int rc;
+  int addr_save,buf_pos;
+  int req_count = 0;
+  char buf[FT245R_FRAGMENT_SIZE+1];
+
+  for (i=0; i<n_bytes; ) {
+     buf_pos = 0;
+     addr_save = addr;
+     for (j=0; j< FT245R_FRAGMENT_SIZE/8/FT245R_CYCLES/4; j++) {
+	if (i >= n_bytes) break;
+        buf_pos += set_data(buf+buf_pos, (addr & 1)?0x28:0x20 ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 9) & 0xff ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 1) & 0xff );
+        buf_pos += set_data(buf+buf_pos, 0);
+	addr ++;
+	i++;
+     }
+     if (i >= n_bytes) {
+        buf[buf_pos++] = 0; // sck down
+     }
+     n = j;
+     ft245r_send(pgm, buf, buf_pos);
+     put_request(addr_save, buf_pos, n);
+     req_count++;
+     if (req_count > REQ_OUTSTANDINGS)
+        do_request(pgm, m);
+
+  }
+  while (do_request(pgm, m))
+	  ;
+  return 0;
+}
+
+static int ft245r_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                             unsigned int page_size, unsigned int addr, 
+			     unsigned int n_bytes)
+{
+  if (strcmp(m->desc, "flash") == 0) {
+    return ft245r_paged_load_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return ft245r_paged_load_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+void ft245r_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "ft245r");
+
+  /*
+   * mandatory functions
+   */
+  pgm->initialize     = ft245r_initialize;
+  pgm->display        = ft245r_display;
+  pgm->enable         = ft245r_enable;
+  pgm->disable        = ft245r_disable;
+  pgm->program_enable = ft245r_program_enable;
+  pgm->chip_erase     = ft245r_chip_erase;
+  pgm->cmd            = ft245r_cmd;
+  pgm->open           = ft245r_open;
+  pgm->close          = ft245r_close;
+  pgm->read_byte      = avr_read_byte_default;
+  pgm->write_byte     = avr_write_byte_default;
+
+  /*
+   * optional functions
+   */
+  pgm->paged_write = ft245r_paged_write;
+  pgm->paged_load = ft245r_paged_load;
+
+  pgm->read_sig_bytes = ft245r_read_sig_bytes;
+}
+
+#else
+static int ft245r_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  //printf ("Blaaaaaaa\n");
+  return -1;
+}
+
+void ft245r_initpgm(PROGRAMMER * pgm)
+{
+  //printf ("Baaa!\n");
+  pgm->initialize     = ft245r_initialize;
+}
+
+#endif
Index: ft245r.h
===================================================================
--- ft245r.h	(revision 0)
+++ ft245r.h	(revision 0)
@@ -0,0 +1,9 @@
+#ifndef ft245r_h
+#define ft245r_h
+
+#include "avrpart.h"
+
+void ft245r_initpgm (PROGRAMMER * pgm);
+
+
+#endif /* ft245r_h */
Index: config_gram.y
===================================================================
--- config_gram.y	(revision 1039)
+++ config_gram.y	(working copy)
@@ -43,6 +43,8 @@
 #include "wiring.h"
 #include "stk500generic.h"
 #include "avr910.h"
+#include "serjtag.h"
+#include "ft245r.h"
 #include "butterfly.h"
 #include "usbasp.h"
 #include "usbtiny.h"
@@ -154,6 +156,8 @@
 %token K_STK600HVSP
 %token K_STK600PP
 %token K_AVR910
+%token K_SERJTAG
+%token K_FT245R
 %token K_USBASP
 %token K_USBDEV
 %token K_USBSN
@@ -462,6 +466,8 @@
   K_STK600HVSP      { stk600hvsp_initpgm(current_prog); } |
   K_STK600PP        { stk600pp_initpgm(current_prog); } |
   K_AVR910          { avr910_initpgm(current_prog); } |
+  K_SERJTAG         { serjtag_initpgm(current_prog); } |
+  K_FT245R          { ft245r_initpgm(current_prog); } |
   K_USBASP          { usbasp_initpgm(current_prog); } |
   K_USBTINY         { usbtiny_initpgm(current_prog); } |
   K_BUTTERFLY       { butterfly_initpgm(current_prog); } |
@@ -1321,10 +1327,10 @@
   value = v->value.number;
   free_token(v);
 
-  if ((value <= 0) || (value >= 18)) {
+  if ((value < 0) || (value >= 18)) {
     fprintf(stderr, 
             "%s: error at line %d of %s: pin must be in the "
-            "range 1-17\n",
+            "range 0-17\n",
             progname, lineno, infile);
     exit(1);
   }
Index: ser_win32.c
===================================================================
--- ser_win32.c	(revision 1039)
+++ ser_win32.c	(working copy)
@@ -46,6 +46,7 @@
 /* HANDLE hComPort=INVALID_HANDLE_VALUE; */
 
 static struct baud_mapping baud_lookup_table [] = {
+#define CBR_3000000	3000000
   { 1200,   CBR_1200 },
   { 2400,   CBR_2400 },
   { 4800,   CBR_4800 },
@@ -54,6 +55,9 @@
   { 38400,  CBR_38400 },
   { 57600,  CBR_57600 },
   { 115200, CBR_115200 },
+#ifdef CBR_3000000
+  { 3000000, CBR_3000000 },
+#endif
   { 0,      0 }                 /* Terminator. */
 };
 
Index: avrdude.conf.in
===================================================================
--- avrdude.conf.in	(revision 1039)
+++ avrdude.conf.in	(working copy)
@@ -17,7 +17,7 @@
 #       desc     = <description> ;                  # quoted string
 #       type     = par | stk500 | stk500v2 | stk500pp | stk500hvsp | stk500generic |
 #                  stk600 | stk600pp | stk600hvsp |
-#                  avr910 | butterfly | usbasp |
+#                  avr910 | butterfly | usbasp | serjtag | ft245r |
 #                  jtagmki | jtagmkii | jtagmkii_isp | jtagmkii_dw |
 #                  jtagmkII_avr32 | jtagmkii_pdi |
 #                  dragon_dw | dragon_jtag | dragon_isp | dragon_pp |
@@ -567,6 +567,42 @@
 ;
 
 programmer
+  id    = "serjtag";
+  desc  = "Serial JTAG Cable";
+  type  = serjtag;
+;
+
+programmer
+  id    = "ft245r";
+  desc  = "FT245R Synchronous BitBang";
+  type  = ft245r;
+  miso  = 1; # D1
+  sck   = 0; # D0
+  mosi  = 2; # D2
+  reset = 4; # D4
+;
+
+programmer
+  id    = "ft232r";
+  desc  = "FT232R Synchronous BitBang";
+  type  = ft245r;
+  miso  = 1;  # RxD
+  sck   = 0;  # RTS
+  mosi  = 2;  # TxD
+  reset = 4;  # DTR
+;
+
+programmer
+  id    = "bwmega";
+  desc  = "BitWizard ftdi_atmega builtin programmer";
+  type  = ft245r;
+  miso  = 5;
+  sck   = 6;
+  mosi  = 3;
+  reset = 7;
+;
+
+programmer
   id    = "usbasp";
   desc  = "USBasp, http://www.fischl.de/usbasp/";
   type  = usbasp;
Index: ser_posix.c
===================================================================
--- ser_posix.c	(revision 1039)
+++ ser_posix.c	(working copy)
@@ -71,6 +71,9 @@
 #ifdef B230400
   { 230400, B230400 },
 #endif
+#ifdef B3000000
+  { 3000000, B3000000 }, 
+#endif
   { 0,      0 }                 /* Terminator. */
 };
 
Index: main.c
===================================================================
--- main.c	(revision 1039)
+++ main.c	(working copy)
@@ -157,8 +157,8 @@
   }
 
   if (percent == 100) {
+    if (!last) fprintf (stderr, "\n\n");
     last = 1;
-    fprintf (stderr, "\n\n");
   }
 
   setvbuf(stderr, (char*)NULL, _IOLBF, 0);
Index: lexer.l
===================================================================
--- lexer.l	(revision 1039)
+++ lexer.l	(working copy)
@@ -120,6 +120,8 @@
 arduino          { yylval=NULL; return K_ARDUINO; }
 avr910           { yylval=NULL; return K_AVR910; }
 avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
+serjtag          { yylval=NULL; return K_SERJTAG; }
+ft245r           { yylval=NULL; return K_FT245R; }
 avrftdi          { yylval=NULL; return K_AVRFTDI; }
 usbasp           { yylval=NULL; return K_USBASP; }
 usbtiny          { yylval=NULL; return K_USBTINY; }
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 1039)
+++ Makefile.am	(working copy)
@@ -88,6 +88,10 @@
 	avr.h \
 	avr910.c \
 	avr910.h \
+	serjtag.c \
+	serjtag.h \
+	ft245r.c \
+	ft245r.h \
 	avrdude.h \
 	avrftdi.c \
 	avrftdi.h \
