Apparently, doing too large read/write I/O on the MPSSE fills up the RX Buffer
of the chip, resulting in STALL/NAK of additional transactions of an OUT request.
Since the OUT request is issued synchronously, an appropriate IN request cannot
be issued, resulting in a deadlock.
This problem is handled by submitting small enough OUT requests, that will not
fill up the RX buffer and interleaving them with IN requests.


git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1142 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
hweisbach 2013-04-27 21:49:27 +00:00
parent 2a3ecd0672
commit a6f4aac8b1
2 changed files with 65 additions and 29 deletions

View File

@ -7,6 +7,10 @@
error messages. avrftdi_print is changed so that a message is printed when error messages. avrftdi_print is changed so that a message is printed when
the verbosity level is greater or equal the message level, to have always-on the verbosity level is greater or equal the message level, to have always-on
messages. messages.
Fix a bug where the RX fifo of the FTDI chip is full, resulting in STALL/NAK
of the ongoing OUT request and subsequently timeout, because an IN request
cannot be issued due to the synchronous part of libftdi. This should fix
#38831 and #38659.
2013-04-25 Joerg Wunsch <j.gnu@uriah.heep.sax.de> 2013-04-25 Joerg Wunsch <j.gnu@uriah.heep.sax.de>

View File

@ -95,6 +95,8 @@ typedef struct avrftdi_s {
uint16_t led_mask; uint16_t led_mask;
/* total number of pins supported by a programmer. varies with FTDI chips */ /* total number of pins supported by a programmer. varies with FTDI chips */
int pin_limit; int pin_limit;
/* internal RX buffer of the device. needed for INOUT transfers */
int rx_buffer_size;
} avrftdi_t; } avrftdi_t;
static int write_flush(avrftdi_t *); static int write_flush(avrftdi_t *);
@ -513,44 +515,65 @@ static int set_led_vfy(struct programmer_t * pgm, int value)
/* Send 'buf_size' bytes from 'cmd' to device and return data from device in /* Send 'buf_size' bytes from 'cmd' to device and return data from device in
* buffer 'data'. * buffer 'data'.
* Write is only performed when mode contains MPSSE_DO_WRITE. * Write is only performed when mode contains MPSSE_DO_WRITE.
* Read is only performed when mode contains MPSSE_DO_READ. * Read is only performed when mode contains MPSSE_DO_WRITE and MPSSE_DO_READ.
*/ */
static int avrftdi_transmit(avrftdi_t* pdata, unsigned char mode, unsigned char *cmd, static int avrftdi_transmit(avrftdi_t* pdata, unsigned char mode, unsigned char *buf,
unsigned char *data, int buf_size) unsigned char *data, int buf_size)
{ {
int k = 0; size_t blocksize;
int n; size_t bsize;
unsigned char buf[4 + buf_size]; size_t remaining = buf_size;
size_t written = 0;
if (mode & MPSSE_DO_WRITE) { unsigned char cmd[3];
buf[0] = mode | MPSSE_WRITE_NEG; // unsigned char si = SEND_IMMEDIATE;
buf[1] = ((buf_size - 1) & 0xff);
buf[2] = (((buf_size - 1) >> 8) & 0xff);
memcpy(buf + 3, cmd, buf_size); cmd[0] = mode | MPSSE_WRITE_NEG;
buf[buf_size + 3] = 0x87; cmd[1] = ((buf_size - 1) & 0xff);
cmd[2] = (((buf_size - 1) >> 8) & 0xff);
//if we are not reading back, we can just write the data out
if(!(mode & MPSSE_DO_READ))
blocksize = buf_size;
else
blocksize = pdata->rx_buffer_size;
#ifndef DRYRUN #ifndef DRYRUN
E(ftdi_write_data(pdata->ftdic, buf, buf_size + 4) != buf_size + 4, pdata->ftdic); E(ftdi_write_data(pdata->ftdic, cmd, sizeof(cmd)) != sizeof(cmd), pdata->ftdic);
#endif #endif
}
if (mode & MPSSE_DO_READ) { while(remaining)
memset(buf, 0, sizeof(buf)); {
do { size_t transfer_size = (remaining > blocksize) ? blocksize : remaining;
#ifndef DRYRUN #ifndef DRYRUN
n = ftdi_read_data(pdata->ftdic, buf + k, buf_size - k); E(ftdi_write_data(pdata->ftdic, &buf[written], transfer_size) != transfer_size, pdata->ftdic);
E(n < 0, pdata->ftdic); #endif
#else #if 0
n = buf_size - k; if(remaining < blocksize)
E(ftdi_write_data(pdata->ftdic, &si, sizeof(si)) != sizeof(si), pdata->ftdic);
#endif #endif
k += n;
} while (k < buf_size);
memcpy(data, buf, buf_size); if (mode & MPSSE_DO_READ) {
int n;
int k = 0;
do {
#ifndef DRYRUN
n = ftdi_read_data(pdata->ftdic, &data[written + k], transfer_size - k);
E(n < 0, pdata->ftdic);
#else
n = transfer_size - k;
#endif
k += n;
} while (k < transfer_size);
}
written += transfer_size;
remaining -= transfer_size;
} }
return k; return written;
} }
static int write_flush(avrftdi_t* pdata) static int write_flush(avrftdi_t* pdata)
@ -721,24 +744,33 @@ static int avrftdi_open(PROGRAMMER * pgm, char *port)
/* set pin limit depending on chip type */ /* set pin limit depending on chip type */
switch(pdata->ftdic->type) { switch(pdata->ftdic->type) {
#if 0
//TODO: issue an error - no MPSSE. hint the user to syncbb?
case TYPE_AM: case TYPE_AM:
case TYPE_BM: case TYPE_BM:
case TYPE_R: case TYPE_R:
#endif avrftdi_print(0, "Found unsupported device type AM, BM or R. " \
"avrftdi cannot work with your chip. Try the 'synbb' programmer.\n");
case TYPE_2232C: case TYPE_2232C:
pdata->pin_limit = 11; pdata->pin_limit = 11;
pdata->rx_buffer_size = 384;
break; break;
case TYPE_2232H: case TYPE_2232H:
pdata->pin_limit = 15;
pdata->rx_buffer_size = 4096;
break;
case TYPE_232H: case TYPE_232H:
pdata->pin_limit = 15; pdata->pin_limit = 15;
pdata->rx_buffer_size = 1024;
break; break;
case TYPE_4232H: case TYPE_4232H:
pdata->pin_limit = 7; pdata->pin_limit = 7;
pdata->rx_buffer_size = 2048;
break; break;
default: default:
//TODO: error/unsupported device avrftdi_print(0, "Found unkown device %x. " \
"I will do my best to work with it, but no guarantees ...\n",
pdata->ftdic->type);
pdata->pin_limit = 7;
pdata->rx_buffer_size = pdata->ftdic->max_packet_size;
break; break;
} }