From 190a4b87e848eb9b8c57eed63354c7309076c448 Mon Sep 17 00:00:00 2001
From: Joerg Wunsch <j@uriah.heep.sax.de>
Date: Mon, 29 Oct 2007 18:03:02 +0000
Subject: [PATCH] Submitted by <ladyada@gmail.com>: Patch #6233: Add support
 for USBtinyISP programmer * usbtiny.c: New file. * usbtiny.h: (Ditto.) *
 Makefile.am: Include usbtiny into the build. * avrdude.conf.in: (Ditto.) *
 config_gram.y: (Ditto.) * lexer.l: (Ditto.) * avrdude.1: Document the usbtiny
 support. * doc/avrdude.texi: (Ditto.)

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@749 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 ChangeLog        |  13 ++
 Makefile.am      |   2 +
 NEWS             |   1 +
 avrdude.1        |  12 +-
 avrdude.conf.in  |   6 +
 config_gram.y    |   8 +
 doc/avrdude.texi |   3 +
 lexer.l          |   1 +
 usbtiny.c        | 484 +++++++++++++++++++++++++++++++++++++++++++++++
 usbtiny.h        |  76 ++++++++
 10 files changed, 600 insertions(+), 6 deletions(-)
 create mode 100644 usbtiny.c
 create mode 100644 usbtiny.h

diff --git a/ChangeLog b/ChangeLog
index d511c5dd..94f5df8c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2007-10-29 Joerg Wunsch <j@uriah.heep.sax.de>
+
+	Submitted by <ladyada@gmail.com>:
+	Patch #6233: Add support for USBtinyISP programmer
+	* usbtiny.c: New file.
+	* usbtiny.h: (Ditto.)
+	* Makefile.am: Include usbtiny into the build.
+	* avrdude.conf.in: (Ditto.)
+	* config_gram.y: (Ditto.)
+	* lexer.l: (Ditto.)
+	* avrdude.1: Document the usbtiny support.
+	* doc/avrdude.texi: (Ditto.)
+
 2007-10-29 Joerg Wunsch <j@uriah.heep.sax.de>
 
 	* doc/avrdude.texi: Sort list of supported programmers into
diff --git a/Makefile.am b/Makefile.am
index 9f4b32e9..094c177d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -138,6 +138,8 @@ libavrdude_a_SOURCES = \
 	usbasp.h \
 	usbdevs.h \
 	usb_libusb.c \
+	usbtiny.h \
+	usbtiny.c \
 	update.h \
 	update.c
 
diff --git a/NEWS b/NEWS
index 61915c5c..d9cadb44 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ Approximate change log for AVRDUDE by version.
 ----------------------------------------------------------------------
 Current:
 
+  * Add support for the USBtinyISP programmer (patch #6233)
 
 Version 5.4:
 
diff --git a/avrdude.1 b/avrdude.1
index 2abb1ca6..cb56e8e0 100644
--- a/avrdude.1
+++ b/avrdude.1
@@ -1,6 +1,6 @@
 .\"
 .\" avrdude - A Downloader/Uploader for AVR device programmers
-.\" Copyright (C) 2001, 2002, 2003, 2005, 2006  Joerg Wunsch
+.\" Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007  Joerg Wunsch
 .\"
 .\" 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
@@ -19,7 +19,7 @@
 .\"
 .\" $Id$
 .\"
-.Dd DATE October 26, 2006
+.Dd DATE October 29, 2007
 .Os
 .Dt AVRDUDE 1
 .Sh NAME
@@ -127,11 +127,11 @@ frequency, so the
 .Fl B Ar bitclock
 option might be required to achieve a stable ISP communication.
 .Pp
-The USBasp ISP adapter is also supported, provided
+The USBasp ISP and USBtinyISP adapters are also supported, provided
 .Nm avrdude
 has been compiled with libusb support.
-It features a simple firwmare-only USB implementation, running on
-an ATmega8 (or ATmega88).
+They both feature simple firwmare-only USB implementations, running on
+an ATmega8 (or ATmega88), or ATtiny2313, respectively.
 .Pp
 Input files can be provided, and output files can be written in
 different file formats, such as raw binary files containing the data
@@ -817,6 +817,6 @@ option) requires a prior chip erase.
 This is an inherent feature of the way JTAG EEPROM programming works.
 This also applies to the STK500 in parallel programming mode.
 .Pp
-The USBasp driver does not offer any option to distinguish multiple
+The USBasp and USBtinyISP drivers do not offer any option to distinguish multiple
 devices connected simultaneously, so effectively only a single device
 is supported.
diff --git a/avrdude.conf.in b/avrdude.conf.in
index 8d6acd23..556d9bad 100644
--- a/avrdude.conf.in
+++ b/avrdude.conf.in
@@ -377,6 +377,12 @@ programmer
   type  = usbasp;
 ;
 
+programmer
+  id    = "usbtiny";
+  desc  = "USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/";
+  type  = usbtiny;
+;
+
 programmer
   id    = "butterfly";
   desc  = "Atmel Butterfly Development Board";
diff --git a/config_gram.y b/config_gram.y
index 6eb3dd20..d75cfd72 100644
--- a/config_gram.y
+++ b/config_gram.y
@@ -42,6 +42,7 @@
 #include "avr910.h"
 #include "butterfly.h"
 #include "usbasp.h"
+#include "usbtiny.h"
 #include "avr.h"
 #include "jtagmkI.h"
 #include "jtagmkII.h"
@@ -136,6 +137,7 @@ static int parse_cmdbits(OPCODE * op);
 %token K_STK500GENERIC
 %token K_AVR910
 %token K_USBASP
+%token K_USBTINY
 %token K_BUTTERFLY
 %token K_TYPE
 %token K_VCC
@@ -421,6 +423,12 @@ prog_parm :
     }
   } |
 
+  K_TYPE TKN_EQUAL K_USBTINY {
+    {
+      usbtiny_initpgm(current_prog);
+    }
+  } |
+
   K_TYPE TKN_EQUAL K_BUTTERFLY {
     { 
       butterfly_initpgm(current_prog);
diff --git a/doc/avrdude.texi b/doc/avrdude.texi
index eaadf6d7..4a8f1e51 100644
--- a/doc/avrdude.texi
+++ b/doc/avrdude.texi
@@ -462,6 +462,9 @@ Atmel STK500, running a version 2.x firmware
 @item @code{usbasp}      @tab
 USBasp,@*
 @url{http://www.fischl.de/usbasp/}
+@item @code{usbtiny}     @tab
+USBtiny simple USB programmer,@*
+@url{http://www.ladyada.net/make/usbtinyisp/}
 @item @code{xil}         @tab
 Xilinx JTAG cable
 @end multitable
diff --git a/lexer.l b/lexer.l
index a1b38a14..c23a0856 100644
--- a/lexer.l
+++ b/lexer.l
@@ -120,6 +120,7 @@ allowfullpagebitstream { yylval=NULL; return K_ALLOWFULLPAGEBITSTREAM; }
 avr910           { yylval=NULL; return K_AVR910; }
 avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
 usbasp           { yylval=NULL; return K_USBASP; }
+usbtiny          { yylval=NULL; return K_USBTINY; }
 bank_size        { yylval=NULL; return K_PAGE_SIZE; }
 banked           { yylval=NULL; return K_PAGED; }
 baudrate         { yylval=NULL; return K_BAUDRATE; }
diff --git a/usbtiny.c b/usbtiny.c
new file mode 100644
index 00000000..edf1322f
--- /dev/null
+++ b/usbtiny.c
@@ -0,0 +1,484 @@
+/*
+ * 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 "usbtiny"-type programmers
+ * Please see http://www.xs4all.nl/~dicks/avr/usbtiny/
+ *        and http://www.ladyada.net/make/usbtinyisp/
+ * For example schematics and detailed documentation
+ */
+
+#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 "usbtiny.h"
+
+#if defined(HAVE_LIBUSB)      // we use LIBUSB to talk to the board
+#include <usb.h>
+
+typedef	unsigned int	uint_t;
+typedef	unsigned long	ulong_t;
+
+extern int avr_write_byte_default ( PROGRAMMER* pgm, AVRPART* p,
+				    AVRMEM* mem, ulong_t addr,
+				    unsigned char data );
+static	usb_dev_handle*	usb_handle;
+static	int		sck_period;
+static	int		chunk_size;
+
+
+// ----------------------------------------------------------------------
+
+// Wrapper for simple usb_control_msg messages
+static int usb_control (unsigned int requestid, unsigned int val, unsigned int index )
+{
+  int nbytes;
+  nbytes = usb_control_msg( usb_handle,
+			    USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			    requestid,
+			    val, index,           // 2 bytes each of data
+			    NULL, 0,              // no data buffer in control messge
+			    USB_TIMEOUT );        // default timeout
+  if(nbytes < 0){
+    fprintf(stderr, "%s: error: usbtiny_transmit: %s\n", progname, usb_strerror());
+    exit(1);
+  }
+
+  return nbytes;
+}
+
+// Wrapper for simple usb_control_msg messages to receive data from programmer
+static int usb_in (unsigned int requestid, unsigned int val, unsigned int index,
+		   unsigned char* buffer, int buflen, int bitclk )
+{
+  int nbytes;
+  int timeout;
+
+  // calculate the amout of time we expect the process to take by
+  // figuring the bit-clock time and buffer size and adding to the standard USB timeout.
+  timeout = USB_TIMEOUT + (buflen * bitclk) / 1000;
+
+  nbytes = usb_control_msg( usb_handle,
+			    USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			    requestid,
+			    val, index,
+			    (char *)buffer, buflen,
+			    timeout);
+  if (nbytes != buflen) {
+    fprintf(stderr, "%s: error: usbtiny_receive: %s (expected %d, got %d)\n",
+	    progname, usb_strerror(), buflen, nbytes);
+    exit(1);
+  }
+
+  return nbytes;
+}
+
+// Wrapper for simple usb_control_msg messages to send data to programmer
+static int usb_out (unsigned int requestid, unsigned int val, unsigned int index,
+		     unsigned char* buffer, int buflen, int bitclk )
+{
+  int nbytes;
+  int timeout;
+
+  // calculate the amout of time we expect the process to take by
+  // figuring the bit-clock time and buffer size and adding to the standard USB timeout.
+  timeout = USB_TIMEOUT + (buflen * bitclk) / 1000;
+
+  nbytes = usb_control_msg( usb_handle,
+			    USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			    requestid,
+			    val, index,
+			    (char *)buffer, buflen,
+			    timeout);
+  if (nbytes != buflen) {
+    fprintf(stderr, "%s: error: usbtiny_send: %s (expected %d, got %d)\n",
+	    progname, usb_strerror(), buflen, nbytes);
+    exit(1);
+  }
+
+  return nbytes;
+}
+
+// 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 USBtiny
+static int usbtiny_avr_op (PROGRAMMER * pgm, AVRPART * p,
+			   int op,
+			   unsigned char res[4])
+{
+  unsigned char	cmd[4];
+
+  if (p->op[op] == NULL) {
+    fprintf( stderr, "Operation %d not defined for this chip!\n", op );
+    return -1;
+  }
+  memset(cmd, 0, sizeof(cmd));
+  avr_set_bits(p->op[op], cmd);
+
+  return pgm->cmd(pgm, cmd, res);
+}
+
+// ----------------------------------------------------------------------
+
+/* Find a device with the correct VID/PID match for USBtiny */
+
+static	int	usbtiny_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
+
+  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 == USBTINY_VENDOR
+	  && dev->descriptor.idProduct == USBTINY_PRODUCT ) {   // found match?
+
+	usb_handle = usb_open(dev);           // attempt to connect to device
+
+	// wrong permissions or something?
+	if (!usb_handle) {
+	  fprintf(stderr, "%s: Warning: cannot open USB device: %s\n",
+		  progname, usb_strerror());
+	  continue;
+	}
+      }
+    }
+  }
+
+  if (!usb_handle) {
+    fprintf( stderr, "%s: Error: Could not find USBtiny device (0x%x/0x%x)\n",
+	     progname, USBTINY_VENDOR, USBTINY_PRODUCT );
+    exit(1);
+  }
+
+  return 0;                  // If we got here, we must have found a good USB device
+}
+
+/* Clean up the handle for the usbtiny */
+static	void usbtiny_close ( PROGRAMMER* pgm )
+{
+  if (! usb_handle) {
+    return;                // not a valid handle, bail!
+  }
+  usb_close(usb_handle);   // ask libusb to clean up
+  usb_handle = NULL;
+}
+
+/* A simple calculator function determines the maximum size of data we can
+   shove through a USB connection without getting errors */
+static void usbtiny_set_chunk_size (int period)
+{
+  chunk_size = CHUNK_SIZE;       // start with the maximum (default)
+  while	(chunk_size > 8 && period > 16) {
+    // Reduce the chunk size for a slow SCK to reduce
+    // the maximum time of a single USB transfer.
+    chunk_size >>= 1;
+    period >>= 1;
+  }
+}
+
+/* Given a SCK bit-clock speed (in useconds) we verify its an OK speed and tell the
+   USBtiny to update itself to the new frequency */
+static int usbtiny_set_sck_period (PROGRAMMER *pgm, double v)
+{
+  sck_period = (int)(v * 1e6 + 0.5);   // convert from us to 'int', the 0.5 is for rounding up
+
+  // Make sure its not 0, as that will confuse the usbtiny
+  if (sck_period < SCK_MIN)
+    sck_period = SCK_MIN;
+
+  // We can't go slower, due to the byte-size of the clock variable
+  if  (sck_period > SCK_MAX)
+    sck_period = SCK_MAX;
+
+  printf( "%s: Setting SCK period to %d usec\n", progname, sck_period );
+
+  // send the command to the usbtiny device.
+  usb_control(USBTINY_POWERUP, sck_period, RESET_LOW);  // MEME: for at90's fix resetstate?
+
+  // with the new speed, we'll have to update how much data we send per usb transfer
+  usbtiny_set_chunk_size(sck_period);
+  return 0;
+}
+
+
+static int usbtiny_initialize (PROGRAMMER *pgm, AVRPART *p )
+{
+  unsigned char res[4];        // store the response from usbtinyisp
+
+  // Check for bit-clock and tell the usbtiny to adjust itself
+  if (pgm->bitclock > 0.0) {
+    // -B option specified: convert to valid range for sck_period
+    usbtiny_set_sck_period(pgm, pgm->bitclock);
+  } else {
+    // -B option not specified: use default
+    sck_period = SCK_DEFAULT;
+    if	(verbose) {
+      printf( "%s: Using SCK period of %d usec\n",
+	      progname, sck_period );
+    }
+    usb_control( USBTINY_POWERUP, sck_period, RESET_LOW );
+    usbtiny_set_chunk_size(sck_period);
+  }
+
+  // Let the device wake up.
+  usleep(50000);
+
+  // Attempt to use the underlying avrdude methods to connect (MEME: is this kosher?)
+  if (! usbtiny_avr_op(pgm, p, AVR_OP_PGM_ENABLE, res)) {
+    // no response, RESET and try again
+    usb_control(USBTINY_POWERUP, sck_period, RESET_HIGH);
+    usb_control(USBTINY_POWERUP, sck_period, RESET_LOW);
+    usleep(50000);
+    if	( ! usbtiny_avr_op( pgm, p, AVR_OP_PGM_ENABLE, res)) {
+      // give up
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* Tell the USBtiny to release the output pins, etc */
+static void usbtiny_powerdown(PROGRAMMER * pgm)
+{
+  if (!usb_handle) {
+    return;                 // wasn't connected in the first place
+  }
+  usb_control(USBTINY_POWERDOWN, 0, 0);      // Send USB control command to device
+}
+
+/* Send a 4-byte SPI command to the USBtinyISP for execution
+   This procedure is used by higher-level Avrdude procedures */
+static int usbtiny_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4])
+{
+  int nbytes;
+
+  // Make sure its empty so we don't read previous calls if it fails
+  memset(res, '\0', sizeof(res) );
+
+  nbytes = usb_in( USBTINY_SPI,
+		   (cmd[1] << 8) | cmd[0],  // convert to 16-bit words
+		   (cmd[3] << 8) | cmd[2],  //  "
+			res, sizeof(res), 8 * sck_period );
+  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] );
+  }
+  return ((nbytes == sizeof(res)) &&      // should have read 4 bytes
+	  res[2] == cmd[1]);              // AVR's do a delayed-echo thing
+}
+
+/* Send the chip-erase command */
+static int usbtiny_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 (! usbtiny_avr_op( pgm, p, AVR_OP_CHIP_ERASE, res )) {
+    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	usbtiny_enable ( PROGRAMMER* pgm ) {}
+
+static void usbtiny_disable ( PROGRAMMER* pgm ) {}
+
+
+/* To speed up programming and reading, we do a 'chunked' read.
+ *  We request just the data itself and the USBtiny uses the SPI function
+ *  given to read in the data. Much faster than sending a 4-byte SPI request
+ *  per byte
+*/
+static int usbtiny_paged_load (PROGRAMMER * pgm, AVRPART * p, AVRMEM* m,
+				    int page_size, int n_bytes )
+{
+  int i;
+  int chunk;
+  int function;
+
+
+  // First determine what we're doing
+  if (strcmp( m->desc, "flash" ) == 0) {
+    function = USBTINY_FLASH_READ;
+  } else {
+    function = USBTINY_EEPROM_READ;
+  }
+
+  for (i = 0; i < n_bytes; i += chunk) {
+    chunk = chunk_size;         // start with the maximum chunk size possible
+
+    // If we want to xmit less than a chunk, thats OK
+    if	(chunk > n_bytes-i)
+      chunk = n_bytes - i;
+
+    // Send the chunk of data to the USBtiny with the function we want
+    // to perform
+    usb_in(function,          // EEPROM or flash
+	   0,                 // delay between SPI commands
+	   i,                 // index
+	   m->buf + i,        // pointer to where we store data
+	   chunk,             // number of bytes
+	   32 * sck_period);  // each byte gets turned into a 4-byte SPI cmd
+                              // usb_in() multiplies this per byte.
+
+    // Tell avrdude how we're doing to provide user feedback
+    report_progress(i + chunk, n_bytes, NULL );
+  }
+
+  return n_bytes;
+}
+
+/* To speed up programming and reading, we do a 'chunked' write.
+ *  We send just the data itself and the USBtiny uses the SPI function
+ *  given to write the data. Much faster than sending a 4-byte SPI request
+ *  per byte.
+*/
+static int usbtiny_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+			       int page_size, int n_bytes)
+{
+  int i;
+  int chunk;        // Size of data to write at once
+  int next;
+  int function;     // which SPI command to use
+  int delay;        // delay required between SPI commands
+
+  // First determine what we're doing
+  if (strcmp( m->desc, "flash" ) == 0) {
+    function = USBTINY_FLASH_WRITE;
+  } else {
+    function = USBTINY_EEPROM_WRITE;
+  }
+
+  delay = 0;
+  if (! m->paged) {
+    // Does this chip not support paged writes?
+    i = (m->readback[1] << 8) | m->readback[0];
+    usb_control( USBTINY_POLL_BYTES, i, 0 );
+    delay = m->max_write_delay;
+  }
+
+  for (i=0; i < n_bytes; i=next) {
+    // start with the max chunk size
+    chunk = chunk_size;
+
+    // we can only write a page at a time anyways
+    if (m->paged && chunk > page_size)
+      chunk = page_size;
+
+    // if there's less data remaining than one chunk
+    if (chunk > n_bytes-i)
+      chunk = n_bytes - i;
+
+    usb_out(function,       // Flash or EEPROM
+	    delay,          // How much to wait between each byte
+	    i,              // Index of data
+	    m->buf + i,     // Pointer to data
+	    chunk,          // Number of bytes to write
+	    32 * sck_period + delay  // each byte gets turned into a
+	                             // 4-byte SPI cmd  usb_in() multiplies
+	                             // this per byte. Then add the cmd-delay
+	    );
+
+    next = i + chunk;       // Calculate what address we're at now
+    if (m->paged
+	&& ((next % page_size) == 0 || next == n_bytes) ) {
+      // If we're at a page boundary, send the SPI command to flush it.
+      avr_write_page(pgm, p, m, (unsigned long) i);
+    }
+
+    report_progress( next, n_bytes, NULL );
+  }
+  return n_bytes;
+}
+
+extern void usbtiny_initpgm ( PROGRAMMER* pgm )
+{
+  strcpy(pgm->type, "USBtiny");
+
+  /* Mandatory Functions */
+  pgm->initialize	= usbtiny_initialize;
+  pgm->enable	        = usbtiny_enable;
+  pgm->disable	        = usbtiny_disable;
+  pgm->program_enable	= NULL;
+  pgm->chip_erase	= usbtiny_chip_erase;
+  pgm->cmd		= usbtiny_cmd;
+  pgm->open		= usbtiny_open;
+  pgm->close		= usbtiny_close;
+  pgm->read_byte        = avr_read_byte_default;
+  pgm->write_byte       = avr_write_byte_default;
+
+  /* Optional Functions */
+  pgm->powerup	        = NULL;
+  pgm->powerdown	= usbtiny_powerdown;
+  pgm->paged_load	= usbtiny_paged_load;
+  pgm->paged_write	= usbtiny_paged_write;
+  pgm->set_sck_period	= usbtiny_set_sck_period;
+}
+
+#else  /* !HAVE_LIBUSB */
+
+// Give a proper error if we were not compiled with libusb
+
+static int usbtiny_nousb_open(struct programmer_t *pgm, char * name)
+{
+  fprintf(stderr, "%s: error: no usb support. Please compile again with libusb installed.\n",
+	  progname);
+
+  exit(1);
+}
+
+void usbtiny_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "usbtiny");
+
+  pgm->open = usbtiny_nousb_open;
+}
+
+#endif /* HAVE_LIBUSB */
diff --git a/usbtiny.h b/usbtiny.h
new file mode 100644
index 00000000..77f9a42e
--- /dev/null
+++ b/usbtiny.h
@@ -0,0 +1,76 @@
+/*
+ * 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 usbtiny_h
+#define usbtiny_h
+
+#include "avrpart.h"
+
+// these are specifically assigned to USBtiny,
+// if you need your own VID and PIDs you can get them for cheap from
+// www.mecanique.co.uk so please don't reuse these. Thanks!
+#define USBTINY_VENDOR  0x1781
+#define USBTINY_PRODUCT 0x0C9F
+
+// Generic requests to the USBtiny
+#define	USBTINY_ECHO 	     0      // echo test
+#define	USBTINY_READ         1	    // read byte (wIndex:address)
+#define	USBTINY_WRITE 	     2	    // write byte (wIndex:address, wValue:value)
+#define	USBTINY_CLR          3	    // clear bit (wIndex:address, wValue:bitno)
+#define	USBTINY_SET          4	    // set bit (wIndex:address, wValue:bitno)
+
+// Programming requests
+#define	USBTINY_POWERUP      5	    // apply power (wValue:SCK-period, wIndex:RESET)
+#define	USBTINY_POWERDOWN    6	    // remove power from chip
+#define	USBTINY_SPI          7	    // issue SPI command (wValue:c1c0, wIndex:c3c2)
+#define	USBTINY_POLL_BYTES   8	    // set poll bytes for write (wValue:p1p2)
+#define	USBTINY_FLASH_READ   9	    // read flash (wIndex:address)
+#define	USBTINY_FLASH_WRITE  10	    // write flash (wIndex:address, wValue:timeout)
+#define	USBTINY_EEPROM_READ  11	    // read eeprom (wIndex:address)
+#define	USBTINY_EEPROM_WRITE 12	    // write eeprom (wIndex:address, wValue:timeout)
+
+
+
+// Flags to indicate how to set RESET on power up
+#define	RESET_LOW	0
+#define	RESET_HIGH	1
+
+// The SCK speed can be set by avrdude, to allow programming of slow-clocked parts
+#define	SCK_MIN		1	// usec delay (target clock >= 4 MHz)
+#define	SCK_MAX		250	// usec (target clock >= 16 KHz)
+#define	SCK_DEFAULT	10	// usec (target clock >= 0.4 MHz)
+
+// How much data, max, do we want to send in one USB packet?
+#define	CHUNK_SIZE	128	// must be power of 2 less than 256
+
+// The default USB Timeout
+#define	USB_TIMEOUT	500	// msec
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void usbtiny_initpgm (PROGRAMMER * pgm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* usbtiny_h */