Index: avrdude.1
===================================================================
--- avrdude.1	(revision 819)
+++ avrdude.1	(working copy)
@@ -142,6 +142,10 @@
 They both feature simple firwmare-only USB implementations, running on
 an ATmega8 (or ATmega88), or ATtiny2313, respectively.
 .Pp
+The Digilent Inc. USB JTAG/ISP adapter (usbdigilent) is supported, provided
+.Nm avrdude
+has been compiled with libusb support.
+.Pp
 Input files can be provided, and output files can be written in
 different file formats, such as raw binary files containing the data
 to download to the chip, Intel hex format, or Motorola S-record
Index: doc/avrdude.texi
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/x-texinfo
Index: config_gram.y
===================================================================
--- config_gram.y	(revision 819)
+++ config_gram.y	(working copy)
@@ -43,6 +43,7 @@
 #include "avr910.h"
 #include "butterfly.h"
 #include "usbasp.h"
+#include "usbdigilent.h"
 #include "usbtiny.h"
 #include "avr.h"
 #include "jtagmkI.h"
@@ -144,6 +145,7 @@
 %token K_STK600PP
 %token K_AVR910
 %token K_USBASP
+%token K_USBDIGILENT
 %token K_USBTINY
 %token K_BUTTERFLY
 %token K_TYPE
@@ -455,6 +457,12 @@
     }
   } |
 
+  K_TYPE TKN_EQUAL K_USBDIGILENT {
+    {
+      usbdigilent_initpgm(current_prog);
+    }
+  } |
+
   K_TYPE TKN_EQUAL K_USBTINY {
     {
       usbtiny_initpgm(current_prog);
Index: usbdigilent.c
===================================================================
--- usbdigilent.c	(revision 0)
+++ usbdigilent.c	(revision 0)
@@ -0,0 +1,712 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2007 Dick Streefland, adapted for 5.4 by Limor Fried
+ *
+ * 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
+ */
+
+/*
+ * Driver for Digilent's USB JTAG/SPI programming cable
+ * Implements two versions of the programmer
+ * Author: Dirk Robinson - dirkrob@gmail.com
+ * Based on usbtiny.c
+ */
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "avrdude.h"
+#include "avr.h"
+#include "pgm.h"
+#include "usbdigilent.h"
+
+#if defined(HAVE_LIBUSB)      // we use LIBUSB to talk to the board
+#include <usb.h>
+
+/*
+ * Private data for this programmer.
+ */
+struct pdata
+{
+  usb_dev_handle *usb_handle;
+  int retries;
+  int version;
+  int ep_cmd_out, ep_data_out, ep_in; // usb endpoints
+  unsigned char cmd_buf[USBDIG_CMD_LEN];   // buffer for USB transfers
+  unsigned char data_buf[USBDIG_DATA_LEN]; // buffer for USB transfers
+};
+
+#define PDATA(pgm) ((struct pdata *)(pgm->cookie))
+
+// ----------------------------------------------------------------------
+
+static void usbdigilent_setup(PROGRAMMER * pgm)
+{
+  if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
+    fprintf(stderr, 
+	    "%s: usbdigilent_setup(): Out of memory allocating private data\n", 
+	    progname);
+    exit(1);
+  }
+  memset(pgm->cookie, 0, sizeof(struct pdata));
+}
+
+static void usbdigilent_teardown(PROGRAMMER * pgm)
+{
+  free(pgm->cookie);
+}
+
+// Wrapper for simple usb_bulk messages to send data to programmer
+// Assumes data part of buffer already set in PDATA(pgm)->buf
+static int usb_out (PROGRAMMER * pgm, unsigned char cmd, unsigned int len,
+    unsigned char want_response)
+{
+  int nbytes;
+  unsigned char *cmd_buf = PDATA(pgm)->cmd_buf;
+  unsigned char *data_buf = PDATA(pgm)->data_buf;
+
+  if (PDATA(pgm)->version == 1) {
+    // Assemble the USB packet:
+    // 1 byte  - command
+    // 2 bytes - length (big endian)
+    // 1 to 61 bytes - data
+    if (3+len > USBDIG_CMD_LEN) {
+      fprintf(stderr, "\n%s: error: usbdigilent_send: packet too big\n", 
+              progname);
+      return -1;
+    }
+    cmd_buf[0] = cmd;
+    cmd_buf[1] = len >> 8;
+    cmd_buf[2] = len & 0xff;
+    memcpy(cmd_buf+3, data_buf, len);
+  } else { // version 2 protocol:
+    // All SPI transfers are merged into one command
+    if (cmd == USBDIG_SPI_V2 ) {
+      cmd_buf[0] = 0x14;
+      // Set byte #1 to 1 if we want a response:
+      cmd_buf[1] = want_response;
+      // length (big endian) - assume 16 bit length is enough
+      cmd_buf[2] = 0;
+      cmd_buf[3] = 0;
+      cmd_buf[4] = len >> 8;
+      cmd_buf[5] = len & 0xff;
+    } 
+    // For non-SPI commands, assume cmd_buf is set externally
+  }
+
+  // Send the command:
+  nbytes = usb_bulk_write( PDATA(pgm)->usb_handle, PDATA(pgm)->ep_cmd_out,
+          (char *)PDATA(pgm)->cmd_buf, USBDIG_CMD_LEN, USBDIG_TIMEOUT);
+  if (nbytes != USBDIG_CMD_LEN) {
+    fprintf(stderr, 
+            "\n%s: error: usbdigilent_send: %s (expected %d, got %d)\n", 
+	    progname, usb_strerror(), USBDIG_CMD_LEN, nbytes);
+    return -1;
+  }
+
+  // Send the data seperately for version 2:
+  if ( PDATA(pgm)->version == 2 && len > 0 ) {
+    nbytes = usb_bulk_write( PDATA(pgm)->usb_handle, PDATA(pgm)->ep_data_out,
+            (char *)PDATA(pgm)->data_buf, len, USBDIG_TIMEOUT);
+    if (nbytes != len) {
+      fprintf(stderr, 
+              "\n%s: error: usbdigilent_send: %s (expected %d, got %d)\n", 
+              progname, usb_strerror(), len, nbytes);
+      return -1;
+    }
+  }
+
+  return nbytes;
+}
+
+// Wrapper for simple usb_bulk messages to receive data from programmer
+// Result goes to PDATA(pgm)->data (overwriting output packet)
+static int usb_in (PROGRAMMER * pgm, unsigned char cmd, unsigned int len)
+{
+  int nbytes;
+  int i;
+  int len_in;
+  unsigned char *buf_in;
+  
+  // First we have to send the output data
+  if ( usb_out(pgm, cmd, len, 1) < 0 ) {
+    return -1;
+  }
+
+  // Determine length of read buffer and where it will go:
+  if (PDATA(pgm)->version == 1) {
+    // Version 1: read 64 bytes into command buffer
+    len_in = USBDIG_CMD_LEN;
+    buf_in = PDATA(pgm)->cmd_buf;
+  } else {
+    // Version 2: read variable number of bytes into data buffer
+    len_in = len;
+    buf_in = PDATA(pgm)->data_buf;
+  }
+
+  // Read in the data:
+  for (i = 0; i < 10; i++) {
+    nbytes = usb_bulk_read( PDATA(pgm)->usb_handle, PDATA(pgm)->ep_in, 
+            (char *)buf_in, len_in, USBDIG_TIMEOUT) ;
+    if (nbytes == len_in) {
+      if (PDATA(pgm)->version == 1) {
+        // Copy data from command buffer to data buffer
+        memcpy(PDATA(pgm)->data_buf, buf_in+3, len);
+      }
+      return len; // Return the number of meaningful bytes
+    }
+    PDATA(pgm)->retries++;
+  }
+  fprintf(stderr, 
+          "\n%s: error: usbdigilent_receive: %s (expected %d, got %d)\n", 
+          progname, usb_strerror(), len_in, nbytes);
+  return -1;
+
+}
+
+// Report the number of retries, and reset the counter.
+static void check_retries (PROGRAMMER * pgm, const char* operation)
+{
+  if (PDATA(pgm)->retries > 0 && quell_progress < 2) {
+    printf("%s: %d retries during %s\n", progname, 
+           PDATA(pgm)->retries, operation);
+  }
+  PDATA(pgm)->retries = 0;
+}
+
+// Sometimes we just need to know the SPI command for the part to perform
+// a function. Here we wrap this request for an operation so that we
+// can just specify the part and operation and it'll do the right stuff
+// to get the information from AvrDude and send to the programmer
+static int usbdigilent_avr_op (PROGRAMMER * pgm, 
+			   OPCODE *op, int addr, unsigned char res[4])
+{
+  unsigned char	cmd[4];
+
+  if (op == NULL) {
+    fprintf( stderr, "Operation not defined for this chip/memory!\n" );
+    return -1;
+  }
+  memset(cmd, 0, sizeof(cmd));
+  avr_set_bits(op, cmd);
+  if ( addr != 0 ) {
+    avr_set_addr(op, cmd, addr);
+  }
+
+  return pgm->cmd(pgm, cmd, res);
+}
+
+// ----------------------------------------------------------------------
+
+/* Find a device with the correct VID/PID match for USBdigilent */
+
+static int usbdigilent_open(PROGRAMMER* pgm, char* name)
+{
+  struct usb_bus      *bus;
+  struct usb_device   *dev = 0;
+
+  usb_init();               // initialize the libusb system
+  usb_find_busses();        // have libusb scan all the usb busses available
+  usb_find_devices();       // have libusb scan all the usb devices available
+
+  PDATA(pgm)->usb_handle = NULL;
+
+  // now we iterate through all the busses and devices
+  for ( bus = usb_busses; bus; bus = bus->next ) {
+    for	( dev = bus->devices; dev; dev = dev->next ) {
+      if (dev->descriptor.idVendor == USBDIG_VENDOR
+	  && dev->descriptor.idProduct == USBDIG_PRODUCT ) {   // found match?
+
+	PDATA(pgm)->usb_handle = usb_open(dev); // attempt to connect to device
+        // wrong permissions or something?
+	if (!PDATA(pgm)->usb_handle) {
+	  fprintf(stderr, "%s: Warning: cannot open USB device: %s\n", 
+		  progname, usb_strerror());
+	  continue;
+	}
+        usb_reset(PDATA(pgm)->usb_handle);
+        // If there are 4 endpoints, use version 2 protocol
+        if (dev->config->interface->altsetting->bNumEndpoints == 4) {
+          PDATA(pgm)->version = 2;
+          PDATA(pgm)->ep_cmd_out = USBDIG_EP_CMD_OUT_V2;
+          PDATA(pgm)->ep_data_out = USBDIG_EP_DATA_OUT_V2;
+          PDATA(pgm)->ep_in = USBDIG_EP_IN_V2;
+        } else { // version 1 protocol (has 2 endpoints)
+          PDATA(pgm)->version = 1;
+          PDATA(pgm)->ep_cmd_out = USBDIG_EP_CMD_OUT_V1;
+          PDATA(pgm)->ep_data_out = 0; // no data endpoint
+          PDATA(pgm)->ep_in = USBDIG_EP_IN_V1;
+        }
+        if (verbose > 1) {
+          printf( "usbdigilent_open: using programmer protocol version %d\n",
+              PDATA(pgm)->version);
+        }
+      }
+    }
+  }
+
+  if (!PDATA(pgm)->usb_handle) {
+    fprintf( stderr, 
+             "%s: Error: Could not find USBdigilent device (0x%04x/0x%04x)\n", 
+	     progname, USBDIG_VENDOR, USBDIG_PRODUCT );
+    return -1;
+  }
+
+  return 0;           // If we got here, we must have found a good USB device
+}
+
+/* Clean up the handle for the usbdigilent */
+static void usbdigilent_close ( PROGRAMMER* pgm )
+{
+  if (! PDATA(pgm)->usb_handle) {
+    return;                // not a valid handle, bail!
+  }
+  usb_close(PDATA(pgm)->usb_handle);   // ask libusb to clean up
+  PDATA(pgm)->usb_handle = NULL;
+}
+
+/* Set clock, data and reset lines (not necesarily in that order) */
+static int usbdigilent_start (PROGRAMMER *pgm) {
+  unsigned char *cmd=PDATA(pgm)->cmd_buf;
+  unsigned char *data=PDATA(pgm)->data_buf;
+
+  if (PDATA(pgm)->version == 1) {
+    // Version 1: enter programming mode sequence
+    data[0] = 0;  // Clear MOSI
+    if (usb_out(pgm, USBDIG_SET_MOSI_V1, 1, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    data[0] = 1;  // Drive the programming lines
+    if (usb_out(pgm, USBDIG_PROG_EN_V1, 1, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    data[0] = 0;  // Reset driven low
+    if (usb_out(pgm, USBDIG_SET_RESET_V1, 1, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+  } else {
+    // Version 2: enter programming mode sequence
+    memset(cmd, 0, 8);
+    cmd[0] = 0x13;
+    if (usb_out(pgm, cmd[0], 0, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    cmd[0] = 0x10;
+    if (usb_out(pgm, cmd[0], 0, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    cmd[0] = 0x12;
+    if (usb_out(pgm, cmd[0], 0, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+  }
+  return 0;
+}
+
+static int usbdigilent_stop (PROGRAMMER *pgm) {
+  unsigned char *cmd=PDATA(pgm)->cmd_buf;
+  unsigned char *data=PDATA(pgm)->data_buf;
+
+  if (PDATA(pgm)->version == 1) {
+    // Version 1: exit programming mode sequence
+    data[0] = 1;  // Reset driven high
+    if (usb_out(pgm, USBDIG_SET_RESET_V1, 1, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    data[0] = 0;  // Release programming lines
+    if (usb_out(pgm, USBDIG_PROG_EN_V1, 1, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+  } else {
+    // Version 2: exit programming mode sequence
+    memset(cmd, 0, 8);
+    cmd[0] = 0x12;
+    cmd[1] = 0x01;
+    if (usb_out(pgm, cmd[0], 0, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    cmd[0] = 0x11;
+    cmd[1] = 0x00;
+    if (usb_out(pgm, cmd[0], 0, 0) < 0) {
+      return -1;
+    }
+    usleep(50000);
+  }
+  return 0;
+}
+
+static int usbdigilent_initialize (PROGRAMMER *pgm, AVRPART *p )
+{
+  unsigned char res[4];        // store the response from usbdigilentisp
+
+  if ( usbdigilent_start(pgm) < 0 ) {
+    return -1;
+  }
+
+  // Attempt to use the underlying avrdude methods to connect
+  if ( usbdigilent_avr_op(pgm, p->op[AVR_OP_PGM_ENABLE], 0, res) < 0) {
+    // no response, RESET and try again
+    if (usbdigilent_stop(pgm) < 0 ||
+        usbdigilent_start(pgm) < 0) {
+      return -1;
+    }
+    usleep(50000);
+    if	( usbdigilent_avr_op( pgm, p->op[AVR_OP_PGM_ENABLE], 0, res) < 0) {
+      // give up
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* Tell the USBdigilent to release the output pins, etc */
+static void usbdigilent_powerdown(PROGRAMMER * pgm)
+{
+  if (!PDATA(pgm)->usb_handle) {
+    return;                 // wasn't connected in the first place
+  }
+  usbdigilent_stop(pgm);    // Send USB commands to stop device
+}
+
+/* Send a 4-byte SPI command to the USBdigilentISP for execution
+   This procedure is used by higher-level Avrdude procedures */
+static int usbdigilent_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                           unsigned char res[4])
+{
+  unsigned char *data=PDATA(pgm)->data_buf;
+  int nbytes;
+
+  // Copy cmd to res: the usb_in command will then overwrite it
+  memcpy(data, cmd, 4 );
+  if ( PDATA(pgm)->version == 1 ) {
+    nbytes = usb_in( pgm, USBDIG_SPI_V1, 4);
+  } else {
+    nbytes = usb_in( pgm, USBDIG_SPI_V2, 4);
+  }
+  if (nbytes < 0) {
+    return -1;
+  }
+  memcpy(res, data, 4 );
+
+  check_retries(pgm, "SPI command");
+  if (verbose > 1) {
+    // print out the data we sent and received
+    printf( "CMD: [%02x %02x %02x %02x] [%02x %02x %02x %02x]\n", 
+	    cmd[0], cmd[1], cmd[2], cmd[3], 
+	    res[0], res[1], res[2], res[3] );
+  }
+  if ((nbytes == 4) &&               // should have read 4 bytes
+	  res[2] == cmd[1]) {        // AVR's do a delayed-echo thing
+    return nbytes;
+  } else {
+    return -1;
+  }
+}
+
+/* Send the chip-erase command */
+static int usbdigilent_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  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;
+  }
+
+  // get the command for erasing this chip and transmit to avrdude
+  if ( usbdigilent_avr_op( pgm, p->op[AVR_OP_CHIP_ERASE], 0, res ) < 0) {
+    return -1;
+  }
+  usleep( p->chip_erase_delay );
+
+  // prepare for further instruction
+  pgm->initialize(pgm, p);
+
+  return 0;
+}
+
+// These are required functions but don't actually do anything
+static void usbdigilent_enable ( PROGRAMMER* pgm ) { }
+
+static void usbdigilent_disable ( PROGRAMMER* pgm ) { }
+
+// Helper function to limit read/write increments to page boundaries
+// Returns 1 if the increment ends on a page boundary
+static int limit_to_page(int *p_chunk, int start, int page_size)
+{
+  int page_num = start/page_size;
+  if ( *p_chunk >= (page_num+1)*page_size - start ) {
+    *p_chunk = (page_num+1)*page_size - start;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/* To speed up programming and reading, we do 'chunked' transfers.
+ *  This function handles both writes and reads.
+ *  For version 1 protocol,
+ *   it assembles up to 15 SPI commands tacked onto the USB control packet.
+ *  For version 2 protocol,
+ *   it assembles up to 64 SPI commands into a seperate USB data packet.
+ */
+static int usbdigilent_paged_transfer (PROGRAMMER * pgm, AVRPART * p, 
+                  AVRMEM* m, int page_size, int n_bytes, int write)
+{
+  int i, j;
+  int chunk;
+  int n_pad_bytes=0;
+  int ext_address=0;
+  int full_atmel_page;     // End of the AVR page
+  int full_digilent_page;  // End of digilent programmer buffer page
+  unsigned char *data=PDATA(pgm)->data_buf;
+  unsigned char res[4];
+  OPCODE * op[2];    // Commands for reading/writing low and high bytes
+  int len_word;
+  int usbdig_chunk_size;
+  int nbytes;
+
+  if ( PDATA(pgm)->version == 1 ) {
+    usbdig_chunk_size = USBDIG_CHUNK_SIZE_V1;
+  } else {
+    usbdig_chunk_size = USBDIG_CHUNK_SIZE_V2;
+  }
+
+  if ( page_size == 0 ) { // Use the Digilent page size if not set.
+    page_size = USBDIG_PAGE_SIZE;
+  }
+
+  // Set up the atmel transfer commands:
+  if (write) {   // Set up for writing:
+    if (m->op[AVR_OP_LOADPAGE_HI]) {
+      op[0] = m->op[AVR_OP_LOADPAGE_LO];
+      op[1] = m->op[AVR_OP_LOADPAGE_HI];
+      len_word = 2;
+    } else {
+      op[0] = m->op[AVR_OP_LOADPAGE_LO];
+      op[1] = m->op[AVR_OP_LOADPAGE_LO];
+      len_word = 1;
+    }
+    // Also, find the number of bytes to pad so writing ends on a full page
+    if ( n_bytes % page_size != 0 ) {
+      n_pad_bytes = page_size - (n_bytes%page_size);
+    }
+  } else {        // Set up for reading:
+    if (m->op[AVR_OP_READ]) {
+      op[0] = m->op[AVR_OP_READ];
+      op[1] = m->op[AVR_OP_READ];
+      len_word = 1;
+    } else {
+      op[0] = m->op[AVR_OP_READ_LO];
+      op[1] = m->op[AVR_OP_READ_HI];
+      len_word = 2;
+    }
+  }
+
+  // Transfer the data:
+  for (i = 0; i < n_bytes+n_pad_bytes; i += chunk) {
+    // Load Extended address if needed:
+    if ( ((i/len_word)>>16) != ext_address ) {
+      ext_address = (i/len_word)>>16;
+      if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_LOAD_EXT_ADDR], 
+            i/len_word, res ) < 0) {
+        return -1;
+      }
+    }
+
+    chunk = usbdig_chunk_size;  // start with the maximum chunk size possible
+    full_atmel_page = 0;
+    full_digilent_page = 0;
+
+    // If we want to xmit less than a chunk, that's OK
+    if	(chunk > n_bytes+n_pad_bytes-i) {
+      chunk = n_bytes+n_pad_bytes - i;
+    }
+    // Stop on Atmel page boundaries:
+    if ( limit_to_page(&chunk, i, page_size) ) {
+      full_atmel_page = 1;
+    }
+    // Stop on Digilent page boundaries:
+    if ( limit_to_page(&chunk, i, USBDIG_PAGE_SIZE) ) {
+      full_digilent_page = 1;
+    }
+
+    // Set up the USB packet:
+    memset(data, 0, USBDIG_DATA_LEN);
+    for (j = 0; j < chunk; j++) {
+      avr_set_bits(op[(i+j)%2], data+4*j);
+      avr_set_addr(op[(i+j)%2], data+4*j, (i+j)/len_word);
+      // If we're writing, add on the output data
+      if ( write ) {
+        if( i+j < n_bytes ) {
+          data[4*j+3] = m->buf[i+j];  // Normal data
+        } else {
+          data[4*j+3] = 0xff;         // Pad bytes
+        }
+      }
+    }
+
+    // Do the USB transfer:
+    if ( write ) {
+      if ( PDATA(pgm)->version == 1) {
+        // version 1 protocol:
+        // Write up to 60 bytes, wait for response at page boundaries
+        // Send the packet
+        if ( chunk<usbdig_chunk_size || full_atmel_page 
+            || full_digilent_page ) {
+          nbytes = usb_in(pgm, USBDIG_WRITE_WAIT_V1, 4*chunk);
+        } else {
+          nbytes = usb_out(pgm, USBDIG_WRITE_PAGE_V1, 4*chunk, 0);
+        }
+      } else { // version 2 protocol
+        // Write up to 256 bytes without wait
+        nbytes = usb_out(pgm, USBDIG_SPI_V2, 4*chunk, 0);
+      }
+      if ( nbytes < 0 ) {
+        return -1;
+      }
+
+      // At end of a page, issue page write command (if memory is paged):
+      if ( (i+chunk)%page_size == 0 && m->paged ) {
+        if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_WRITEPAGE], 
+              i/len_word, res ) < 0) {
+            return -1;
+        }
+        // Wait for AVR part to write the page:
+        usleep(m->min_write_delay);        /* microseconds */
+      }
+    } else { // read
+      if ( PDATA(pgm)->version == 1) {
+        // version 1 protocol:
+        // Read up to 60 bytes, using different command when at page boundaries
+        //   (for unknown reason)
+        // Send the packet and get the result:
+        if ( chunk<usbdig_chunk_size || full_atmel_page 
+            || full_digilent_page ) {
+          // Read up to 15 bytes
+          nbytes = usb_in(pgm, USBDIG_SPI_V1, 4*chunk);
+        } else {
+          // Read full 15 bytes
+          nbytes = usb_in(pgm, USBDIG_READ_PAGE_V1, 4*chunk);
+        }
+      } else { // version 2 protocol
+        // Read up to 256 bytes:
+        nbytes = usb_in(pgm, USBDIG_SPI_V2, 4*chunk);
+      }
+      if ( nbytes < 0 ) {
+        return -1;
+      }
+
+      // Read the result from the USB packet:
+      for (j = 0; j < chunk; j++) {
+        m->buf[i+j] = data[4*j+3];
+      }
+    }
+
+    // Tell avrdude how we're doing to provide user feedback
+    report_progress(i + chunk, n_bytes+n_pad_bytes, NULL );
+  }
+
+  // Extended addressing - set back to zero if necessary:
+  if ( 0 != ext_address ) {
+    if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_LOAD_EXT_ADDR], 0, res ) < 0) {
+      return -1;
+    }
+  }
+
+  return n_bytes;
+}
+
+/* To speed up programming and reading, we do a 'chunked' read.  */
+static int usbdigilent_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+			       int page_size, int n_bytes)
+{
+  return usbdigilent_paged_transfer(pgm, p, m, page_size, n_bytes, 0);
+}
+
+/* To speed up programming and reading, we do a 'chunked' write.  */
+static int usbdigilent_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+			       int page_size, int n_bytes)
+{
+  if ( ! m->paged ) {     // Only paged memories are allowed for write
+    return -1;            // (Since we can't add delay between single writes)
+  }
+  return usbdigilent_paged_transfer(pgm, p, m, page_size, n_bytes, 1);
+}
+
+extern void usbdigilent_initpgm ( PROGRAMMER* pgm )
+{
+  strcpy(pgm->type, "USBdigilent");
+
+  /* Mandatory Functions */
+  pgm->initialize	= usbdigilent_initialize;
+  pgm->enable	        = usbdigilent_enable;
+  pgm->disable	        = usbdigilent_disable;
+  pgm->program_enable	= NULL;
+  pgm->chip_erase	= usbdigilent_chip_erase;
+  pgm->cmd		= usbdigilent_cmd;
+  pgm->open		= usbdigilent_open;
+  pgm->close		= usbdigilent_close;
+  pgm->read_byte        = avr_read_byte_default;
+  pgm->write_byte       = avr_write_byte_default;
+
+  /* Optional Functions */
+  pgm->powerup	        = NULL;
+  pgm->powerdown	= usbdigilent_powerdown;
+  pgm->paged_load	= usbdigilent_paged_load;
+  pgm->paged_write	= usbdigilent_paged_write;
+  pgm->set_sck_period	= NULL;
+  pgm->setup            = usbdigilent_setup;
+  pgm->teardown         = usbdigilent_teardown;
+}
+
+#else  /* !HAVE_LIBUSB */
+
+// Give a proper error if we were not compiled with libusb
+
+static int usbdigilent_nousb_open(struct programmer_t *pgm, char * name)
+{
+  fprintf(stderr, 
+  "%s: error: no usb support. Please compile again with libusb installed.\n", 
+	  progname);
+
+  return -1;
+}
+
+void usbdigilent_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "usbdigilent");
+
+  pgm->open = usbdigilent_nousb_open;
+}
+
+#endif /* HAVE_LIBUSB */
+// vim:sw=2:ts=8
Index: usbdigilent.h
===================================================================
--- usbdigilent.h	(revision 0)
+++ usbdigilent.h	(revision 0)
@@ -0,0 +1,100 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2007 Limor Fried
+ *
+ * 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
+ */
+
+
+#ifndef usbdigilent_h
+#define usbdigilent_h
+
+#include "avrpart.h"
+
+#define USBDIG_VENDOR  0x1443
+#define USBDIG_PRODUCT 0x0001
+
+/* Digilent command format - version 2 protocol
+ * Commands are sent with USB bulk transfers and always 64 bytes long.
+ * Commands go to EP (endpoint) 1. 
+ * The data output packet associated with a command is
+ * variable length, up to 512 bytes, and goes to EP 2.
+ * Data returned comes from EP 6.
+ *
+ * The command format can vary with the command code, unlike version 1.
+ * Version 2 has a unified command to handle all SPI transfers:
+ * Byte 1: command code (0x14)
+ * Byte 2: 1 - return serial output data, 0 - disregard output
+ * Byte 3-6: number of bytes of data (big endian)
+ */
+
+/* Digilent command format - version 1 protocol
+ * Commands are sent with USB bulk transfers and always 64 bytes long.
+ * Commands that have a response also return a 64 byte packet.
+ * Byte 1: command (output) / response code (response)
+ * Byte 2: length (upper byte)
+ * Byte 3: length (lower byte)
+ * Bytes 4-64: data
+ *
+ * Most commands send raw SPI data. Thus up to 15 commands can fit in
+ * data part of one USB transfer.
+ * Output to endpoint 4, input from endpoint 2
+ */
+
+#define USBDIG_CMD_LEN 64
+#define USBDIG_DATA_LEN 512
+
+// Version 1 USB parameters:
+#define USBDIG_EP_CMD_OUT_V1  4 // 64 bytes command out
+#define USBDIG_EP_IN_V1       2 // 64 bytes command response/return val
+
+// Version 2 USB parameters:
+#define USBDIG_EP_CMD_OUT_V2  1  // 64 bytes command out
+#define USBDIG_EP_DATA_OUT_V2 2  // 1-512 bytes data out
+#define USBDIG_EP_IN_V2       6  // 1-512 bytes data in
+
+// Version 1 programmer commands:
+#define USBDIG_PROG_EN_V1   0x08   // Enable driving of programming lines
+#define USBDIG_SET_RESET_V1 0x09   // Set/clear the reset line
+#define USBDIG_SET_MOSI_V1  0x0a   // Set/clear the Atmel's input
+
+#define USBDIG_WRITE_PAGE_V1 0x0b   // Write 15 bytes, no response
+#define USBDIG_WRITE_WAIT_V1 0x8b   // Write up to 15 bytes, wait for response
+#define USBDIG_READ_PAGE_V1  0x4b   // Read 15 bytes
+#define USBDIG_SPI_V1        0xcb   // Write/Read up to 15 bytes (with response)
+
+// Version 2 programmer commands:
+#define USBDIG_SPI_V2       0x14   // Unified SPI command in version 2
+
+// How much data, max, do we want to send in one USB packet?
+#define USBDIG_CHUNK_SIZE_V1 15   // 15 SPI commands = 60 bytes
+#define USBDIG_CHUNK_SIZE_V2 64   // 64 SPI commands = 256 bytes
+#define USBDIG_PAGE_SIZE     64   // Digilent programmer can hold 
+                                  //   64 SPI instructions
+
+// The default USB Timeout
+#define USBDIG_TIMEOUT 500 // msec
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void usbdigilent_initpgm (PROGRAMMER * pgm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* usbdigilent_h */
Index: avrdude.conf.in
===================================================================
--- avrdude.conf.in	(revision 819)
+++ avrdude.conf.in	(working copy)
@@ -411,6 +411,12 @@
 ;
 
 programmer
+  id    = "usbdigilent";
+  desc  = "Digilent USB Programmer, http://www.digilentinc.com";
+  type  = usbdigilent;
+;
+
+programmer
   id    = "usbtiny";
   desc  = "USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/";
   type  = usbtiny;
Index: avr.c
===================================================================
--- avr.c	(revision 819)
+++ avr.c	(working copy)
@@ -149,7 +149,7 @@
              int verbose)
 {
   unsigned char    rbyte;
-  unsigned long    i;
+  long             i;
   unsigned char  * buf;
   AVRMEM * mem;
   int rc;
@@ -540,7 +540,7 @@
 {
   int              rc;
   int              wsize;
-  unsigned long    i;
+  long             i;
   unsigned char    data;
   int              werror;
   AVRMEM         * m;
Index: lexer.l
===================================================================
--- lexer.l	(revision 819)
+++ lexer.l	(working copy)
@@ -121,6 +121,7 @@
 avr910           { yylval=NULL; return K_AVR910; }
 avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
 usbasp           { yylval=NULL; return K_USBASP; }
+usbdigilent      { yylval=NULL; return K_USBDIGILENT; }
 usbtiny          { yylval=NULL; return K_USBTINY; }
 bank_size        { yylval=NULL; return K_PAGE_SIZE; }
 banked           { yylval=NULL; return K_PAGED; }
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 819)
+++ Makefile.am	(working copy)
@@ -138,6 +138,8 @@
 	usbasp.c \
 	usbasp.h \
 	usbdevs.h \
+	usbdigilent.h \
+	usbdigilent.c \
 	usb_libusb.c \
 	usbtiny.h \
 	usbtiny.c \
