mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Re-indent and style up serial ftdi
Signed-off-by: Anton Lundin <glance@acc.umu.se> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
f33843ed35
commit
3104508247
1 changed files with 315 additions and 351 deletions
666
serial_ftdi.c
666
serial_ftdi.c
|
@ -46,31 +46,30 @@
|
||||||
#define MAX_BACKOFF 500 // Max milliseconds to wait before timing out.
|
#define MAX_BACKOFF 500 // Max milliseconds to wait before timing out.
|
||||||
|
|
||||||
struct serial_t {
|
struct serial_t {
|
||||||
/* Library ftdi_ctx. */
|
/* Library ftdi_ctx. */
|
||||||
dc_context_t *context;
|
dc_context_t *context;
|
||||||
/*
|
/*
|
||||||
* The file descriptor corresponding to the serial port.
|
* The file descriptor corresponding to the serial port.
|
||||||
* Also a libftdi_ftdi_ctx could be used?
|
* Also a libftdi_ftdi_ctx could be used?
|
||||||
*/
|
*/
|
||||||
struct ftdi_context *ftdi_ctx;
|
struct ftdi_context *ftdi_ctx;
|
||||||
long timeout;
|
long timeout;
|
||||||
/*
|
/*
|
||||||
* Serial port settings are saved into this variable immediately
|
* Serial port settings are saved into this variable immediately
|
||||||
* after the port is opened. These settings are restored when the
|
* after the port is opened. These settings are restored when the
|
||||||
* serial port is closed.
|
* serial port is closed.
|
||||||
* Saving this using libftdi context or libusb. Search further.
|
* Saving this using libftdi context or libusb. Search further.
|
||||||
* Custom implementation using libftdi functions could be done.
|
* Custom implementation using libftdi functions could be done.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Half-duplex settings */
|
/* Half-duplex settings */
|
||||||
int halfduplex;
|
int halfduplex;
|
||||||
unsigned int baudrate;
|
unsigned int baudrate;
|
||||||
unsigned int nbits;
|
unsigned int nbits;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used internally for opening ftdi devices
|
// Used internally for opening ftdi devices
|
||||||
int
|
int open_ftdi_device (struct ftdi_context *ftdi_ctx)
|
||||||
open_ftdi_device (struct ftdi_context *ftdi_ctx)
|
|
||||||
{
|
{
|
||||||
int accepted_pids[] = { 0x6001, 0x6010, 0x6011, // Suunto (Smart Interface), Heinrichs Weikamp
|
int accepted_pids[] = { 0x6001, 0x6010, 0x6011, // Suunto (Smart Interface), Heinrichs Weikamp
|
||||||
0xF460, // Oceanic
|
0xF460, // Oceanic
|
||||||
|
@ -91,11 +90,10 @@ open_ftdi_device (struct ftdi_context *ftdi_ctx)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int serial_enumerate (serial_callback_t callback, void *userdata)
|
||||||
serial_enumerate (serial_callback_t callback, void *userdata)
|
|
||||||
{
|
{
|
||||||
// Unimplemented.
|
// Unimplemented.
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,21 +101,19 @@ serial_enumerate (serial_callback_t callback, void *userdata)
|
||||||
// Open the serial port.
|
// Open the serial port.
|
||||||
// Initialise ftdi_context and use it to open the device
|
// Initialise ftdi_context and use it to open the device
|
||||||
//
|
//
|
||||||
|
int serial_open (serial_t **out, dc_context_t *context, const char* name)
|
||||||
int
|
|
||||||
serial_open (serial_t **out, dc_context_t *context, const char* name)
|
|
||||||
{
|
{
|
||||||
if (out == NULL)
|
if (out == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (context, "Open: name=%s", name ? name : "");
|
INFO (context, "Open: name=%s", name ? name : "");
|
||||||
|
|
||||||
// Allocate memory.
|
// Allocate memory.
|
||||||
serial_t *device = (serial_t *) malloc (sizeof (serial_t));
|
serial_t *device = (serial_t *) malloc (sizeof (serial_t));
|
||||||
if (device == NULL) {
|
if (device == NULL) {
|
||||||
SYSERROR (context, errno);
|
SYSERROR (context, errno);
|
||||||
return -1; // ENOMEM (Not enough space)
|
return -1; // ENOMEM (Not enough space)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ftdi_context *ftdi_ctx = ftdi_new();
|
struct ftdi_context *ftdi_ctx = ftdi_new();
|
||||||
if (ftdi_ctx == NULL) {
|
if (ftdi_ctx == NULL) {
|
||||||
|
@ -125,223 +121,212 @@ serial_open (serial_t **out, dc_context_t *context, const char* name)
|
||||||
return -1; // ENOMEM (Not enough space)
|
return -1; // ENOMEM (Not enough space)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Library context.
|
// Library context.
|
||||||
device->context = context;
|
device->context = context;
|
||||||
|
|
||||||
// Default to blocking reads.
|
// Default to blocking reads.
|
||||||
device->timeout = -1;
|
device->timeout = -1;
|
||||||
|
|
||||||
// Default to full-duplex.
|
// Default to full-duplex.
|
||||||
device->halfduplex = 0;
|
device->halfduplex = 0;
|
||||||
device->baudrate = 0;
|
device->baudrate = 0;
|
||||||
device->nbits = 0;
|
device->nbits = 0;
|
||||||
|
|
||||||
// Initialize device ftdi context
|
// Initialize device ftdi context
|
||||||
ftdi_init(ftdi_ctx);
|
ftdi_init(ftdi_ctx);
|
||||||
|
|
||||||
if (ftdi_set_interface(ftdi_ctx,INTERFACE_ANY)) {
|
if (ftdi_set_interface(ftdi_ctx,INTERFACE_ANY)) {
|
||||||
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_ftdi_device(ftdi_ctx) < 0) {
|
if (open_ftdi_device(ftdi_ctx) < 0) {
|
||||||
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftdi_usb_reset(ftdi_ctx)) {
|
|
||||||
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ftdi_usb_purge_buffers(ftdi_ctx)) {
|
if (ftdi_usb_reset(ftdi_ctx)) {
|
||||||
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftdi_usb_purge_buffers(ftdi_ctx)) {
|
||||||
|
ERROR (context, ftdi_get_error_string(ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->ftdi_ctx = ftdi_ctx;
|
device->ftdi_ctx = ftdi_ctx;
|
||||||
*out = device;
|
*out = device;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Close the serial port.
|
// Close the serial port.
|
||||||
//
|
//
|
||||||
|
int serial_close (serial_t *device)
|
||||||
int
|
|
||||||
serial_close (serial_t *device)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Restore the initial terminal attributes.
|
// Restore the initial terminal attributes.
|
||||||
// See if it is possible using libusb or libftdi
|
// See if it is possible using libusb or libftdi
|
||||||
|
|
||||||
int ret = ftdi_usb_close(device->ftdi_ctx);
|
int ret = ftdi_usb_close(device->ftdi_ctx);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR (device->context, "Unable to close the ftdi device : %d (%s)\n",
|
ERROR (device->context, "Unable to close the ftdi device : %d (%s)\n",
|
||||||
ret, ftdi_get_error_string(device->ftdi_ctx));
|
ret, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ftdi_free(device->ftdi_ctx);
|
ftdi_free(device->ftdi_ctx);
|
||||||
|
|
||||||
// Free memory.
|
// Free memory.
|
||||||
free (device);
|
free (device);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Configure the serial port (baudrate, databits, parity, stopbits and flowcontrol).
|
// Configure the serial port (baudrate, databits, parity, stopbits and flowcontrol).
|
||||||
//
|
//
|
||||||
|
int serial_configure (serial_t *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
|
||||||
int
|
|
||||||
serial_configure (serial_t *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i",
|
INFO (device->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i",
|
||||||
baudrate, databits, parity, stopbits, flowcontrol);
|
baudrate, databits, parity, stopbits, flowcontrol);
|
||||||
|
|
||||||
enum ftdi_bits_type ft_bits;
|
enum ftdi_bits_type ft_bits;
|
||||||
enum ftdi_stopbits_type ft_stopbits;
|
enum ftdi_stopbits_type ft_stopbits;
|
||||||
enum ftdi_parity_type ft_parity;
|
enum ftdi_parity_type ft_parity;
|
||||||
|
|
||||||
if (ftdi_set_baudrate(device->ftdi_ctx, baudrate) < 0) {
|
if (ftdi_set_baudrate(device->ftdi_ctx, baudrate) < 0) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the character size.
|
// Set the character size.
|
||||||
switch (databits) {
|
switch (databits) {
|
||||||
case 7:
|
case 7:
|
||||||
ft_bits = BITS_7;
|
ft_bits = BITS_7;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
ft_bits = BITS_8;
|
ft_bits = BITS_8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the parity type.
|
// Set the parity type.
|
||||||
switch (parity) {
|
switch (parity) {
|
||||||
case SERIAL_PARITY_NONE: // No parity
|
case SERIAL_PARITY_NONE: // No parity
|
||||||
ft_parity = NONE;
|
ft_parity = NONE;
|
||||||
break;
|
break;
|
||||||
case SERIAL_PARITY_EVEN: // Even parity
|
case SERIAL_PARITY_EVEN: // Even parity
|
||||||
ft_parity = EVEN;
|
ft_parity = EVEN;
|
||||||
break;
|
break;
|
||||||
case SERIAL_PARITY_ODD: // Odd parity
|
case SERIAL_PARITY_ODD: // Odd parity
|
||||||
ft_parity = ODD;
|
ft_parity = ODD;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the number of stop bits.
|
// Set the number of stop bits.
|
||||||
switch (stopbits) {
|
switch (stopbits) {
|
||||||
case 1: // One stopbit
|
case 1: // One stopbit
|
||||||
ft_stopbits = STOP_BIT_1;
|
ft_stopbits = STOP_BIT_1;
|
||||||
break;
|
break;
|
||||||
case 2: // Two stopbits
|
case 2: // Two stopbits
|
||||||
ft_stopbits = STOP_BIT_2;
|
ft_stopbits = STOP_BIT_2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the attributes
|
// Set the attributes
|
||||||
if (ftdi_set_line_property(device->ftdi_ctx, ft_bits, ft_stopbits, ft_parity)) {
|
if (ftdi_set_line_property(device->ftdi_ctx, ft_bits, ft_stopbits, ft_parity)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the flow control.
|
// Set the flow control.
|
||||||
switch (flowcontrol) {
|
switch (flowcontrol) {
|
||||||
case SERIAL_FLOWCONTROL_NONE: // No flow control.
|
case SERIAL_FLOWCONTROL_NONE: // No flow control.
|
||||||
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_DISABLE_FLOW_CTRL) < 0) {
|
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_DISABLE_FLOW_CTRL) < 0) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SERIAL_FLOWCONTROL_HARDWARE: // Hardware (RTS/CTS) flow control.
|
|
||||||
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_RTS_CTS_HS) < 0) {
|
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SERIAL_FLOWCONTROL_SOFTWARE: // Software (XON/XOFF) flow control.
|
case SERIAL_FLOWCONTROL_HARDWARE: // Hardware (RTS/CTS) flow control.
|
||||||
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_XON_XOFF_HS) < 0) {
|
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_RTS_CTS_HS) < 0) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
case SERIAL_FLOWCONTROL_SOFTWARE: // Software (XON/XOFF) flow control.
|
||||||
return -1;
|
if (ftdi_setflowctrl(device->ftdi_ctx, SIO_XON_XOFF_HS) < 0) {
|
||||||
}
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
device->baudrate = baudrate;
|
device->baudrate = baudrate;
|
||||||
device->nbits = 1 + databits + stopbits + (parity ? 1 : 0);
|
device->nbits = 1 + databits + stopbits + (parity ? 1 : 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Configure the serial port (timeouts).
|
// Configure the serial port (timeouts).
|
||||||
//
|
//
|
||||||
|
int serial_set_timeout (serial_t *device, long timeout)
|
||||||
int
|
|
||||||
serial_set_timeout (serial_t *device, long timeout)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "Timeout: value=%li", timeout);
|
INFO (device->context, "Timeout: value=%li", timeout);
|
||||||
|
|
||||||
device->timeout = timeout;
|
device->timeout = timeout;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Configure the serial port (recommended size of the input/output buffers).
|
// Configure the serial port (recommended size of the input/output buffers).
|
||||||
//
|
//
|
||||||
|
int serial_set_queue_size (serial_t *device, unsigned int input, unsigned int output)
|
||||||
int
|
|
||||||
serial_set_queue_size (serial_t *device, unsigned int input, unsigned int output)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
|
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
|
||||||
|
|
||||||
ftdi_read_data_set_chunksize(device->ftdi_ctx, output);
|
ftdi_read_data_set_chunksize(device->ftdi_ctx, output);
|
||||||
ftdi_write_data_set_chunksize(device->ftdi_ctx, input);
|
ftdi_write_data_set_chunksize(device->ftdi_ctx, input);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_set_halfduplex (serial_t *device, int value)
|
||||||
int
|
|
||||||
serial_set_halfduplex (serial_t *device, int value)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
// Most ftdi chips support full duplex operation. ft232rl does.
|
// Most ftdi chips support full duplex operation. ft232rl does.
|
||||||
// Crosscheck other chips.
|
// Crosscheck other chips.
|
||||||
|
|
||||||
device->halfduplex = value;
|
device->halfduplex = value;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int serial_set_latency (serial_t *device, unsigned int milliseconds)
|
||||||
serial_set_latency (serial_t *device, unsigned int milliseconds)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
@ -351,60 +336,59 @@ serial_set_latency (serial_t *device, unsigned int milliseconds)
|
||||||
|
|
||||||
if (milliseconds < 1 || milliseconds > 255)
|
if (milliseconds < 1 || milliseconds > 255)
|
||||||
return -1;
|
return -1;
|
||||||
ftdi_set_latency_timer(device->ftdi_ctx, milliseconds);
|
ftdi_set_latency_timer(device->ftdi_ctx, milliseconds);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int serial_read (serial_t *device, void *data, unsigned int size)
|
||||||
serial_read (serial_t *device, void *data, unsigned int size)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
// The total timeout.
|
// The total timeout.
|
||||||
long timeout = device->timeout;
|
long timeout = device->timeout;
|
||||||
|
|
||||||
// The absolute target time.
|
// The absolute target time.
|
||||||
struct timeval tve;
|
struct timeval tve;
|
||||||
|
|
||||||
static int backoff = 1;
|
static int backoff = 1;
|
||||||
int init = 1;
|
int init = 1;
|
||||||
unsigned int nbytes = 0;
|
unsigned int nbytes = 0;
|
||||||
while (nbytes < size) {
|
while (nbytes < size) {
|
||||||
struct timeval tvt;
|
struct timeval tvt;
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
if (gettimeofday (&now, NULL) != 0) {
|
if (gettimeofday (&now, NULL) != 0) {
|
||||||
SYSERROR (device->context, errno);
|
SYSERROR (device->context, errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init) {
|
if (init) {
|
||||||
// Calculate the initial timeout.
|
// Calculate the initial timeout.
|
||||||
tvt.tv_sec = (timeout / 1000);
|
tvt.tv_sec = (timeout / 1000);
|
||||||
tvt.tv_usec = (timeout % 1000) * 1000;
|
tvt.tv_usec = (timeout % 1000) * 1000;
|
||||||
// Calculate the target time.
|
// Calculate the target time.
|
||||||
timeradd (&now, &tvt, &tve);
|
timeradd (&now, &tvt, &tve);
|
||||||
} else {
|
} else {
|
||||||
// Calculate the remaining timeout.
|
// Calculate the remaining timeout.
|
||||||
if (timercmp (&now, &tve, <))
|
if (timercmp (&now, &tve, <))
|
||||||
timersub (&tve, &now, &tvt);
|
timersub (&tve, &now, &tvt);
|
||||||
else
|
else
|
||||||
timerclear (&tvt);
|
timerclear (&tvt);
|
||||||
}
|
}
|
||||||
init = 0;
|
init = 0;
|
||||||
} else if (timeout == 0) {
|
} else if (timeout == 0) {
|
||||||
timerclear (&tvt);
|
timerclear (&tvt);
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = ftdi_read_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
|
int n = ftdi_read_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (n == LIBUSB_ERROR_INTERRUPTED)
|
if (n == LIBUSB_ERROR_INTERRUPTED)
|
||||||
continue; //Retry.
|
continue; //Retry.
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1; //Error during read call.
|
return -1; //Error during read call.
|
||||||
} else if (n == 0) {
|
} else if (n == 0) {
|
||||||
// Exponential backoff.
|
// Exponential backoff.
|
||||||
if (backoff > MAX_BACKOFF) {
|
if (backoff > MAX_BACKOFF) {
|
||||||
ERROR(device->context, "FTDI read timed out.");
|
ERROR(device->context, "FTDI read timed out.");
|
||||||
|
@ -417,119 +401,113 @@ serial_read (serial_t *device, void *data, unsigned int size)
|
||||||
backoff = 1;
|
backoff = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nbytes += n;
|
nbytes += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes);
|
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes);
|
||||||
|
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_write (serial_t *device, const void *data, unsigned int size)
|
||||||
int
|
|
||||||
serial_write (serial_t *device, const void *data, unsigned int size)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
struct timeval tve, tvb;
|
struct timeval tve, tvb;
|
||||||
if (device->halfduplex) {
|
if (device->halfduplex) {
|
||||||
// Get the current time.
|
// Get the current time.
|
||||||
if (gettimeofday (&tvb, NULL) != 0) {
|
if (gettimeofday (&tvb, NULL) != 0) {
|
||||||
SYSERROR (device->context, errno);
|
SYSERROR (device->context, errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int nbytes = 0;
|
unsigned int nbytes = 0;
|
||||||
while (nbytes < size) {
|
while (nbytes < size) {
|
||||||
|
|
||||||
int n = ftdi_write_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
|
int n = ftdi_write_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (n == LIBUSB_ERROR_INTERRUPTED)
|
if (n == LIBUSB_ERROR_INTERRUPTED)
|
||||||
continue; // Retry.
|
continue; // Retry.
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1; // Error during write call.
|
return -1; // Error during write call.
|
||||||
} else if (n == 0) {
|
} else if (n == 0) {
|
||||||
break; // EOF.
|
break; // EOF.
|
||||||
}
|
}
|
||||||
|
|
||||||
nbytes += n;
|
nbytes += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->halfduplex) {
|
if (device->halfduplex) {
|
||||||
// Get the current time.
|
// Get the current time.
|
||||||
if (gettimeofday (&tve, NULL) != 0) {
|
if (gettimeofday (&tve, NULL) != 0) {
|
||||||
SYSERROR (device->context, errno);
|
SYSERROR (device->context, errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the elapsed time (microseconds).
|
// Calculate the elapsed time (microseconds).
|
||||||
struct timeval tvt;
|
struct timeval tvt;
|
||||||
timersub (&tve, &tvb, &tvt);
|
timersub (&tve, &tvb, &tvt);
|
||||||
unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec;
|
unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec;
|
||||||
|
|
||||||
// Calculate the expected duration (microseconds). A 2 millisecond fudge
|
// Calculate the expected duration (microseconds). A 2 millisecond fudge
|
||||||
// factor is added because it improves the success rate significantly.
|
// factor is added because it improves the success rate significantly.
|
||||||
unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000;
|
unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000;
|
||||||
|
|
||||||
// Wait for the remaining time.
|
// Wait for the remaining time.
|
||||||
if (elapsed < expected) {
|
if (elapsed < expected) {
|
||||||
unsigned long remaining = expected - elapsed;
|
unsigned long remaining = expected - elapsed;
|
||||||
|
|
||||||
// The remaining time is rounded up to the nearest millisecond to
|
// The remaining time is rounded up to the nearest millisecond to
|
||||||
// match the Windows implementation. The higher resolution is
|
// match the Windows implementation. The higher resolution is
|
||||||
// pointless anyway, since we already added a fudge factor above.
|
// pointless anyway, since we already added a fudge factor above.
|
||||||
serial_sleep (device, (remaining + 999) / 1000);
|
serial_sleep (device, (remaining + 999) / 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes);
|
HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes);
|
||||||
|
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_flush (serial_t *device, int queue)
|
||||||
int
|
|
||||||
serial_flush (serial_t *device, int queue)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "Flush: queue=%u, input=%i, output=%i", queue,
|
INFO (device->context, "Flush: queue=%u, input=%i, output=%i", queue,
|
||||||
serial_get_received (device),
|
serial_get_received (device),
|
||||||
serial_get_transmitted (device));
|
serial_get_transmitted (device));
|
||||||
|
|
||||||
switch (queue) {
|
switch (queue) {
|
||||||
case SERIAL_QUEUE_INPUT:
|
case SERIAL_QUEUE_INPUT:
|
||||||
if (ftdi_usb_purge_tx_buffer(device->ftdi_ctx)) {
|
if (ftdi_usb_purge_tx_buffer(device->ftdi_ctx)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SERIAL_QUEUE_OUTPUT:
|
case SERIAL_QUEUE_OUTPUT:
|
||||||
if (ftdi_usb_purge_rx_buffer(device->ftdi_ctx)) {
|
if (ftdi_usb_purge_rx_buffer(device->ftdi_ctx)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (ftdi_usb_purge_buffers(device->ftdi_ctx)) {
|
if (ftdi_usb_purge_buffers(device->ftdi_ctx)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_send_break (serial_t *device)
|
||||||
int
|
|
||||||
serial_send_break (serial_t *device)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)a
|
return -1; // EINVAL (Invalid argument)a
|
||||||
|
|
||||||
INFO (device->context, "Break : One time period.");
|
INFO (device->context, "Break : One time period.");
|
||||||
|
|
||||||
|
@ -538,133 +516,119 @@ serial_send_break (serial_t *device)
|
||||||
// and resetting the baudrate up again. But it has flaws.
|
// and resetting the baudrate up again. But it has flaws.
|
||||||
// Not implementing it before researching more.
|
// Not implementing it before researching more.
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_set_break (serial_t *device, int level)
|
||||||
int
|
|
||||||
serial_set_break (serial_t *device, int level)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "Break: value=%i", level);
|
INFO (device->context, "Break: value=%i", level);
|
||||||
|
|
||||||
// Not implemented in libftdi yet. Research it further.
|
// Not implemented in libftdi yet. Research it further.
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_set_dtr (serial_t *device, int level)
|
||||||
int
|
|
||||||
serial_set_dtr (serial_t *device, int level)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "DTR: value=%i", level);
|
INFO (device->context, "DTR: value=%i", level);
|
||||||
|
|
||||||
if (ftdi_setdtr(device->ftdi_ctx, level)) {
|
if (ftdi_setdtr(device->ftdi_ctx, level)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_set_rts (serial_t *device, int level)
|
||||||
int
|
|
||||||
serial_set_rts (serial_t *device, int level)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
INFO (device->context, "RTS: value=%i", level);
|
INFO (device->context, "RTS: value=%i", level);
|
||||||
|
|
||||||
if (ftdi_setrts(device->ftdi_ctx, level)) {
|
if (ftdi_setrts(device->ftdi_ctx, level)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_get_received (serial_t *device)
|
||||||
int
|
|
||||||
serial_get_received (serial_t *device)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
// Direct access is not encouraged. But function implementation
|
// Direct access is not encouraged. But function implementation
|
||||||
// is not available. The return quantity might be anything.
|
// is not available. The return quantity might be anything.
|
||||||
// Find out further about its possible values and correct way of
|
// Find out further about its possible values and correct way of
|
||||||
// access.
|
// access.
|
||||||
int bytes = device->ftdi_ctx->readbuffer_remaining;
|
int bytes = device->ftdi_ctx->readbuffer_remaining;
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_get_transmitted (serial_t *device)
|
||||||
int
|
|
||||||
serial_get_transmitted (serial_t *device)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
// This is not possible using libftdi. Look further into it.
|
// This is not possible using libftdi. Look further into it.
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_get_line (serial_t *device, int line)
|
||||||
int
|
|
||||||
serial_get_line (serial_t *device, int line)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1; // EINVAL (Invalid argument)
|
return -1; // EINVAL (Invalid argument)
|
||||||
|
|
||||||
unsigned short int status[2] = {0};
|
unsigned short int status[2] = {0};
|
||||||
|
|
||||||
if(ftdi_poll_modem_status(device->ftdi_ctx, status)) {
|
if(ftdi_poll_modem_status(device->ftdi_ctx, status)) {
|
||||||
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
ERROR (device->context, ftdi_get_error_string(device->ftdi_ctx));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (line) {
|
switch (line) {
|
||||||
case SERIAL_LINE_DCD:
|
case SERIAL_LINE_DCD:
|
||||||
return (status[0] & MODEM_DCD) == MODEM_DCD;
|
return (status[0] & MODEM_DCD) == MODEM_DCD;
|
||||||
case SERIAL_LINE_CTS:
|
case SERIAL_LINE_CTS:
|
||||||
return (status[0] & MODEM_CTS) == MODEM_CTS;
|
return (status[0] & MODEM_CTS) == MODEM_CTS;
|
||||||
case SERIAL_LINE_DSR:
|
case SERIAL_LINE_DSR:
|
||||||
return (status[0] & MODEM_DSR) == MODEM_DSR;
|
return (status[0] & MODEM_DSR) == MODEM_DSR;
|
||||||
case SERIAL_LINE_RNG:
|
case SERIAL_LINE_RNG:
|
||||||
return (status[0] & MODEM_RNG) == MODEM_RNG;
|
return (status[0] & MODEM_RNG) == MODEM_RNG;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serial_sleep (serial_t *device, unsigned long timeout)
|
||||||
int
|
|
||||||
serial_sleep (serial_t *device, unsigned long timeout)
|
|
||||||
{
|
{
|
||||||
if (device == NULL)
|
if (device == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
INFO (device->context, "Sleep: value=%lu", timeout);
|
INFO (device->context, "Sleep: value=%lu", timeout);
|
||||||
|
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
ts.tv_sec = (timeout / 1000);
|
ts.tv_sec = (timeout / 1000);
|
||||||
ts.tv_nsec = (timeout % 1000) * 1000000;
|
ts.tv_nsec = (timeout % 1000) * 1000000;
|
||||||
|
|
||||||
while (nanosleep (&ts, &ts) != 0) {
|
while (nanosleep (&ts, &ts) != 0) {
|
||||||
if (errno != EINTR ) {
|
if (errno != EINTR ) {
|
||||||
SYSERROR (device->context, errno);
|
SYSERROR (device->context, errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue