#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <ftd2xx.h>
// Seems like this sort of stuff should have been provided by FTDI
static const char *ft_errors[] =
{
[FT_OK] = "Success",
[FT_INVALID_HANDLE] = "Invalid device handle",
[FT_DEVICE_NOT_FOUND] = "Device not found",
[FT_DEVICE_NOT_OPENED] = "Device not opened",
[FT_IO_ERROR] = "Input/output error",
[FT_INSUFFICIENT_RESOURCES] = "Insufficient resources",
[FT_INVALID_PARAMETER] = "Invalid parameter",
[FT_INVALID_BAUD_RATE] = "Invalid baud rate",
[FT_DEVICE_NOT_OPENED_FOR_ERASE] = "Device not opened for erase",
[FT_DEVICE_NOT_OPENED_FOR_WRITE] = "Device not opened for write",
[FT_FAILED_TO_WRITE_DEVICE] = "Failed to write device",
[FT_EEPROM_READ_FAILED] = "EEPROM read failed",
[FT_EEPROM_WRITE_FAILED] = "EEPROM write failed",
[FT_EEPROM_ERASE_FAILED] = "EEPROM erase failed",
[FT_EEPROM_NOT_PRESENT] = "EEPROM not present",
[FT_EEPROM_NOT_PROGRAMMED] = "EEPROM not programmed",
[FT_INVALID_ARGS] = "Invalid argument",
[FT_NOT_SUPPORTED] = "Not supported",
[FT_OTHER_ERROR] = "Other error"
};
static const int num_ft_errors = sizeof(ft_errors) / sizeof(ft_errors[FT_OK]);
static const char *ft_strerror(FT_STATUS ft_status)
{
if (ft_status < 0 // Should be impossible because it is unsigned
|| ft_status >= num_ft_errors
|| !ft_errors[ft_status])
{
return "Unknown error";
}
return ft_errors[ft_status];
}
static void ft_error_exit(const char *str, FT_STATUS ft_status)
{
if (str && *str)
fprintf(stderr, "%s: %sn", str, ft_strerror(ft_status));
else
fprintf(stderr, "%sn", ft_strerror(ft_status));
exit(1);
}
static void std_error_exit(const char *str)
{
perror(str);
exit(1);
}
static void pthread_error_exit(const char *str, int err)
{
if (str && *str)
fprintf(stderr, "%s: %sn", str, strerror(err));
else
fprintf(stderr, "%sn", strerror(err));
exit(1);
}
static FT_HANDLE ft_handle;
// Registered with atexit()
static void cleanup_ft_handle(void)
{
(void)FT_Close(ft_handle);
}
static struct termios saved_termios;
// Registered with atexit()
static void cleanup_termios(void)
{
if (tcsetattr(STDIN_FILENO, TCSAFLUSH | TCSASOFT, &saved_termios))
std_error_exit("tcsetattr restore");
}
// Reader thread function
static void *reader_func(void *arg)
{
FT_STATUS ft_status;
EVENT_HANDLE eh;
DWORD chars_in_q;
char buf[1024];
int pterr;
if ((pterr = pthread_mutex_init(&eh.eMutex, NULL)))
pthread_error_exit("pthread_mutex_init", pterr);
if ((pterr = pthread_cond_init(&eh.eCondVar, NULL)))
pthread_error_exit("pthread_cond_init", pterr);
if ((ft_status = FT_SetEventNotification(ft_handle, FT_EVENT_RXCHAR,
(PVOID)&eh)) != FT_OK)
ft_error_exit("FT_SetEventNotification", ft_status);
for (;;)
{
if ((pterr = pthread_mutex_lock(&eh.eMutex)))
pthread_error_exit("pthread_mutex_lock", pterr);
if ((ft_status = FT_GetQueueStatus(ft_handle, &chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
if (chars_in_q == 0)
{
if ((pterr = pthread_cond_wait(&eh.eCondVar, &eh.eMutex)))
pthread_error_exit("pthread_cond_wait", pterr);
if ((ft_status = FT_GetQueueStatus(ft_handle,
&chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
}
if ((pterr = pthread_mutex_unlock(&eh.eMutex)))
pthread_error_exit("pthread_mutex_unlock", pterr);
while (chars_in_q)
{
DWORD len_to_read, len_read;
ssize_t len_written;
len_to_read = (chars_in_q > sizeof(buf)) ? sizeof(buf) : chars_in_q;
// Read from the Evaluation Board
if ((ft_status = FT_Read(ft_handle, (LPVOID)buf,
len_to_read, &len_read)) != FT_OK)
ft_error_exit("FT_Read", ft_status);
if (!len_read)
{
fprintf(stderr, "FT_Read returned no data.n");
exit(1);
}
while (len_read)
{
// Write to stdout
len_written = write(STDOUT_FILENO, buf, len_read);
if (len_written == -1)
std_error_exit("fwrite");
len_read -= len_written;
}
chars_in_q -= len_read;
}
}
return NULL;
}
// Writer thread function
static void *writer_func(void *arg)
{
char buf[1024];
ssize_t len_read;
FT_STATUS ft_status;
DWORD len_written;
do
{
len_read = read(STDIN_FILENO, buf, sizeof(buf));
switch (len_read) {
case 0:
// EOF
break;
case -1:
// Error
std_error_exit("fread");
break;
default:
// Got some bytes.
if ((ft_status = FT_Write(ft_handle, (LPVOID)buf,
len_read, &len_written)) != FT_OK)
ft_error_exit("FT_Write", ft_status);
if (len_read != len_written)
{
fprintf(stderr, "FT_Write did not write all the data.n");
exit(1);
}
break;
}
} while (len_read);
return NULL;
}
// Main event
int main(int argc, char **argv)
{
FT_STATUS ft_status;
// Add the VID/PID for the Luminary Micro LM3S811 Evaluation Board
if ((ft_status = FT_SetVIDPID(0x0403, 0xBCD9)) != FT_OK)
ft_error_exit("FT_SetVIDPID", ft_status);
// Open up the second port of the Luminary board. The first port is JTAG.
if ((ft_status = FT_OpenEx("LM3S811 Evaluation Board B",
FT_OPEN_BY_DESCRIPTION, &ft_handle)) != FT_OK)
ft_error_exit("FT_OpenEx", ft_status);
// Ensure the handle gets closed per FTDI's D2XX OS X release notes
if (atexit(cleanup_ft_handle))
std_error_exit("atexit");
// 115200, 8N1, no flow control
if ((ft_status = FT_SetBaudRate(ft_handle, 115200)) != FT_OK)
ft_error_exit("FT_SetBaudRate", ft_status);
if ((ft_status = FT_SetDataCharacteristics(ft_handle,
FT_BITS_8,
FT_STOP_BITS_1,
FT_PARITY_NONE)) != FT_OK)
ft_error_exit("FT_SetDataCharacteristics", ft_status);
if ((ft_status = FT_SetFlowControl(ft_handle,
FT_FLOW_NONE, 0, 0)) != FT_OK)
ft_error_exit("FT_SetFlowControl", ft_status);
// Flush the FTDI's buffers
if ((ft_status = FT_Purge(ft_handle, FT_PURGE_RX | FT_PURGE_TX)) != FT_OK)
ft_error_exit("FT_Purge", ft_status);
// Turn off buffering on stdout
(void)setvbuf(stdout, NULL, _IONBF, 0);
// If stdin and stdout refer to the same tty, set the tty to raw
struct stat stdin_sb, stdout_sb;
if (fstat(STDIN_FILENO, &stdin_sb))
std_error_exit("fstat stdin");
if (fstat(STDOUT_FILENO, &stdout_sb))
std_error_exit("fstat stdout");
if ((stdin_sb.st_mode & S_IFCHR)
&& stdin_sb.st_ino == stdout_sb.st_ino
&& stdin_sb.st_dev == stdout_sb.st_dev)
{
if (!tcgetattr(STDIN_FILENO, &saved_termios))
{
if (atexit(cleanup_termios))
std_error_exit("atexit");
struct termios tios = saved_termios;
cfmakeraw(&tios);
tios.c_lflag |= ISIG;
tios.c_cc[VINTR] = ' 03'; // ^c for now
tios.c_cc[VQUIT] = _POSIX_VDISABLE;
tios.c_cc[VSUSP] = _POSIX_VDISABLE;
tios.c_cc[VMIN] = 1;
tios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &tios))
std_error_exit("tcsetattr stdin");
}
else if (errno != ENODEV)
std_error_exit("tcgetattr stdin");
}
// Mask all allowed signals because after we kick off our
// reader and writer threads, we'll just wait for the signals.
sigset_t sig_set;
(void)sigfillset(&sig_set);
int pterr;
if ((pterr = pthread_sigmask(SIG_BLOCK, &sig_set, NULL)))
pthread_error_exit("pthread_sigmask", pterr);
// Create reader and writer threads.
pthread_attr_t ptattr;
if ((pterr = pthread_attr_init(&ptattr)))
pthread_error_exit("pthread_attr_init", pterr);
if ((pterr = pthread_attr_setdetachstate(&ptattr,
PTHREAD_CREATE_DETACHED)))
pthread_error_exit("pthread_attr_setdetachstate", pterr);
pthread_t reader_id, writer_id;
if ((pterr = pthread_create(&reader_id, &ptattr, reader_func, NULL)))
pthread_error_exit("pthread_create reader", pterr);
if ((pterr = pthread_create(&writer_id, &ptattr, writer_func, NULL)))
pthread_error_exit("pthread_create writer", pterr);
// Now wait for a signal to terminate us
int caught_sig;
if (sigwait(&sig_set, &caught_sig))
std_error_exit("sigwait");
exit(0);
}
|