I'm working on porting an existing, functional C# application we have that uses the FTDI D2XX C# .NET wrapper on Windows, over to Linux and Mono. I have followed directions given on installing the Linux D2XX driver. When it's detected that we're running on Linux we look for and load the Linux driver in the C# .NET wrapper, instead of the Windows driver.
After doing that work, much of our application code is still fine, but it appears that when the event set with the SetEventNotification function is triggered on an FT_EVENT_RXCHAR event, the application crashes.
The flow of the C# application is to:
- Set up communication with the FTDI device
- SetEventNotification to be notified when new data is available from the FTDI device
- Ping the FTDI device, letting it know we're ready and we'd like some data
- When notified of new data (from using the SetEventNotification), do some things with it
A bit more detail:
- Read'ing and Write'ing with the C# wrapper seem to work fine on Linux / Mono
- The device is first pinged using a Write, then the following Reads are successful
- Using example code provided in the D2XX Programmer's Guide, C code written on Linux doesn't crash when using SetEventNotification
- The (apparent) alternative, using the C# wrapper's Read on a desired number of bytes appears to be working appropriately on Linux / Mono
A couple of questions:
- Has anyone worked with the C# .NET Wrapper for the D2XX drivers on Linux / Mono and had this issue?
- C# Wrapper's SetEventNotification expects an EventWaitHandle, which then retrieves the native operating system handle by using SafeWaitHandle. Is it possible that the issue is here?
Any insight offered is much appreciated.
Edit:
The stack trace is now attached below.
I tried to move to a polling approach, but it appears that it's possible to have a "frame" of data not fully written when we perform an FTDI Read and we'll have a gap in the data.
I have reached out to FTDI for some guidance on the issue. Will update if I hear back something actionable.
# mono --debug ./CT4USB.exe
Native stacktrace:
mono() [0x49d5fc]
mono() [0x424a0e]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x10330) [0x7f3253539330]
/lib/x86_64-linux-gnu/libpthread.so.0(pthread_mutex_lock+0x4) [0x7f3253533404]
/usr/local/lib/libftd2xx.so(+0x12f52) [0x7f324b9d1f52]
/usr/local/lib/libftd2xx.so(+0x134f1) [0x7f324b9d24f1]
/usr/local/lib/libftd2xx.so(processor_thread+0x21b) [0x7f324b9d29bd]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x8184) [0x7f3253531184]
/lib/x86_64-linux-gnu/libc.so.6(clone+0x6d) [0x7f325325e37d]
Debug info from gdb:
[New LWP 5654]
[New LWP 5653]
[New LWP 5652]
[New LWP 5645]
[New LWP 5644]
[New LWP 5643]
[New LWP 5642]
[New LWP 5641]
[New LWP 5640]
[New LWP 5639]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f3253538b9d in nanosleep () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
Id Target Id Frame
11 Thread 0x7f32523ff700 (LWP 5639) "mono" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
10 Thread 0x7f3252a81700 (LWP 5640) "Finalizer" sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:85
9 Thread 0x7f3253efa700 (LWP 5641) "mono" __clock_nanosleep (clock_id=1, flags=1, req=0x7f3253ef9d80, rem=0x7f325326c974 <__clock_nanosleep+132>) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:49
8 Thread 0x7f324bfff700 (LWP 5642) "Threadpool work" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
7 Thread 0x7f324bdfe700 (LWP 5643) "Threadpool work" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
6 Thread 0x7f324b9be700 (LWP 5644) "Threadpool work" 0x00007f3253250fdd in poll () at ../sysdeps/unix/syscall-template.S:81
5 Thread 0x7f324b1bd700 (LWP 5645) "Threadpool work" 0x00007f3253250fdd in poll () at ../sysdeps/unix/syscall-template.S:81
4 Thread 0x7f324a7ff700 (LWP 5652) "Threadpool work" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
3 Thread 0x7f3249ffe700 (LWP 5653) "Threadpool work" 0x00007f3253538ed9 in __libc_waitpid (pid=5655, stat_loc=0x7f3249ffce4c, options=0) at ../sysdeps/unix/sysv/linux/waitpid.c:40
2 Thread 0x7f32497fd700 (LWP 5654) "Threadpool work" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
* 1 Thread 0x7f325405d7c0 (LWP 5638) "mono" 0x00007f3253538b9d in nanosleep () at ../sysdeps/unix/syscall-template.S:81
Thread 11 (Thread 0x7f32523ff700 (LWP 5639)):
#0 pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1 0x00000000005f9aec in ?? ()
#2 0x00007f3253531184 in start_thread (arg=0x7f32523ff700) at pthread_create.c:312
#3 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 10 (Thread 0x7f3252a81700 (LWP 5640)):
#0 sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:85
#1 0x000000000061de28 in mono_sem_wait ()
#2 0x00000000005a2076 in ?? ()
#3 0x00000000005843d3 in ?? ()
#4 0x0000000000624666 in ?? ()
#5 0x00007f3253531184 in start_thread (arg=0x7f3252a81700) at pthread_create.c:312
#6 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 9 (Thread 0x7f3253efa700 (LWP 5641)):
#0 __clock_nanosleep (clock_id=1, flags=1, req=0x7f3253ef9d80, rem=0x7f325326c974 <__clock_nanosleep+132>) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:49
#1 0x00000000006122c8 in ?? ()
#2 0x0000000000588244 in ?? ()
#3 0x00000000005843d3 in ?? ()
#4 0x0000000000624666 in ?? ()
#5 0x00007f3253531184 in start_thread (arg=0x7f3253efa700) at pthread_create.c:312
#6 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 8 (Thread 0x7f324bfff700 (LWP 5642)):
#0 pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
#1 0x00000000005fef90 in ?? ()
#2 0x00000000006117a2 in ?? ()
#3 0x00000000005840fd in ?? ()
#4 0x00000000005853e6 in ?? ()
#5 0x00000000418b9b0e in ?? ()
#6 0x0000000000000000 in ?? ()
Thread 7 (Thread 0x7f324bdfe700 (LWP 5643)):
#0 pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
#1 0x000000000061b982 in ?? ()
#2 0x0000000000586b58 in ?? ()
#3 0x00000000005843d3 in ?? ()
#4 0x0000000000624666 in ?? ()
#5 0x00007f3253531184 in start_thread (arg=0x7f324bdfe700) at pthread_create.c:312
#6 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 6 (Thread 0x7f324b9be700 (LWP 5644)):
#0 0x00007f3253250fdd in poll () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007f324b9f2a52 in linux_netlink_event_thread_main () from /usr/local/lib/libftd2xx.so
#2 0x00007f3253531184 in start_thread (arg=0x7f324b9be700) at pthread_create.c:312
#3 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 5 (Thread 0x7f324b1bd700 (LWP 5645)):
#0 0x00007f3253250fdd in poll () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007f324b9eb14e in handle_events () from /usr/local/lib/libftd2xx.so
#2 0x00007f324b9eb569 in libusb_handle_events_timeout_completed () from /usr/local/lib/libftd2xx.so
#3 0x00007f324b9eb674 in libusb_handle_events_timeout () from /usr/local/lib/libftd2xx.so
#4 0x00007f324b9c924f in poll_async_libusb () from /usr/local/lib/libftd2xx.so
#5 0x00007f3253531184 in start_thread (arg=0x7f324b1bd700) at pthread_create.c:312
#6 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 4 (Thread 0x7f324a7ff700 (LWP 5652)):
#0 pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
#1 0x00007f324b9cf9fe in EventWait () from /usr/local/lib/libftd2xx.so
#2 0x00007f324b9d2651 in reader_thread () from /usr/local/lib/libftd2xx.so
#3 0x00007f3253531184 in start_thread (arg=0x7f324a7ff700) at pthread_create.c:312
#4 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 3 (Thread 0x7f3249ffe700 (LWP 5653)):
#0 0x00007f3253538ed9 in __libc_waitpid (pid=5655, stat_loc=0x7f3249ffce4c, options=0) at ../sysdeps/unix/sysv/linux/waitpid.c:40
#1 0x000000000049d689 in ?? ()
#2 0x0000000000424a0e in ?? ()
#3 <signal handler called>
#4 __GI___pthread_mutex_lock (mutex=0x0) at ../nptl/pthread_mutex_lock.c:66
#5 0x00007f324b9d1f52 in signalSomeEvents () from /usr/local/lib/libftd2xx.so
#6 0x00007f324b9d24f1 in ProcessBulkInData () from /usr/local/lib/libftd2xx.so
#7 0x00007f324b9d29bd in processor_thread () from /usr/local/lib/libftd2xx.so
#8 0x00007f3253531184 in start_thread (arg=0x7f3249ffe700) at pthread_create.c:312
#9 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 2 (Thread 0x7f32497fd700 (LWP 5654)):
#0 pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1 0x00007f324b9cf904 in EventWait () from /usr/local/lib/libftd2xx.so
#2 0x00007f324b9cbfd1 in write_thread () from /usr/local/lib/libftd2xx.so
#3 0x00007f3253531184 in start_thread (arg=0x7f32497fd700) at pthread_create.c:312
#4 0x00007f325325e37d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Thread 1 (Thread 0x7f325405d7c0 (LWP 5638)):
#0 0x00007f3253538b9d in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#1 0x00000000005fedfa in ?? ()
#2 0x000000000061101b in ?? ()
#3 0x000000000058415e in ?? ()
#4 0x0000000000585309 in ?? ()
#5 0x00000000418b3e8d in ?? ()
#6 0x00000000015de450 in ?? ()
#7 0x00007ffc9ba9b960 in ?? ()
#8 0x00007f3253fb8130 in ?? ()
#9 0x00007f3252400578 in ?? ()
#10 0x00007f3252400528 in ?? ()
#11 0x00000000015a41e0 in ?? ()
#12 0x00000000418b3e07 in ?? ()
#13 0x00007ffc9ba9b4d0 in ?? ()
#14 0x00007ffc9ba9b450 in ?? ()
/build/buildd/gdb-7.7.1/gdb/dwarf2-frame.c:692: internal-error: Unknown CFI encountered.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) [answered Y; input not from terminal]
/build/buildd/gdb-7.7.1/gdb/dwarf2-frame.c:692: internal-error: Unknown CFI encountered.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Create a core file of GDB? (y or n) [answered Y; input not from terminal]
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
Aborted (core dumped)
Edit the 2nd:
The representations of Mono's SafeWaitHandle and the expected parameter to SetEventNotification are completely different. Because the FTDI D2XX library is trying to treat the SafeWaitHandle as an EVENT_HANDLE (defined in the ftd2xx.h header; basically a pthread_cond_t and pthread_mutex_t), I think that's why we get a crash.
I think I can proceed by using P/Invoke with libpthread in the FTDI D2XX C# .NET wrapper to perform the necessary conversions between pthread conditions and mutexes and Mono SafeWaitHandle's. At this point, I need working C code which will loop and block on pthread_cond_wait for FTDI D2XX. I haven't gotten that code working yet. It's listed below if anyone has advice:
/*/*
gcc -o SetEventNotification_separate_thread SetEventNotification_separate_thread.c -lpthread -lftd2xx -Wl,-rpath /usr/local/lib
*/
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include "../ftd2xx.h"
static FT_HANDLE ft_handle;
// 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 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 void std_error_exit(const char *str)
{
perror(str);
exit(1);
}
// Reader thread function
static void *reader_func(void *arg)
{
FT_STATUS ft_status;
EVENT_HANDLE eh;
DWORD chars_in_q;
char buf[8192];
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 (;;)
{
printf("at for: chars_in_q: %d\n", chars_in_q);
if ((pterr = pthread_mutex_lock(&eh.eMutex))){
pthread_error_exit("pthread_mutex_lock", pterr);
printf("pthread_mutex_lock failed...\n");
}
printf("pthread_mutex_lock\n");
if ((ft_status = FT_GetQueueStatus(ft_handle, &chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
printf("before if (chars_in_q == 0): chars_in_q: %d\n", chars_in_q);
// while (chars_in_q == 0)
// {
printf("<<----------- SetEventNotification pthread_cond_wait --------------->>\n");
if ((pterr = pthread_cond_wait(&eh.eCondVar, &eh.eMutex)))
pthread_error_exit("pthread_cond_wait", pterr);
printf("pthread_cond_wait\n");
if ((ft_status = FT_GetQueueStatus(ft_handle,
&chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
printf("before if (chars_in_q == 0): chars_in_q: %d\n", chars_in_q);
// }
if ((pterr = pthread_mutex_unlock(&eh.eMutex)))
pthread_error_exit("pthread_mutex_unlock", pterr);
printf("pthread_mutex_unlock\n");
while (chars_in_q)
{
printf("at while (chars_in_q): chars_in_q: %d\n", 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;
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);
}
len_written = printf("%s\n", buf);
printf("len_to_read: %d\n", len_to_read);
printf("len_read: %d\n", len_read);
chars_in_q -= len_read;
}
}
}
int main(int argc, char *argv[])
{
FT_STATUS ft_status;
DWORD devCount;
DWORD devIndex = 0; // first device
DWORD numDevices = 1;
char serialNumber[64]; // more than enough room!
const unsigned int baudrate = 8000000;
FT_DEVICE_LIST_INFO_NODE * devInfo;
devInfo = malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*numDevices);
ft_status = FT_CreateDeviceInfoList(&devCount);
if( ft_status != FT_OK)
{
printf("No devices connected!\n");
exit(-1);
}
printf("Devices found: %d\n", devCount);
ft_status = FT_ListDevices((PVOID)devIndex, serialNumber, FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER);
if ( ft_status == FT_OK)
{
// FT_ListDevices OK, serial number is in serialNumber
printf("Device Serial Number: %s\n", serialNumber);
}
else
{
// FT_ListDevices failed
}
ft_status = FT_OpenEx(serialNumber, FT_OPEN_BY_SERIAL_NUMBER, &ft_handle);
printf("after calling FT_OpenEx\n");
if ( ft_status == FT_OK)
{
// FT_OpenEx OK, ft_handle not null
printf("FT_OpenEx succeeded\n");
}
else
{
printf("FT_OpenEx failed\n");
}
ft_status = FT_SetBaudRate(ft_handle, baudrate);
if ( ft_status == FT_OK)
{
// FT_SetBaudRate OK
printf("FT_SetBaudRate succeeded\n");
}
else
{
printf("FT_SetBaudRate failed\n");
}
ft_status = FT_SetFlowControl(ft_handle, FT_FLOW_RTS_CTS, 0, 0);
if ( ft_status == FT_OK)
{
// FT_SetFlowControl OK
printf("FT_SetFlowControl succeeded\n");
}
else
{
printf("FT_SetFlowControl failed\n");
}
// 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);
// Create reader and writer threads.
int pterr;
sigset_t sig_set;
(void)sigfillset(&sig_set);
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);
// Now wait for a signal to terminate us
int caught_sig;
if (sigwait(&sig_set, &caught_sig))
std_error_exit("sigwait");
exit(0);
}
gcc -o SetEventNotification_separate_thread SetEventNotification_separate_thread.c -lpthread -lftd2xx -Wl,-rpath /usr/local/lib
*/
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include "../ftd2xx.h"
static FT_HANDLE ft_handle;
// 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 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 void std_error_exit(const char *str)
{
perror(str);
exit(1);
}
// Reader thread function
static void *reader_func(void *arg)
{
FT_STATUS ft_status;
EVENT_HANDLE eh;
DWORD chars_in_q;
char buf[8192];
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 (;;)
{
printf("at for: chars_in_q: %d\n", chars_in_q);
if ((pterr = pthread_mutex_lock(&eh.eMutex))){
pthread_error_exit("pthread_mutex_lock", pterr);
printf("pthread_mutex_lock failed...\n");
}
printf("pthread_mutex_lock\n");
if ((ft_status = FT_GetQueueStatus(ft_handle, &chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
printf("before if (chars_in_q == 0): chars_in_q: %d\n", chars_in_q);
// while (chars_in_q == 0)
// {
printf("<<----------- SetEventNotification pthread_cond_wait --------------->>\n");
if ((pterr = pthread_cond_wait(&eh.eCondVar, &eh.eMutex)))
pthread_error_exit("pthread_cond_wait", pterr);
printf("pthread_cond_wait\n");
if ((ft_status = FT_GetQueueStatus(ft_handle,
&chars_in_q)) != FT_OK)
ft_error_exit("FT_GetQueueStatus", ft_status);
printf("before if (chars_in_q == 0): chars_in_q: %d\n", chars_in_q);
// }
if ((pterr = pthread_mutex_unlock(&eh.eMutex)))
pthread_error_exit("pthread_mutex_unlock", pterr);
printf("pthread_mutex_unlock\n");
while (chars_in_q)
{
printf("at while (chars_in_q): chars_in_q: %d\n", 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;
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);
}
len_written = printf("%s\n", buf);
printf("len_to_read: %d\n", len_to_read);
printf("len_read: %d\n", len_read);
chars_in_q -= len_read;
}
}
}
int main(int argc, char *argv[])
{
FT_STATUS ft_status;
DWORD devCount;
DWORD devIndex = 0; // first device
DWORD numDevices = 1;
char serialNumber[64]; // more than enough room!
const unsigned int baudrate = 8000000;
FT_DEVICE_LIST_INFO_NODE * devInfo;
devInfo = malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*numDevices);
ft_status = FT_CreateDeviceInfoList(&devCount);
if( ft_status != FT_OK)
{
printf("No devices connected!\n");
exit(-1);
}
printf("Devices found: %d\n", devCount);
ft_status = FT_ListDevices((PVOID)devIndex, serialNumber, FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER);
if ( ft_status == FT_OK)
{
// FT_ListDevices OK, serial number is in serialNumber
printf("Device Serial Number: %s\n", serialNumber);
}
else
{
// FT_ListDevices failed
}
ft_status = FT_OpenEx(serialNumber, FT_OPEN_BY_SERIAL_NUMBER, &ft_handle);
printf("after calling FT_OpenEx\n");
if ( ft_status == FT_OK)
{
// FT_OpenEx OK, ft_handle not null
printf("FT_OpenEx succeeded\n");
}
else
{
printf("FT_OpenEx failed\n");
}
ft_status = FT_SetBaudRate(ft_handle, baudrate);
if ( ft_status == FT_OK)
{
// FT_SetBaudRate OK
printf("FT_SetBaudRate succeeded\n");
}
else
{
printf("FT_SetBaudRate failed\n");
}
ft_status = FT_SetFlowControl(ft_handle, FT_FLOW_RTS_CTS, 0, 0);
if ( ft_status == FT_OK)
{
// FT_SetFlowControl OK
printf("FT_SetFlowControl succeeded\n");
}
else
{
printf("FT_SetFlowControl failed\n");
}
// 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);
// Create reader and writer threads.
int pterr;
sigset_t sig_set;
(void)sigfillset(&sig_set);
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);
// Now wait for a signal to terminate us
int caught_sig;
if (sigwait(&sig_set, &caught_sig))
std_error_exit("sigwait");
exit(0);
}
It always gets stuck on the pthread_cond_wait. Any thoughts would be appreciated.
Again no answer, but I don't know how Mono defines EventWaitHandle.SafeWaitHandle.
In the example provided by FTDI for Linux (c) this is the definition
so I wonder if this is related to the definition of SafeWaitHandle