static dc_status_t serial_ftdi_read (void **userdata, void *data, size_t size, size_t *actual) { ftdi_serial_t *device = (ftdi_serial_t*) *userdata; if (device == NULL) return DC_STATUS_INVALIDARGS; // The total timeout. long timeout = device->timeout; // Simulate blocking read as 10s timeout if (timeout == -1) timeout = 10000; int backoff = 1; int slept = 0; unsigned int nbytes = 0; while (nbytes < size) { int n = ftdi_read_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes); if (n < 0) { if (n == LIBUSB_ERROR_INTERRUPTED) continue; //Retry. ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx)); return DC_STATUS_IO; //Error during read call. } else if (n == 0) { // Exponential backoff. if (slept >= timeout) { ERROR(device->context, "%s", "FTDI read timed out."); return DC_STATUS_TIMEOUT; } serial_ftdi_sleep (device, backoff); slept += backoff; backoff *= 2; if (backoff + slept > timeout) backoff = timeout - slept; } else { // Reset backoff to 1 on success. backoff = 1; } nbytes += n; } INFO (device->context, "Read %d bytes", nbytes); if (actual) *actual = nbytes; return DC_STATUS_SUCCESS; }
static dc_status_t serial_ftdi_read (void *io, void *data, size_t size, size_t *actual) { ftdi_serial_t *device = io; if (device == NULL) return DC_STATUS_INVALIDARGS; // The total timeout. long timeout = device->timeout; // Simulate blocking read as 10s timeout if (timeout == -1) timeout = 10000; unsigned int start_time = serial_ftdi_get_msec(); unsigned int nbytes = 0; while (nbytes < size) { int n = ftdi_read_data (device->ftdi_ctx, (unsigned char *) data + nbytes, size - nbytes); if (n < 0) { if (n == LIBUSB_ERROR_INTERRUPTED) continue; //Retry. ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx)); return DC_STATUS_IO; //Error during read call. } else if (n == 0) { if (serial_ftdi_get_msec() - start_time > timeout) { ERROR(device->context, "%s", "FTDI read timed out."); return DC_STATUS_TIMEOUT; } serial_ftdi_sleep (device, 1); } nbytes += n; } INFO (device->context, "Read %d bytes", nbytes); if (actual) *actual = nbytes; return DC_STATUS_SUCCESS; }
static dc_status_t serial_ftdi_write (void **userdata, const void *data, size_t size, size_t *actual) { ftdi_serial_t *device = (ftdi_serial_t*) *userdata; if (device == NULL) return DC_STATUS_INVALIDARGS; struct timeval tve, tvb; if (device->halfduplex) { // Get the current time. if (gettimeofday (&tvb, NULL) != 0) { SYSERROR (device->context, errno); return DC_STATUS_IO; } } unsigned int nbytes = 0; while (nbytes < size) { int n = ftdi_write_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes); if (n < 0) { if (n == LIBUSB_ERROR_INTERRUPTED) continue; // Retry. ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx)); return DC_STATUS_IO; // Error during write call. } else if (n == 0) { break; // EOF. } nbytes += n; } if (device->halfduplex) { // Get the current time. if (gettimeofday (&tve, NULL) != 0) { SYSERROR (device->context, errno); return DC_STATUS_IO; } // Calculate the elapsed time (microseconds). struct timeval tvt; timersub (&tve, &tvb, &tvt); unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec; // Calculate the expected duration (microseconds). A 2 millisecond fudge // factor is added because it improves the success rate significantly. unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000; // Wait for the remaining time. if (elapsed < expected) { unsigned long remaining = expected - elapsed; // The remaining time is rounded up to the nearest millisecond to // match the Windows implementation. The higher resolution is // pointless anyway, since we already added a fudge factor above. serial_ftdi_sleep (device, (remaining + 999) / 1000); } } INFO (device->context, "Wrote %d bytes", nbytes); if (actual) *actual = nbytes; return DC_STATUS_SUCCESS; }