cog/Frameworks/libsidplay/sidplay-residfp-code/.svn/pristine/ac/ac8281d6476a3b5d331a247ca2a...

332 lines
8.6 KiB
Plaintext

//
// exSID_ftdiwrap.c
// An FTDI access wrapper for exSID USB
//
// (C) 2016 Thibaut VARENE
// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
//
// Coding style is somewhat unorthodox ;P
/**
* @file
* exSID USB FTDI access wrapper
* @author Thibaut VARENE
* @date 2016
* @note Primary target is libftdi (cleaner API), adaptations are made for others.
* Sadly, libftdi's implementation of read() is unreliable (it doesn't seem
* to honour the usb timeout value and doesn't properly block long enough).
* This is why libftd2xx is prefered (tried first) for now. Unfortunately,
* using libftd2xx comes with a significant performance penalty since
* the code is tailored for libftdi.
*/
#include "exSID_defs.h"
#include <stdio.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#define TEXT(x) x
#elif defined (_WIN32)
#include <windows.h>
#else
#error dl not supported
#endif
#ifdef HAVE_FTD2XX
#include <ftd2xx.h>
#ifndef XSFW_SUPPORT
#define XSFW_SUPPORT
#endif
#else
#warning libftd2xx support disabled.
#endif
#ifdef HAVE_FTDI
#include <ftdi.h>
#ifndef XSFW_SUPPORT
#define XSFW_SUPPORT
#endif
#else
#warning libftdi support disabled.
#endif
#ifndef XSFW_SUPPORT
#error No known method to access FTDI chip
#endif
#define XSFW_WRAPDECL
#include "exSID_ftdiwrap.h"
#define EXSID_INTERFACES "libftd2xx, libftdi" // XXX TODO Should be set by configure
static unsigned int dummysize = 0; // DWORD in unsigned int
#ifdef _WIN32
static HMODULE dlhandle = NULL;
static char *_xSfw_dlerror() {
DWORD dwError = GetLastError();
char* lpMsgBuf = NULL;
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
0,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf,
0,
NULL);
return lpMsgBuf;
}
#define _xSfw_dlopen(libName) LoadLibrary(libName)
#define _xSfw_dlsym(hModule, lpProcName) GetProcAddress(hModule, lpProcName)
#define _xSfw_dlclose(hModule) FreeLibrary(hModule)
#define _xSfw_clear_dlerror() SetLastError(0)
#define _xSfw_free_errstr(str) LocalFree(str)
#else // ! _WIN32
static void * dlhandle = NULL;
#define _xSfw_dlopen(filename) dlopen(filename, RTLD_NOW|RTLD_LOCAL)
#define _xSfw_dlsym(handle, symbol) dlsym(handle, symbol)
#define _xSfw_dlclose(handle) dlclose(handle)
#define _xSfw_dlerror() dlerror()
#define _xSfw_clear_dlerror() dlerror()
#define _xSfw_free_errstr(str) /* nothing */
#endif // _WIN32
/** Flag to signal which of the supported libraries is in use */
typedef enum {
XS_LIBNONE,
XS_LIBFTDI,
XS_LIBFTD2XX,
} libtype_t;
static libtype_t libtype = XS_LIBNONE;
// private functions
static int (* _xSfw_set_baudrate)(void * ftdi, int baudrate);
static int (* _xSfw_set_line_property)(void * ftdi, int bits, int sbit, int parity);
static int (* _xSfw_setflowctrl)(void * ftdi, int flowctrl);
static int (* _xSfw_set_latency_timer)(void * ftdi, unsigned char latency);
// callbacks for ftdi
#ifdef HAVE_FTDI
static int (* _ftdi_usb_open_desc)(void *, int, int, const char *, const char *);
#endif
// callbacks for FTD2XX
#ifdef HAVE_FTD2XX
static int (*_FT_Write)(void *, LPVOID, int, unsigned int *);
static int (*_FT_Read)(void *, LPVOID, int, unsigned int *);
static int (*_FT_OpenEx)(const char *, int, void **);
static int (*_FT_SetBaudRate)(void *, int);
static int (*_FT_SetDataCharacteristics)(void *, int, int, int);
static int (*_FT_SetFlowControl)(void *, int, int, int);
static int (*_FT_SetLatencyTimer)(void *, unsigned char);
static int (*_FT_Purge)(void *, int);
static int (*_FT_Close)(void *);
#endif
// wrappers for ftdi
#ifdef HAVE_FTDI
static int _xSfwftdi_usb_open_desc(void ** ftdi, int vid, int pid, const char * desc, const char * serial)
{
return _ftdi_usb_open_desc(*ftdi, vid, pid, desc, serial);
}
#endif
// wrappers for FTD2XX
#ifdef HAVE_FTD2XX
static int _xSfwFT_write_data(void * restrict ftdi, const unsigned char * restrict buf, int size)
{
static int rval;
if(unlikely(rval = _FT_Write(ftdi, (LPVOID)buf, size, &dummysize)))
return -rval;
else
return dummysize;
}
static int _xSfwFT_read_data(void * restrict ftdi, unsigned char * restrict buf, int size)
{
static int rval;
if (unlikely(rval = _FT_Read(ftdi, (LPVOID)buf, size, &dummysize)))
return -rval;
else
return dummysize;
}
static int _xSfwFT_usb_open_desc(void ** ftdi, int vid, int pid, const char * desc, const char * serial)
{
return -_FT_OpenEx(desc, FT_OPEN_BY_DESCRIPTION, ftdi);
}
static int _xSfwFT_usb_purge_buffers(void * ftdi)
{
return -_FT_Purge(ftdi, FT_PURGE_RX | FT_PURGE_TX);
}
static int _xSfwFT_usb_close(void * ftdi)
{
return -_FT_Close(ftdi);
}
static char * _xSfwFT_get_error_string(void * ftdi)
{
return "FTD2XX error";
}
#endif
/**
* Attempt to dlopen a known working library to access FTDI chip.
* Will try libftd2xx first, then libftdi.
* @return 0 on success, -1 on error.
*/
int xSfw_dlopen()
{
#define XSFW_DLSYM(a, b) \
*(void **)(&a) = _xSfw_dlsym(dlhandle, b); \
if (a == NULL) { \
dlerrorstr = _xSfw_dlerror(); \
goto dlfail; \
}
char * dlerrorstr = NULL;
#ifdef HAVE_FTD2XX
#ifdef _WIN32
# define LIBFTD2XX "ftd2xx"
#else
# define LIBFTD2XX "libftd2xx"
#endif
// try libftd2xx first - XXX TODO version check
if ((dlhandle = _xSfw_dlopen(TEXT(LIBFTD2XX SHLIBEXT)))) {
_xSfw_clear_dlerror(); // clear dlerror
xSfw_new = NULL;
xSfw_free = NULL;
XSFW_DLSYM(_FT_Write, "FT_Write");
xSfw_write_data = _xSfwFT_write_data;
XSFW_DLSYM(_FT_Read, "FT_Read");
xSfw_read_data = _xSfwFT_read_data;
XSFW_DLSYM(_FT_OpenEx, "FT_OpenEx");
xSfw_usb_open_desc = _xSfwFT_usb_open_desc;
XSFW_DLSYM(_FT_SetBaudRate, "FT_SetBaudRate");
XSFW_DLSYM(_FT_SetDataCharacteristics, "FT_SetDataCharacteristics");
XSFW_DLSYM(_FT_SetFlowControl, "FT_SetFlowControl");
XSFW_DLSYM(_FT_SetLatencyTimer, "FT_SetLatencyTimer");
XSFW_DLSYM(_FT_Purge, "FT_Purge");
xSfw_usb_purge_buffers = _xSfwFT_usb_purge_buffers;
XSFW_DLSYM(_FT_Close, "FT_Close");
xSfw_usb_close = _xSfwFT_usb_close;
xSfw_get_error_string = _xSfwFT_get_error_string;
libtype = XS_LIBFTD2XX;
xsdbg("Using libftd2xx\n");
}
else
#endif
#ifdef HAVE_FTDI
// otherwise try libftdi1 - XXX TODO version check
if ((dlhandle = _xSfw_dlopen(TEXT("libftdi1" SHLIBEXT)))) {
_xSfw_clear_dlerror(); // clear dlerror
XSFW_DLSYM(xSfw_new, "ftdi_new");
XSFW_DLSYM(xSfw_free, "ftdi_free");
XSFW_DLSYM(xSfw_write_data, "ftdi_write_data");
XSFW_DLSYM(xSfw_read_data, "ftdi_read_data");
XSFW_DLSYM(_ftdi_usb_open_desc, "ftdi_usb_open_desc");
xSfw_usb_open_desc = _xSfwftdi_usb_open_desc;
XSFW_DLSYM(_xSfw_set_baudrate, "ftdi_set_baudrate");
XSFW_DLSYM(_xSfw_set_line_property, "ftdi_set_line_property");
XSFW_DLSYM(_xSfw_setflowctrl, "ftdi_setflowctrl");
XSFW_DLSYM(_xSfw_set_latency_timer, "ftdi_set_latency_timer");
XSFW_DLSYM(xSfw_usb_purge_buffers, "ftdi_usb_purge_buffers");
XSFW_DLSYM(xSfw_usb_close, "ftdi_usb_close");
XSFW_DLSYM(xSfw_get_error_string, "ftdi_get_error_string");
libtype = XS_LIBFTDI;
xsdbg("Using libftdi\n");
}
else
#endif
// if none worked, fail.
{
xserror("No method found to access FTDI interface.\n"
"Are any of the following libraries installed?\n"
"\t" EXSID_INTERFACES "\n");
return -1;
}
return 0;
dlfail:
xserror("dlsym error: %s\n", dlerrorstr);
_xSfw_free_errstr(dlerrorstr);
xSfw_dlclose(dlhandle);
return -1;
}
/**
* Setup FTDI chip to match exSID firmware.
* Defaults to 8N1, no flow control.
* @param ftdi ftdi handle
* @param baudrate Target baudrate
* @param latency Target latency
* @return 0 on success, rval on error.
*/
int xSfw_usb_setup(void * ftdi, int baudrate, int latency)
{
int rval = 0;
#ifdef HAVE_FTDI
if (XS_LIBFTDI == libtype) {
rval = _xSfw_set_baudrate(ftdi, baudrate);
if (rval < 0)
xserror("SBR error\n");
rval = _xSfw_set_line_property(ftdi, BITS_8 , STOP_BIT_1, NONE);
if (rval < 0)
xserror("SLP error\n");
rval = _xSfw_setflowctrl(ftdi, SIO_DISABLE_FLOW_CTRL);
if (rval < 0)
xserror("SFC error\n");
rval = _xSfw_set_latency_timer(ftdi, latency);
if (rval < 0)
xserror("SLT error\n");
}
else
#endif
#ifdef HAVE_FTD2XX
if (XS_LIBFTD2XX == libtype) {
rval = -_FT_SetBaudRate(ftdi, baudrate);
if (rval < 0)
xserror("SBR error\n");
rval = -_FT_SetDataCharacteristics(ftdi, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE);
if (rval < 0)
xserror("SLP error\n");
rval = -_FT_SetFlowControl(ftdi, FT_FLOW_NONE, 0, 0);
if (rval < 0)
xserror("SFC error\n");
rval = -_FT_SetLatencyTimer(ftdi, latency);
if (rval < 0)
xserror("SLT error\n");
}
else
#endif
xserror("Unkown access method\n");
return rval;
}
/**
* Release dlopen'd library.
*/
void xSfw_dlclose()
{
if (dlhandle != NULL) {
_xSfw_dlclose(dlhandle);
dlhandle = NULL;
}
}