avrdude-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avrdude-dev] [PATCH] avrdude + libftdi, on Linux


From: David Brownell
Subject: [avrdude-dev] [PATCH] avrdude + libftdi, on Linux
Date: Sun, 22 Oct 2006 21:00:06 -0700
User-agent: KMail/1.7.1

Here's a patch for "libftdi" based FTDI bitbanging, tested on Linux and based
on the earlier Win32-only version (which linked against a proprietary FTDI
closed source library) found at:

  https://savannah.nongnu.org/patch/?4330

When libusb is available, "configure" also checks for libftdi.  When both
are available, an "ftbb" programer type is made available; otherwise there
should be no visible difference beyond avrdude.conf accepting a new "ftbb"
programmer type.  (But I expect I missed a few expectations about how to
configure this ...)

You need to specify the /proc/bus/usb/BBB/DDD path, "-P BBB/DDD", for the
device; that's a bit annoying since DDD is not stable, but that problem
comes from libusb via usbfs and there's no good alternative.  It's also
not quite the same as what usb_libusb.c does, but that's neither exported
nor particularly sensible either (overloading the clocking parameter).

Tested using a mega32 on a USB-powered Olimex breadboard which integrates
an ft232bl.  That's the worst case in terms of robustness since B series
chips don't directly support RX/TX synchronization.  If you don't specify
a "baudrate" it defaults to something that worked ok for me ... at least,
enough to flash a serial loader that works robustly with ftdi_sio.  The
whole clocking thing here looks to be a mess that may need to be sorted
out with help from non-public specs, and may never work well on ft232b.

Newer chips (ft2232, ft232r etc) have in particular a mode where the TX and
RX can be synchronized, which should be much more robust as well as handling
far higher bitrates.  However, those remain untested.


I hope some other folk find this useful, but I'm not going to have time
to take this to the next level myself.  I'll suggest that someone with
interest in such things get an ft232r based setup [1] and see how speedy
and robust you can make this.  I'd not be surprised to learn that libftdi
needs updates for those current generation parts.

- Dave

[1] One thought would be to combine a ft232r breakout board (like the one
    SparkFun.com sells for $15) with a Mega8L (or somesuch) and LEDS on a
    solderless breadboard.  Run at 3.3V with just USB power.


Index: avrdude/avrdude.conf.in
===================================================================
--- avrdude.orig/avrdude.conf.in        2006-10-22 20:07:48.000000000 -0700
+++ avrdude/avrdude.conf.in     2006-10-22 20:10:16.000000000 -0700
@@ -703,6 +703,44 @@ programmer
 ;
 
 #
+# FTDI USB adapters of 'B' generation (ft232bm, ft245bm, etc) and newer
+# ('C' generation: ft232r, ft2232, etc) have a "bitbang" mode.
+#
+# "pin"   ft232   ft245
+# -----   -----   -----
+#   1      TXD    data0
+#   2      RXD    data1
+#   3      RTS    data2
+#   4      CTS    data3
+#   5      DTR    data4
+#   6      DSR    data5
+#   7      DCD    data6
+#   8      RI     data7
+#
+
+# p40usb-isp -- protoboard, jumpered for:  reset=CTS sck=DTR mosi=DSR miso=RTS
+programmer
+  id       = "p40usb-isp";
+  desc     = "ft232b bitbanging, Olimex p40usb jumpered to ICSP";
+  type     = ftbb;
+  reset    = 4;
+  sck      = 5;
+  mosi     = 6;
+  miso     = 3;
+;
+
+programmer
+  id       = "ezdop";
+  desc     = "AE6HO EZ-Doppler";
+  type     = ftbb;
+  reset    = 5;
+  sck      = 3;
+  mosi     = 2;
+  miso     = 1;
+;
+
+
+#
 # PART DEFINITIONS
 #
 
Index: avrdude/ftbb.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ avrdude/ftbb.h      2006-10-22 20:10:16.000000000 -0700
@@ -0,0 +1,28 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004  Theodore A. Roth  <address@hidden>
+ * Copyright (C) 2005 Johnathan Corgan
+ *
+ * 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 __ftbb_h__
+#define __ftbb_h__
+
+#include "avrpart.h"
+
+void ftbb_initpgm (PROGRAMMER *pgm);
+
+#endif /* __ftbb_h__ */
Index: avrdude/config_gram.y
===================================================================
--- avrdude.orig/config_gram.y  2006-10-22 20:07:48.000000000 -0700
+++ avrdude/config_gram.y       2006-10-22 20:10:16.000000000 -0700
@@ -43,6 +43,7 @@
 #include "avr.h"
 #include "jtagmkI.h"
 #include "jtagmkII.h"
+#include "ftbb.h"
 
 #if defined(WIN32NATIVE)
 #define strtok_r( _s, _sep, _lasts ) \
@@ -131,6 +132,7 @@ static int parse_cmdbits(OPCODE * op);
 %token K_AVR910
 %token K_USBASP
 %token K_BUTTERFLY
+%token K_FTBB
 %token K_TYPE
 %token K_VCC
 %token K_VFYLED
@@ -437,6 +439,12 @@ prog_parm :
     }
   } |
 
+  K_TYPE TKN_EQUAL K_FTBB {
+    {
+      ftbb_initpgm(current_prog);
+    }
+  } |
+
   K_DESC TKN_EQUAL TKN_STRING {
     strncpy(current_prog->desc, $3->value.string, PGM_DESCLEN);
     current_prog->desc[PGM_DESCLEN-1] = 0;
Index: avrdude/lexer.l
===================================================================
--- avrdude.orig/lexer.l        2006-10-22 20:07:48.000000000 -0700
+++ avrdude/lexer.l     2006-10-22 20:10:16.000000000 -0700
@@ -140,6 +140,7 @@ eeprom           { yylval=NULL; return K
 enablepageprogramming { yylval=NULL; return K_ENABLEPAGEPROGRAMMING; }
 errled           { yylval=NULL; return K_ERRLED; }
 flash            { yylval=NULL; return K_FLASH; }
+ftbb             { yylval=NULL; return K_FTBB; }
 has_jtag         { yylval=NULL; return K_HAS_JTAG; }
 id               { yylval=NULL; return K_ID; }
 idr              { yylval=NULL; return K_IDR; }
Index: avrdude/ftbb_libftdi.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ avrdude/ftbb_libftdi.c      2006-10-22 20:10:16.000000000 -0700
@@ -0,0 +1,861 @@
+/*
+ * ftbb_libftdi -- ft232x bitbang SPI interface for avrdude
+ *
+ * Copyright (C) 2006 David Brownell
+ *
+ * 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
+ */
+
+#include "ac_cfg.h"
+#ifdef HAVE_LIBFTDI
+
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#include <ftdi.h>
+
+#include "avr.h"
+#include "pgm.h"
+#include "bitbang.h"
+#include "ftbb.h"
+
+
+/*
+ * FTDI 'B' generation parts (like the ft232bm) and newer 'C' generation
+ * ones (ft232r etc) support a bitbang mode as well as uart or parallel
+ * interfacing.
+ *
+ * To access this mode, the kernel FTDI driver must **NOT** be bound to the
+ * device; and permissions on the usbfs /proc/bus/usb/BUS/DEV file (that's
+ * assuming Linux) must be set appropriately.  If your FTDI device shows in
+ * /proc/bus/usb/devices like this no-eeprom ft232bl board:
+ *
+ *   T:  Bus=03 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 32 Spd=12  MxCh= 0
+ *       ^^^^^^                               ^^^^^^^^
+ *   D:  Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+ *   P:  Vendor=0403 ProdID=6001 Rev= 4.00
+ *   S:  Manufacturer=FTDI
+ *   S:  Product=USB <-> Serial
+ *   C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 90mA
+ *   I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
+ *   E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
+ *   E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
+ *
+ * The pathname used for that device will be 003/032.  Manufacturer and
+ * product strings may have been changed by the device's EEPROM, like the
+ * vendor and product ID codes.
+ */
+
+#define nRESET (1 << (pgm->pinno[PIN_AVR_RESET] - 1))
+#define SCK    (1 << (pgm->pinno[PIN_AVR_SCK]   - 1))
+#define MOSI   (1 << (pgm->pinno[PIN_AVR_MOSI]  - 1))
+#define MISO   (1 << (pgm->pinno[PIN_AVR_MISO]  - 1))
+
+#define TX_FIFO_SIZE           128
+#define RX_FIFO_SIZE           384             /* 256 on ft232r */
+
+#define ERR_LOST_SYNCH         -10
+#define ERR_IDCODE             -8
+
+#define RETRY_COUNT            30
+
+#define MAX_B_BAUD             4800
+
+
+// #define DEBUG
+
+extern int verbose;
+
+static struct ftdi_context     ftdi;
+static unsigned char           txbits;
+
+
+static void msleep(unsigned msec)
+{
+       struct timespec         req, rem;
+
+       req.tv_sec = msec / 1000;;
+       req.tv_nsec = 1000000 * (msec % 1000);
+
+       while (nanosleep(&req, &rem) < 0)
+               req = rem;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* open/close a bitbang channel through an FTDI chip to an AVR device */
+
+static void ftbb_close(PROGRAMMER *pgm)
+{
+       int                     status;
+
+       if (!ftdi.readbuffer)
+               return;
+
+       if (ftdi.bitbang_enabled) {
+               status = ftdi_disable_bitbang(&ftdi);
+               if (status < 0)
+                       fprintf(stderr, "%s: error %s bitbang: %s\n",
+                                       pgm->type, "stopping", ftdi.error_str);
+       }
+
+       ftdi_usb_close(&ftdi);
+       ftdi_deinit(&ftdi);
+}
+
+static int ftbb_open(PROGRAMMER *pgm, char *name)
+{
+       struct usb_bus          *bus;
+       struct usb_device       *udev = NULL;
+       int                     status;
+
+       /* REVISIT this assumes no CBUS support on newer chips,
+        * and doesn't support the B channel on 2232 chips.
+        */
+       bitbang_check_prerequisites(pgm);
+       if (pgm->pinno[PIN_AVR_RESET] > 8
+                       || pgm->pinno[PIN_AVR_SCK] > 8
+                       || pgm->pinno[PIN_AVR_MOSI] > 8
+                       || pgm->pinno[PIN_AVR_MISO] > 8) {
+               fprintf(stderr, "%s: invalid pin number\n", pgm->type);
+               return -1;
+       }
+
+
+       /* map 'name' to a USB device
+        *
+        * NOTE yes this naming scheme is lousy, since it changes each time
+        * the device enumerates (/proc/bus/usb/BUS/DEV).  Unfortunately,
+        * that's what libusb exposes.  Recent Linux kernels have hooks for
+        * positional naming, visible through sysfs but without an API.
+        */
+       usb_init();
+       status = usb_find_busses();
+       if (status < 0) {
+               fprintf(stderr, "%s: error %s %s: %s\n",
+                               pgm->type, "scanning USB busses",
+                               name, usb_strerror());
+               return -1;
+       }
+
+       status = usb_find_devices();
+       if (status < 0) {
+               fprintf(stderr, "%s: error %s %s: %s\n",
+                               pgm->type, "scanning USB devices",
+                               name, usb_strerror());
+               return -1;
+       }
+
+       /* NOTE:  we don't consider product/vendor codes since they can
+        * trivially be altered by EEPROM to be something we won't know.
+        */
+       for (bus = usb_get_busses(); bus; bus = bus->next) {
+               char            usb_name[12];
+
+               for (udev = bus->devices; udev; udev = udev->next) {
+                       sprintf(usb_name, "%s/%s",
+                                       bus->dirname,
+                                       udev->filename);
+                       if (verbose >= 4)
+                               fprintf(stderr, "%s: found '%s', want '%s'\n",
+                                               pgm->type, usb_name, name);
+                       if (strcmp(usb_name, name) == 0)
+                               goto found;
+               }
+       }
+       fprintf(stderr, "%s: error %s %s: %s\n",
+                       pgm->type, "finding",
+                       name, "no such device");
+       return -1;
+
+
+found:
+       /* never read/write more than the chip's fifo holds */
+       ftdi_init(&ftdi);
+       (void) ftdi_write_data_set_chunksize(&ftdi, TX_FIFO_SIZE);
+       (void) ftdi_read_data_set_chunksize(&ftdi, RX_FIFO_SIZE);
+
+       /* NOTE: internal libftdi-0.7 bugs cancel each other out ... it sets
+        * up ftdi.in_ep with an OUT endpoint number, out_ep with an IN one;
+        * but then it writes to the IN and reads from the OUT.
+        */
+
+       status = ftdi_usb_open_dev(&ftdi, udev);
+       if (status < 0) {
+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+               /* REVISIT code 5 == "ftdi__sio is likely bound to it" ...
+                * usb_detach_kernel_driver_np() could fix that for us.
+                */
+#endif
+               fprintf(stderr, "%s: error %s %s: %s\n",
+                               pgm->type, "opening",
+                               name, ftdi.error_str);
+               goto fail;
+       }
+
+       if (!pgm->baudrate)
+               pgm->baudrate = MAX_B_BAUD;
+
+       /* FIXME find a reliable way to kick in SYNCBB on ft232r parts;
+        * one would expect that to be quite robust...
+        */
+
+       switch (ftdi.type) {
+       case TYPE_BM:
+               /* basic bitbang, without tx/rx synch */
+               ftdi.bitbang_mode = BITMODE_BITBANG;
+
+               /* we can't prevent rx fifo overruns (and data lossage)
+                * caused by process scheduling, and there also seem to
+                * be modes where the AVR gets and _stays_ confused...
+                */
+               if (pgm->baudrate > MAX_B_BAUD)
+                       fprintf(stderr, "%s: FT2xxB devices are chancey "
+                                       "at 'high' baudrates (%d)\n",
+                                       pgm->type, pgm->baudrate);
+               break;
+       case TYPE_2232C:
+               /* synchronized bitbang:  no rx without tx, so no overruns */
+               ftdi.bitbang_mode = BITMODE_SYNCBB;
+               break;
+       case TYPE_AM:
+               /* hardware can't bitbang */
+               fprintf(stderr, "%s: ft2xxA devices not supported\n",
+                               pgm->type);
+               goto fail;
+       }
+
+       /* Docs say:  bytes are clocked out of the TX fifo (and into the
+        * RX fifo) according to the 16x pre-sample baudrate clock ...
+        * with six clocks per byte (per timing diagrams showing the r/w
+        * strobes, exposed only in the newer parts).
+        *
+        * NOTE: measurements (multimeter) with bitstreams that toggle bits
+        * in successive bytes suggest the public docs are lying.  Not only
+        * is the duty cycle more like 30% than 50%, but the Hz observed
+        * don't match the formula that's derived from that spec info, at
+        * numerous speeds.  Not all of that can be measurement errors,
+        * and combining libftdi with bitbanging produces a variety of SPI
+        * clock rates that aren't monotonically increasing; peak observed
+        * was about 117 KHz.
+        *
+        * NOTE: libftdi-0.7 mangles the baudrate if bitbang is enabled; not
+        * clear what's up with that.  It seems to be just a fudge factor,
+        * since observed measurements have been 4x to 5x the requested
+        * rate, not consistently 4x  Plus, setting setting baudrate after
+        * enabling bitbang (vs before as done here) has caused trouble.
+        *
+        * NOTE: libftdi-0.7 has at least one obvious *BUG* here ... it
+        * computes an actual baud rate that may be different from the
+        * requested one, but reports the requested one (x4 for bitbang)
+        * not the actual one!!!
+        *
+        * REVISIT sort out this clocking mess ...
+        */
+       status = ftdi_set_baudrate(&ftdi, pgm->baudrate / 16);
+       if (status < 0) {
+               fprintf(stderr, "%s: error setting baud to %d: %s\n",
+                               pgm->type, pgm->baudrate / 16,
+                               ftdi.error_str);
+               goto fail;
+       }
+       pgm->baudrate = ftdi.baudrate * 16;
+
+       status = ftdi_enable_bitbang(&ftdi, MOSI | SCK | nRESET);
+       if (status < 0) {
+               fprintf(stderr, "%s: error %s bitbang: %s\n",
+                               pgm->type, "starting", ftdi.error_str);
+               goto fail;
+       }
+
+       /* SCK and nRESET must be low; MOSI too, by convention */
+       txbits = 0;
+       ftdi_write_data(&ftdi, &txbits, 1);
+       ftdi_write_data(&ftdi, &txbits, 1);
+       (void) ftdi_usb_purge_buffers(&ftdi);
+
+       return 0;
+
+fail:
+       ftbb_close(pgm);
+       return -1;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* To get decent speed, we write our SPI bitstream over bulk packets with
+ * embedded SCK toggling.  Each "baud" writes two bytes from the TX fifo,
+ * and reads two bytes back into the RX fifo.  After writing, we read back
+ * the result, getting our TX data mixed with MISO responses from the AVR.
+ *
+ * For the C series, with SYNCBB support, that should happen reliably; the
+ * hardware maps short full duplex SPI transactions into half duplex, by
+ * coupling the RX and TX fifos.
+ *
+ * Not so for the B series, which doesn't sync RX with TX ... we have no
+ * way to ensure the RX fifo isn't so full of garbage that it'll discard
+ * the response data we care about.  A kernel driver could do a much better
+ * job on the synchronization, ditto direct access through Linux's usbfs,
+ * but neither libftdi nor libusb cooperate with such requirements.
+ *
+ * So for B series parts we must either:
+ *   (a) ignore the response, e.g. for write messages it never matters.
+ *   (b) retry, e.g. for reads it's always safe.
+ *   (c) synchronize ourselves by sticking a marker in the stream before
+ *       commands ... while SCK is low we can encode data using MOSI, thus
+ *       solving two problems:  finding the response data, and noticing
+ *       when the RX fifo discards it.
+ *   (d) use multiple USB messages per bit ... VERY SLOW.
+ *
+ * For now we won't implement (d).
+ */
+
+
+/* one SPI bit per baud ... encoded in two USB bytes */
+static inline unsigned char *
+bangbit(PROGRAMMER *pgm, unsigned char *bp, int bit,
+               unsigned char sck, unsigned char mosi)
+{
+       unsigned char   tx = txbits;
+
+       /* txbits has nRESET and LED values set, SCK and MOSI clear */
+
+       if (bit)
+               tx |= mosi;
+       *bp++ = tx;
+
+       tx |= sck;
+       *bp++ = tx;
+
+       return bp;
+}
+
+/* sixteen USB bytes per SPI byte */
+static unsigned char *
+bangbyte(PROGRAMMER *pgm, unsigned char *bp, int byte)
+{
+       unsigned        i;
+       unsigned char   sck = SCK, mosi = MOSI;
+
+       for (i = 0; i < 8; i++, byte <<= 1)
+               bp = bangbit(pgm, bp, byte & 0x80, sck, mosi);
+       return bp;
+}
+
+static unsigned char
+unbang(PROGRAMMER *pgm, unsigned char **bpp)
+{
+       unsigned        i;
+       unsigned        byte, miso = MISO;
+       unsigned char   *bp = *bpp;
+
+       for (i = 0, byte = 0; /* NOP */; byte <<= 1, bp += 2) {
+               if (*bp & miso)
+                       byte |= 1;
+               if (i++ < 8)
+                       continue;
+               break;
+       }
+
+       *bpp = bp;
+       return byte;
+}
+
+/* marks are not needed when ftdi.bitbang_mode == BITMODE_SYNCBB ... */
+static unsigned char *
+bangmark(PROGRAMMER *pgm, unsigned char *bp)
+{
+       unsigned        i;
+       unsigned char   b1 = txbits | MOSI;
+       unsigned char   b2 = txbits;
+
+       for (i = 0; i < 8; i++)
+               *bp++ = b1;
+       for (i = 0; i < 8; i++)
+               *bp++ = b2;
+       return bp;
+}
+
+static unsigned char *
+findmark(PROGRAMMER *pgm, unsigned char *bp, int len)
+{
+       unsigned char   *bp0 = bp;
+       unsigned        i, j;
+       unsigned char   byte, mask = SCK | MOSI;
+       unsigned char   b1 = MOSI, b2 = 0;
+       int             l = len;
+
+       /* There's an indeterminate amount of garbage at the front of this
+        * buffer; find the mark so we can skip it.
+        */
+       for (i = 0; i < len; i++) {
+               for (j = 0; j < 8 && i < len; j++, i++) {
+                       byte = *bp++ & mask;
+                       if (byte != b1)
+                               break;
+               }
+               if (j != 8)
+                       continue;
+
+               for (j = 0; j < 8 && i < len; j++, i++) {
+                       byte = *bp++ & mask;
+                       if (byte != b2)
+                               break;
+               }
+               if (j == 8) {
+                       /* return bytestream where bp[0,2,4,...]
+                        * all have valid MISO bits, and the length
+                        * is enough to hold the whole response.
+                        */
+                       bp -= 2;
+                       len = l - (bp - bp0);
+                       if (len < (16 * 4))
+                               break;
+                       return bp;
+               }
+       }
+
+       return NULL;
+}
+
+#ifdef DEBUG
+static char *sep(int offset)
+{
+       offset++;
+       if (offset % 8)
+               return " ";
+       if (offset % 16)
+               return "  ";
+       if (offset % 32)
+               return " - ";
+       return "\n";
+}
+#endif
+
+static int
+fastcmd(PROGRAMMER *pgm, unsigned char cmd[4], unsigned char res[4], int err)
+{
+       unsigned char   tx[TX_FIFO_SIZE];
+       unsigned char   rx[RX_FIFO_SIZE];
+       unsigned char   *bp = tx;
+       unsigned        len;
+       int             status;
+
+       txbits &= ~(SCK | MOSI);
+       bp = bangmark(pgm, bp);
+       bp = bangbyte(pgm, bp, cmd[0]);
+       bp = bangbyte(pgm, bp, cmd[1]);
+       bp = bangbyte(pgm, bp, cmd[2]);
+       bp = bangbyte(pgm, bp, cmd[3]);
+       *bp++ = txbits;
+       len = bp - tx;
+
+       /* the RX fifo discards new data when the fifo is full, so we want it
+        * to start without any data.
+        *
+        * NOTE:  libftdi-0.7 issue, it purges RX before TX, so for B-series
+        * chips the RX buffer is **GUARANTEED** to start with more garbage
+        * than is necessary, thereby promoting data lossage ... otherwise
+        * it'd be reasonable to ftdi_usb_purge_buffers().
+        *
+        * NOTE:  hmm, this RX purge still doesn't seem to behave well; it's
+        * been observed to sometimes kick in _after_ the written data, or
+        * maybe in conjunction with corruption of the written data...
+        *
+        * REVISIT not needed for SYNCBB?
+        */
+       if (usb_control_msg(ftdi.usb_dev, 0x40, 0, 1,
+                               ftdi.index, NULL, 0,
+                               ftdi.usb_write_timeout) != 0) {
+               fprintf(stderr, "%s: %s error: %s\n",
+                               pgm->type, "ftbb_cmd rx purge",
+                               strerror(errno));
+               return -1;
+       }
+
+       status = ftdi_write_data(&ftdi, tx, len);
+       if (status < 0) {
+               fprintf(stderr, "%s: %s error: %s\n",
+                               pgm->type, "ftbb_cmd write",
+                               ftdi.error_str);
+               return status;
+       }
+
+       /* read back result, then skip the marker */
+       if (ftdi.bitbang_mode == BITMODE_BITBANG)
+               len = sizeof rx /* or a bit less on ft232r... */;
+       status = ftdi_read_data(&ftdi, rx, len);
+       if (status < 0) {
+               fprintf(stderr, "%s: %s error: %s\n",
+                               pgm->type, "ftbb_cmd read",
+                               ftdi.error_str);
+               return status;
+       }
+
+#ifdef DEBUG
+       if (verbose >= 3) {
+               int i;
+
+               printf("\n... SCK = %02x, MOSI = %02x, "
+                               "MISO = %02x, nRESET = %02x ...\n",
+                               SCK, MOSI, MISO, nRESET);
+               printf("TX:\n");
+               for (i = 0; i < (bp - tx); i++)
+                       printf("%02x%s", tx[i], sep(i));
+               printf("\nRX:\n");
+               for (i = 0; i < len; i++) {
+                       rx[i] &= SCK | MISO | MOSI | nRESET;
+                       printf("%02x%s", rx[i], sep(i));
+               }
+               printf("\n");
+       }
+#endif
+
+       bp = findmark(pgm, rx, sizeof rx);
+       if (!bp) {
+               /* RX fifo filled before we got our data.  For idempotent
+                * requests like reads, no worry -- just retry.
+                */
+               if (err || verbose > 1)
+                       fprintf(stderr, "%s: %s error: %s\n",
+                               pgm->type, "ftbb_fastcmd", "overrun");
+
+               return ERR_LOST_SYNCH;
+       }
+
+       res[0] = unbang(pgm, &bp);
+       res[1] = unbang(pgm, &bp);
+       res[2] = unbang(pgm, &bp);
+       res[3] = unbang(pgm, &bp);
+
+       if (verbose >= 2) {
+               int             i;
+
+               fprintf(stderr, "%s: [ ", "ftbb_fastcmd");
+               for(i = 0; i < 4; i++)
+                       fprintf(stderr, "%02x ", cmd[i]);
+               fprintf(stderr, "] --> [ ");
+               for(i = 0; i < 4; i++)
+                       fprintf(stderr, "%02x ", res[i]);
+               fprintf(stderr, "]\n");
+       }
+
+       return 0;
+}
+
+static int
+ftbb_cmd(PROGRAMMER *pgm, unsigned char cmd[4], unsigned char res[4])
+{
+       int             status;
+
+       status = fastcmd(pgm, cmd, res, 1);
+
+       return status;
+}
+
+/* reads are idempotent, so we retry after loss-of-synch */
+static int
+ftbb_read_byte(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m,
+                 unsigned long addr, unsigned char *bytep)
+{
+       OPCODE          *op = NULL;
+       unsigned char   cmd[4];
+       unsigned char   res[4];
+       unsigned        do_retry = RETRY_COUNT;
+       int             status;
+
+       /* REVISIT this "LOAD_EXT_ADDR" needs coding and testing */
+       if (m->op[AVR_OP_LOAD_EXT_ADDR])
+               return -1;
+
+       if (m->op[AVR_OP_READ_LO]) {
+               op = m->op[(addr & 1) ? AVR_OP_READ_HI : AVR_OP_READ_LO];
+               addr = addr / 2;
+       } else
+               op = m->op[AVR_OP_READ];
+
+       if (op == NULL)
+               return -1;
+
+       memset(cmd, 0, sizeof(cmd));
+       avr_set_bits(op, cmd);
+       avr_set_addr(op, cmd, addr);
+       *bytep = 0;
+
+       /* retry, ignoring loss-of-sync errors */
+       do {
+               status = fastcmd(pgm, cmd, res, 0);
+               if (status != ERR_LOST_SYNCH)
+                       break;
+
+               /* FIXME sometimes the chip seems to enter a mode where
+                * cmd [ ww xx yy zz ] --> res [ 00 ww xx DD ], where
+                * DD == yy **NOT** the correct data!!, after synch loss...
+                * maybe chip reset using a program_enable() shaped hammer
+                * will be needed.
+                */
+               msleep(1);
+       } while (do_retry--);
+
+       if (status == 0)
+               avr_get_output(op, res, bytep);
+       return status;
+}
+
+#ifdef WRITEBYTE
+/* writes involve no reply, so ignore the synch stuff */
+static int
+ftbb_write_byte(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m,
+                 unsigned long addr, unsigned char byte)
+{
+       OPCODE          *op = NULL;
+       unsigned char   cmd[4];
+       unsigned char   tx[TX_FIFO_SIZE];
+       unsigned char   *bp = tx;
+       unsigned        len;
+       int             status;
+
+       /* flash */
+       if (m->paged) {
+               if (m->op[AVR_OP_LOADPAGE_LO]) {
+                       op = m->op[(addr & 1)
+                                       ? AVR_OP_LOADPAGE_HI
+                                       : AVR_OP_LOADPAGE_LO];
+                       addr >>= 1;
+               }
+#if 0
+       /* eeprom, fuse/hfuse/lfuse/efuse, lock, ... */
+       } else {
+               /* REVISIT ftbb_read_byte, then check value to
+                * see if we need to write it ... that way we'll
+                * avoid false errors if writes lose synch.
+                */
+               if (m->op[AVR_OP_WRITE_LO]) {
+                       op = m->op[(addr & 1)
+                                       ? AVR_OP_WRITE_HI
+                                       : AVR_OP_WRITE_LO];
+                       addr >>= 1;
+               } else
+                       op = m->op[AVR_OP_WRITE];
+#endif
+       }
+       if (op == NULL)
+               return -1;
+
+       memset(cmd, 0, sizeof cmd);
+       avr_set_bits(op, cmd);
+       avr_set_addr(op, cmd, addr);
+       avr_set_input(op, cmd, byte);
+
+       txbits &= ~(SCK | MOSI);
+       /* NO MARKER */
+       bp = bangbyte(pgm, bp, cmd[0]);
+       bp = bangbyte(pgm, bp, cmd[1]);
+       bp = bangbyte(pgm, bp, cmd[2]);
+       bp = bangbyte(pgm, bp, cmd[3]);
+       *bp++ = txbits;
+       len = bp - tx;
+
+       status = ftdi_write_data(&ftdi, tx, len);
+       if (status < 0)
+               fprintf(stderr, "%s: %s error: %s\n",
+                               pgm->type, "ftbb_write_byte",
+                               ftdi.error_str);
+       else if (verbose >= 2) {
+               int             i;
+
+               fprintf(stderr, "%s: [ ", "ftbb_write_byte");
+               for(i = 0; i < 4; i++)
+                       fprintf(stderr, "%02x ", cmd[i]);
+               fprintf(stderr, "] --> [ ignored ]\n");
+       }
+
+
+       return status;
+}
+#endif /* WRITEBYTE */
+
+/*-----------------------------------------------------------------------*/
+
+static int ftbb_program_enable(PROGRAMMER *pgm, AVRPART *p)
+{
+       unsigned char   cmd[4];
+       unsigned char   res[4];
+       int             status;
+
+       if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
+               fprintf(stderr, "%s: %s has no %s instruction\n",
+                               pgm->type, p->desc, "program enable");
+               return -1;
+       }
+
+       memset(cmd, 0, sizeof(cmd));
+       avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
+       status = ftbb_cmd(pgm, cmd, res);
+
+       /* retry the init sequence (for most AVRs) */
+       if (status == 0 && res[2] != cmd[1]) {
+               if (verbose)
+                       fprintf(stderr, "%s: %s %s result[2] != %#02x??\n",
+                                       pgm->type, p->desc,
+                                       "program enable",
+                                       cmd[1]);
+               status = ERR_IDCODE;
+       }
+       return status;
+}
+
+static int ftbb_chip_erase(PROGRAMMER *pgm, AVRPART *p)
+{
+       unsigned char   cmd[4];
+       unsigned char   res[4];
+       int             status;
+
+       if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+               fprintf(stderr, "%s: %s has no %s instruction\n",
+                               pgm->type, p->desc, "chip erase");
+               return -1;
+       }
+
+       memset(cmd, 0, sizeof(cmd));
+       avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
+       status = ftbb_cmd(pgm, cmd, res);
+       usleep(p->chip_erase_delay);
+
+       return status;
+}
+
+/*-----------------------------------------------------------------------*/
+
+static void ftbb_reset(PROGRAMMER *pgm)
+{
+       int     status;
+
+       /* lower SCK and reset chip to prepare for serial programming */
+       txbits &= ~(SCK | MOSI | nRESET);
+       status = ftdi_write_data(&ftdi, &txbits, 1);
+       if (status < 0)
+               fprintf(stderr, "%s: %s error, %s\n",
+                               pgm->type, "reset", ftdi.error_str);
+}
+
+static void ftbb_endreset(PROGRAMMER *pgm)
+{
+       int     status;
+
+       if (!ftdi.readbuffer)
+               return;
+
+       /* lower SCK and take out of reset */
+       txbits &= ~(SCK | MOSI);
+       txbits |= nRESET;
+
+       status = ftdi_write_data(&ftdi, &txbits, 1);
+       if (status < 0)
+               fprintf(stderr, "%s: %s error, %s\n",
+                               pgm->type, "end reset", ftdi.error_str);
+}
+
+static int ftbb_initialize(PROGRAMMER *pgm, AVRPART *p)
+{
+       unsigned        do_retry = RETRY_COUNT;
+       int             status;
+
+       /* Atmel documentation says that to be sure we can start SPI
+        * serial programming we must pulse nRESET high for 2+ cpu clocks
+        * after sclk was set low, then wait 20 ms before program_enable.
+        */
+       do {
+               (void) ftbb_endreset(pgm);
+               msleep(1);
+               (void) ftbb_reset(pgm);
+
+               msleep(20);
+               status = ftbb_program_enable(pgm, p);
+
+               /* out-of-sync? */
+               if (status == ERR_LOST_SYNCH)
+                       status = ERR_IDCODE;
+
+       } while (status == ERR_IDCODE && do_retry--);
+       return status;
+}
+
+/*-----------------------------------------------------------------------*/
+
+static void ftbb_display(PROGRAMMER *pgm, char *spaces)
+{
+       /* NOTE:  these names assume a usb<-->serial adapter,
+        * not a usb<-->fifo part like ft245
+        */
+       static const char *pin[9] = { "(NO PIN)",
+               "txd", "rxd", "rts", "cts",
+               "dtr", "dsr", "dcd", "ri"
+               };
+
+       printf("%s  %s: ftdi %s bitbang mode\n",
+               spaces, pgm->type,
+               (ftdi.bitbang_mode == BITMODE_SYNCBB)
+                       ? "synchronous" : "async"
+               );
+       printf("%s  %s: nRESET uses %s\n",
+               spaces, pgm->type, pin[pgm->pinno[PIN_AVR_RESET]]);
+       printf("%s  %s: SCK    uses %s\n",
+               spaces, pgm->type, pin[pgm->pinno[PIN_AVR_SCK]]);
+       printf("%s  %s: MOSI   uses %s\n",
+               spaces, pgm->type, pin[pgm->pinno[PIN_AVR_MOSI]]);
+       printf("%s  %s: MISO   uses %s\n",
+               spaces, pgm->type, pin[pgm->pinno[PIN_AVR_MISO]]);
+
+       /* REVISIT: there are four more pins, even when CBUS isn't
+        * available ... let there be LEDs!
+        */
+}
+
+/*-----------------------------------------------------------------------*/
+
+void ftbb_initpgm (PROGRAMMER *pgm)
+{
+       strcpy(pgm->type, "ftbb");
+       txbits = nRESET;
+
+       /* mandatory: */
+       pgm->initialize = ftbb_initialize;
+       pgm->display = ftbb_display;
+       pgm->enable = ftbb_reset;
+       pgm->disable = ftbb_endreset;
+       pgm->program_enable = ftbb_program_enable;
+       pgm->chip_erase = ftbb_chip_erase;
+       pgm->cmd = ftbb_cmd;
+       pgm->open = ftbb_open;
+       pgm->close = ftbb_close;
+
+       /* smarter/optimized versions */
+       pgm->read_byte = ftbb_read_byte;
+#ifdef WRITEBYTE
+       pgm->write_byte = ftbb_write_byte;
+#endif /* WRITEBYTE */
+}
+
+#else
+
+void ftbb_initpgm (PROGRAMMER *pgm)
+{
+       /* REVISIT does this fail cleanly enough? */
+}
+
+#endif
+
Index: avrdude/Makefile.am
===================================================================
--- avrdude.orig/Makefile.am    2006-10-22 20:07:48.000000000 -0700
+++ avrdude/Makefile.am 2006-10-22 20:10:16.000000000 -0700
@@ -47,7 +47,7 @@ avrdude_CPPFLAGS = -DCONFIG_DIR=\"$(sysc
 
 avrdude_CFLAGS   = @ENABLE_WARNINGS@  
 
-avrdude_LDADD  = @LIBUSB@
+avrdude_LDADD  = @LIBFTDI@ @LIBUSB@
 
 bin_PROGRAMS = avrdude
 
@@ -88,6 +88,8 @@ avrdude_SOURCES = \
        fileio.c \
        fileio.h \
        freebsd_ppi.h \
+       ftbb_libftdi.c \
+       ftbb.h \
        jtagmkI.c \
        jtagmkI.h \
        jtagmkI_private.h \
Index: avrdude/configure.ac
===================================================================
--- avrdude.orig/configure.ac   2006-10-22 20:07:48.000000000 -0700
+++ avrdude/configure.ac        2006-10-22 20:10:16.000000000 -0700
@@ -60,6 +60,16 @@ if test x$have_libusb = xyes; then
    AC_DEFINE([HAVE_LIBUSB])
 fi
 AC_SUBST(LIBUSB, $LIBUSB)
+if test x$have_libusb = xyes; then
+  AH_TEMPLATE([HAVE_LIBFTDI],
+             [Define if FTDI bitbang support is enabled via libftdi])
+  AC_CHECK_LIB([ftdi], [ftdi_enable_bitbang], [have_libftdi=yes])
+  if test x$have_libftdi = xyes; then
+     LIBFTDI=-lftdi
+     AC_DEFINE([HAVE_LIBFTDI])
+  fi
+  AC_SUBST(LIBFTDI, $LIBFTDI)
+fi
 
 # Checks for header files.
 AC_HEADER_STDC




reply via email to

[Prev in Thread] Current Thread [Next in Thread]