libexsid  2.1
exSID_ftdiwrap.c
Go to the documentation of this file.
1 //
2 // exSID_ftdiwrap.c
3 // An FTDI access wrapper for exSID USB
4 //
5 // (C) 2016 Thibaut VARENE
6 // License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
7 //
8 // Coding style is somewhat unorthodox ;P
9 
19 #include "exSID_defs.h"
20 #include <stdio.h>
21 
22 #ifdef HAVE_DLFCN_H
23  #include <dlfcn.h>
24  #define TEXT(x) x
25 #elif defined (_WIN32)
26  #include <windows.h>
27  #include <time.h> // missing include in libftdi on WIN32
28 #else
29  #error dl not supported
30 #endif
31 
32 #ifdef HAVE_FTD2XX
33  #include <ftd2xx.h>
34  #ifndef XSFW_SUPPORT
35  #define XSFW_SUPPORT
36  #endif
37 #else
38  #warning libftd2xx support disabled.
39 #endif
40 
41 #ifdef HAVE_FTDI
42  #include <ftdi.h>
43  #ifndef XSFW_SUPPORT
44  #define XSFW_SUPPORT
45  #endif
46 #else
47  #warning libftdi support disabled.
48 #endif
49 
50 #ifndef XSFW_SUPPORT
51  #error No known method to access FTDI chip
52 #endif
53 
54 #define XSFW_WRAPDECL
55 #include "exSID_ftdiwrap.h"
56 
57 #define EXSID_INTERFACES "libftdi, libftd2xx" // XXX TODO Should be set by configure
58 
59 #ifdef _WIN32
60  static HMODULE dlhandle = NULL;
61 
62  static char *_xSfw_dlerror() {
63  DWORD dwError = GetLastError();
64  char* lpMsgBuf = NULL;
65  FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
66  0,
67  dwError,
68  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
69  (LPSTR)&lpMsgBuf,
70  0,
71  NULL);
72  return lpMsgBuf;
73  }
74 
75  #define _xSfw_dlopen(libName) LoadLibrary(libName)
76  #define _xSfw_dlsym(hModule, lpProcName) GetProcAddress(hModule, lpProcName)
77  #define _xSfw_dlclose(hModule) FreeLibrary(hModule)
78  #define _xSfw_clear_dlerror() SetLastError(0)
79  #define _xSfw_free_errstr(str) LocalFree(str)
80 #else // ! _WIN32
81  static void * dlhandle = NULL;
82  #define _xSfw_dlopen(filename) dlopen(filename, RTLD_NOW|RTLD_LOCAL)
83  #define _xSfw_dlsym(handle, symbol) dlsym(handle, symbol)
84  #define _xSfw_dlclose(handle) dlclose(handle)
85  #define _xSfw_dlerror() dlerror()
86  #define _xSfw_clear_dlerror() dlerror()
87  #define _xSfw_free_errstr(str) /* nothing */
88 #endif // _WIN32
89 
90 
92 typedef enum {
96 } libtype_t;
97 
98 static libtype_t libtype = XS_LIBNONE;
99 
100 // private functions
101 static int (* _xSfw_set_baudrate)(void * ftdi, int baudrate);
102 static int (* _xSfw_set_line_property)(void * ftdi, int bits, int sbit, int parity);
103 static int (* _xSfw_setflowctrl)(void * ftdi, int flowctrl);
104 static int (* _xSfw_set_latency_timer)(void * ftdi, unsigned char latency);
105 
106 // callbacks for ftdi
107 #ifdef HAVE_FTDI
108 static int (* _ftdi_usb_open_desc)(void *, int, int, const char *, const char *);
109 #endif
110 
111 // callbacks for FTD2XX
112 #ifdef HAVE_FTD2XX
113 static int (*_FT_Write)(void *, LPVOID, int, unsigned int *);
114 static int (*_FT_Read)(void *, LPVOID, int, unsigned int *);
115 static int (*_FT_OpenEx)(const char *, int, void **);
116 static int (*_FT_ResetDevice)(void *);
117 static int (*_FT_SetBaudRate)(void *, int);
118 static int (*_FT_SetDataCharacteristics)(void *, int, int, int);
119 static int (*_FT_SetFlowControl)(void *, int, int, int);
120 static int (*_FT_SetLatencyTimer)(void *, unsigned char);
121 static int (*_FT_Close)(void *);
122 #endif
123 
124 // wrappers for ftdi
125 #ifdef HAVE_FTDI
126 static int _xSfwftdi_usb_open_desc(void ** ftdi, int vid, int pid, const char * desc, const char * serial)
127 {
128  return _ftdi_usb_open_desc(*ftdi, vid, pid, desc, serial);
129 }
130 #endif
131 
132 // wrappers for FTD2XX
133 #ifdef HAVE_FTD2XX
134 static int _xSfwFT_write_data(void * restrict ftdi, const unsigned char * restrict buf, int size)
135 {
136  DWORD dummysize; // DWORD in unsigned int
137  int rval;
138  if(unlikely(rval = _FT_Write(ftdi, (LPVOID)buf, size, &dummysize)))
139  return -rval;
140  else
141  return dummysize;
142 }
143 
144 static int _xSfwFT_read_data(void * restrict ftdi, unsigned char * restrict buf, int size)
145 {
146  DWORD dummysize; // DWORD in unsigned int
147  int rval;
148  if (unlikely(rval = _FT_Read(ftdi, (LPVOID)buf, size, &dummysize)))
149  return -rval;
150  else
151  return dummysize;
152 }
153 
154 static int _xSfwFT_usb_open_desc(void ** ftdi, int vid, int pid, const char * desc, const char * serial)
155 {
156  return -_FT_OpenEx(desc, FT_OPEN_BY_DESCRIPTION, ftdi);
157 }
158 
159 static int _xSfwFT_usb_close(void * ftdi)
160 {
161  return -_FT_Close(ftdi);
162 }
163 
164 static char * _xSfwFT_get_error_string(void * ftdi)
165 {
166  return "FTD2XX error";
167 }
168 #endif
169 
176 {
177 #define XSFW_DLSYM(a, b) \
178  *(void **)(&a) = _xSfw_dlsym(dlhandle, b); \
179  if (a == NULL) { \
180  dlerrorstr = _xSfw_dlerror(); \
181  goto dlfail; \
182  }
183 
184  char * dlerrorstr = NULL;
185 
186  if (dlhandle) {
187  xsdbg("recursive dlopen()!\n");
188  return 0;
189  }
190 
191 #ifdef HAVE_FTDI
192  // try libftdi1 first - XXX TODO version check
193  if ((dlhandle = _xSfw_dlopen(TEXT("libftdi1" SHLIBEXT)))) {
194  _xSfw_clear_dlerror(); // clear dlerror
195  XSFW_DLSYM(xSfw_new, "ftdi_new");
196  XSFW_DLSYM(xSfw_free, "ftdi_free");
197  XSFW_DLSYM(xSfw_write_data, "ftdi_write_data");
198  XSFW_DLSYM(xSfw_read_data, "ftdi_read_data");
199  XSFW_DLSYM(_ftdi_usb_open_desc, "ftdi_usb_open_desc");
200  xSfw_usb_open_desc = _xSfwftdi_usb_open_desc;
201  XSFW_DLSYM(_xSfw_set_baudrate, "ftdi_set_baudrate");
202  XSFW_DLSYM(_xSfw_set_line_property, "ftdi_set_line_property");
203  XSFW_DLSYM(_xSfw_setflowctrl, "ftdi_setflowctrl");
204  XSFW_DLSYM(_xSfw_set_latency_timer, "ftdi_set_latency_timer");
205  XSFW_DLSYM(xSfw_usb_close, "ftdi_usb_close");
206  XSFW_DLSYM(xSfw_get_error_string, "ftdi_get_error_string");
207  libtype = XS_LIBFTDI;
208  xsdbg("Using libftdi\n");
209  }
210  else
211 #endif
212 #ifdef HAVE_FTD2XX
213 #ifdef _WIN32
214 # define LIBFTD2XX "ftd2xx"
215 #else
216 # define LIBFTD2XX "libftd2xx"
217 #endif
218  // otherwise try libftd2xx - XXX TODO version check
219  if ((dlhandle = _xSfw_dlopen(TEXT(LIBFTD2XX SHLIBEXT)))) {
220  _xSfw_clear_dlerror(); // clear dlerror
221  xSfw_new = NULL;
222  xSfw_free = NULL;
223  XSFW_DLSYM(_FT_Write, "FT_Write");
224  xSfw_write_data = _xSfwFT_write_data;
225  XSFW_DLSYM(_FT_Read, "FT_Read");
226  xSfw_read_data = _xSfwFT_read_data;
227  XSFW_DLSYM(_FT_OpenEx, "FT_OpenEx");
228  xSfw_usb_open_desc = _xSfwFT_usb_open_desc;
229  XSFW_DLSYM(_FT_ResetDevice, "FT_ResetDevice");
230  XSFW_DLSYM(_FT_SetBaudRate, "FT_SetBaudRate");
231  XSFW_DLSYM(_FT_SetDataCharacteristics, "FT_SetDataCharacteristics");
232  XSFW_DLSYM(_FT_SetFlowControl, "FT_SetFlowControl");
233  XSFW_DLSYM(_FT_SetLatencyTimer, "FT_SetLatencyTimer");
234  XSFW_DLSYM(_FT_Close, "FT_Close");
235  xSfw_usb_close = _xSfwFT_usb_close;
236  xSfw_get_error_string = _xSfwFT_get_error_string;
237  libtype = XS_LIBFTD2XX;
238  xsdbg("Using libftd2xx\n");
239  }
240  else
241 #endif
242  // if none worked, fail.
243  {
244  xsdbg("No method found to access FTDI interface.\n"
245  "Are any of the following libraries installed?\n"
246  "\t" EXSID_INTERFACES);
247  return -1;
248  }
249 
250  return 0;
251 
252 dlfail:
253  xsdbg("dlsym error: %s", dlerrorstr);
254  _xSfw_free_errstr(dlerrorstr);
255  xSfw_dlclose(dlhandle);
256  return -1;
257 }
258 
267 int xSfw_usb_setup(void * ftdi, int baudrate, int latency)
268 {
269  int rval = 0;
270 
271 #ifdef HAVE_FTDI
272  if (XS_LIBFTDI == libtype) {
273  // ftdi_usb_open_desc() performs device reset
274  rval = _xSfw_set_baudrate(ftdi, baudrate);
275  if (rval < 0) {
276  xsdbg("SBR error");
277  goto setupfail;
278  }
279  rval = _xSfw_set_line_property(ftdi, BITS_8 , STOP_BIT_1, NONE);
280  if (rval < 0) {
281  xsdbg("SLP error");
282  goto setupfail;
283  }
284  rval = _xSfw_setflowctrl(ftdi, SIO_DISABLE_FLOW_CTRL);
285  if (rval < 0) {
286  xsdbg("SFC error");
287  goto setupfail;
288  }
289  rval = _xSfw_set_latency_timer(ftdi, latency);
290  if (rval < 0) {
291  xsdbg("SLT error");
292  goto setupfail;
293  }
294  }
295  else
296 #endif
297 #ifdef HAVE_FTD2XX
298  if (XS_LIBFTD2XX == libtype) {
299  rval = -_FT_ResetDevice(ftdi);
300  if (rval < 0) {
301  xsdbg("RSD error");
302  goto setupfail;
303  }
304  rval = -_FT_SetBaudRate(ftdi, baudrate);
305  if (rval < 0) {
306  xsdbg("SBR error");
307  goto setupfail;
308  }
309  rval = -_FT_SetDataCharacteristics(ftdi, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE);
310  if (rval < 0) {
311  xsdbg("SLP error");
312  goto setupfail;
313  }
314  rval = -_FT_SetFlowControl(ftdi, FT_FLOW_NONE, 0, 0);
315  if (rval < 0) {
316  xsdbg("SFC error");
317  goto setupfail;
318  }
319  rval = -_FT_SetLatencyTimer(ftdi, latency);
320  if (rval < 0) {
321  xsdbg("SLT error");
322  goto setupfail;
323  }
324  }
325  else
326 #endif
327  xsdbg("Unkown access method\n");
328 setupfail:
329  return rval;
330 }
331 
336 {
337  if (dlhandle != NULL) {
338  _xSfw_dlclose(dlhandle);
339  dlhandle = NULL;
340  }
341 }
xSfw_write_data_p xSfw_write_data
Data write callback.
#define EXSID_INTERFACES
#define unlikely(x)
Definition: exSID_defs.h:110
#define _xSfw_dlopen(filename)
int xSfw_dlopen()
Attempt to dlopen a known working library to access FTDI chip.
#define _xSfw_clear_dlerror()
FTDI access wrapper header file.
const int vid
USB VID.
Definition: exSID.c:75
#define XSFW_DLSYM(a, b)
void * ftdi
FTDI device handle.
Definition: exSID.c:119
xSfw_read_data_p xSfw_read_data
Data read callback.
#define _xSfw_dlclose(handle)
const int pid
USB PID.
Definition: exSID.c:74
xSfw_new_p xSfw_new
Handle allocation callback.
#define _xSfw_dlerror()
xSfw_usb_open_desc_p xSfw_usb_open_desc
Device open callback.
#define xsdbg(format,...)
Definition: exSID_defs.h:102
#define _xSfw_free_errstr(str)
xSfw_get_error_string_p xSfw_get_error_string
Human readable error string callback.
int xSfw_usb_setup(void *ftdi, int baudrate, int latency)
Setup FTDI chip to match exSID firmware.
const char * desc
USB device description string.
Definition: exSID.c:73
libexsid private definitions header file.
libtype_t
Flag to signal which of the supported libraries is in use.
void xSfw_dlclose()
Release dlopen'd library.
xSfw_free_p xSfw_free
Handle deallocation callback.
xSfw_usb_close_p xSfw_usb_close
Device close callback.