-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathftd2xx2libftdi.c
342 lines (283 loc) · 8.6 KB
/
ftd2xx2libftdi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2020 manison <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdbool.h>
#include <crtdbg.h>
#include "ftd2xx2libftdi.h"
#define VcppException(sev, err) ((sev) | (FACILITY_VISUALCPP<<16) | (err))
#define VcppModNotFound VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND)
#define VcppProcNotFound VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND)
static int IsDelayLoadException(unsigned int code)
{
if (code == VcppModNotFound || code == VcppProcNotFound)
return EXCEPTION_EXECUTE_HANDLER;
return EXCEPTION_CONTINUE_SEARCH;
}
#ifdef _M_X64
# define FTD2XX_NAME "ftd2xx64.dll"
#else
# define FTD2XX_NAME "ftd2xx.dll"
#endif
#define FT_MOD_NOT_FOUND ((FT_STATUS)ERROR_MOD_NOT_FOUND)
#define _d(call) __try { \
FT_STATUS __status = call; \
if (!FT_SUCCESS(__status)) \
ftdi->last_error = ft_strerr(__status); \
return __status; \
} \
__except (IsDelayLoadException(GetExceptionCode())) { \
ftdi->last_error = "Unable to find " FTD2XX_NAME; \
return FT_MOD_NOT_FOUND; \
}
static inline const char * ft_strerr(FT_STATUS status)
{
static const char *msg[] = {
"ok",
"invalid handle",
"device not found",
"device not opened",
"io error",
"insufficient resources",
"invalid parameter",
"invalid baud rate",
"device not opened for erase",
"device not opened for write",
"failed to write device",
"eeprom read failed",
"eeprom write failed",
"eeprom erase failed",
"eeprom not present",
"eeprom not programmed",
"invalid args",
"not supported",
"other error",
"device list not ready",
};
if (status < sizeof(msg) / sizeof(msg[0]))
return msg[status];
return NULL;
}
static FT_STATUS FTd_OpenEx(struct ftdi_context *ftdi, PVOID pArg1, DWORD Flags)
{
_d(FT_OpenEx(pArg1, Flags, &ftdi->handle))
}
static FT_STATUS FTd_Close(struct ftdi_context *ftdi)
{
_d(FT_Close(ftdi->handle))
}
static FT_STATUS FTd_CreateDeviceInfoList(struct ftdi_context *ftdi, LPDWORD lpdwNumDevs)
{
_d(FT_CreateDeviceInfoList(lpdwNumDevs))
}
static FT_STATUS FTd_GetDeviceInfoList(struct ftdi_context *ftdi, FT_DEVICE_LIST_INFO_NODE *pDest, LPDWORD lpdwNumDevs)
{
_d(FT_GetDeviceInfoList(pDest, lpdwNumDevs))
}
static FT_STATUS FTd_GetDeviceInfo(struct ftdi_context *ftdi, FT_DEVICE *lpftDevice, LPDWORD lpdwID, PCHAR SerialNumber, PCHAR Description, LPVOID Dummy)
{
_d(FT_GetDeviceInfo(ftdi->handle, lpftDevice, lpdwID, SerialNumber, Description, Dummy));
}
static FT_STATUS FTd_SetBitMode(struct ftdi_context *ftdi, UCHAR ucMask, UCHAR ucEnable)
{
_d(FT_SetBitMode(ftdi->handle, ucMask, ucEnable))
}
static FT_STATUS FTd_SetBaudRate(struct ftdi_context *ftdi, ULONG BaudRate)
{
_d(FT_SetBaudRate(ftdi->handle, BaudRate))
}
static FT_STATUS FTd_Read(struct ftdi_context *ftdi, LPVOID lpBuffer, DWORD dwBytesToRead, LPDWORD lpBytesReturned)
{
_d(FT_Read(ftdi->handle, lpBuffer, dwBytesToRead, lpBytesReturned))
}
static FT_STATUS FTd_Write(struct ftdi_context *ftdi, LPVOID lpBuffer, DWORD dwBytesToWrite, LPDWORD lpBytesWritten)
{
_d(FT_Write(ftdi->handle, lpBuffer, dwBytesToWrite, lpBytesWritten))
}
static FT_STATUS FTd_SetTimeouts(struct ftdi_context *ftdi, ULONG ReadTimeout, ULONG WriteTimeout)
{
_d(FT_SetTimeouts(ftdi->handle, ReadTimeout, WriteTimeout))
}
static FT_STATUS FTd_SetLatencyTimer(struct ftdi_context *ftdi, UCHAR ucLatency)
{
_d(FT_SetLatencyTimer(ftdi->handle, ucLatency))
}
static FT_STATUS FTd_Purge(struct ftdi_context *ftdi, ULONG Mask)
{
_d(FT_Purge(ftdi->handle, Mask))
}
struct ftdi_context *ftdi_new(void)
{
struct ftdi_context * ftdi = (struct ftdi_context *)malloc(sizeof(struct ftdi_context));
if (ftdi == NULL)
return NULL;
memset(ftdi, 0, sizeof(*ftdi));
return ftdi;
}
int ftdi_init(struct ftdi_context *ftdi)
{
memset(ftdi, 0, sizeof(*ftdi));
ftdi_set_interface(ftdi, INTERFACE_ANY);
return 0;
}
int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface intf)
{
if (ftdi == NULL)
return -2;
int index = (intf == INTERFACE_ANY) ? INTERFACE_A : intf;
if (ftdi->handle != NULL && index != ftdi->index) {
ftdi->last_error = "interface can not be changed on an already open device";
return -3;
}
ftdi->index = index;
return 0;
}
void ftdi_deinit(struct ftdi_context *ftdi)
{
if (ftdi == NULL)
return;
if (ftdi->handle != NULL) {
FT_STATUS status = FTd_Close(ftdi);
_ASSERTE(FT_SUCCESS(status));
ftdi->handle = NULL;
}
}
void ftdi_free(struct ftdi_context *ftdi)
{
ftdi_deinit(ftdi);
free(ftdi);
}
static bool matches_criteria(const FT_DEVICE_LIST_INFO_NODE *devinfo, unsigned current, int vendor, int product,
const char *description, const char *serial, unsigned int index)
{
if (description != NULL && strcmp(devinfo->Description, description) != 0)
return false;
if (serial != NULL && strcmp(devinfo->SerialNumber, serial) != 0)
return false;
if (index > 0 && current != index)
return false;
return true;
}
int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
const char *description, const char *serial, unsigned int index)
{
_ASSERTE(ftdi != NULL);
_ASSERTE(ftdi->handle == NULL);
// Call to any ftd2xx.dll function will try to delay load it, possibly raising an exception.
DWORD count;
FT_STATUS status = FTd_CreateDeviceInfoList(ftdi, &count);
if (!FT_SUCCESS(status)) {
// last_error is already set
return -3;
}
if (count == 0) {
ftdi->last_error = "no device found";
return -3;
}
FT_DEVICE_LIST_INFO_NODE *devinfo_list = malloc(sizeof(FT_DEVICE_LIST_INFO_NODE) * count);
status = FTd_GetDeviceInfoList(ftdi, devinfo_list, &count);
if (!FT_SUCCESS(status)) {
free(devinfo_list);
// last_error is already set
return -3;
}
FT_DEVICE_LIST_INFO_NODE *devinfo = NULL;
for (DWORD i = 0; i < count; i++) {
if (matches_criteria(&devinfo_list[i], i, vendor, product, description, serial, index)) {
devinfo = &devinfo_list[i];
break;
}
}
if (devinfo == NULL) {
ftdi->last_error = "device not found";
free(devinfo_list);
return -3;
}
FT_DEVICE type = devinfo->Type;
status = FTd_OpenEx(ftdi, devinfo->SerialNumber, FT_OPEN_BY_SERIAL_NUMBER);
free(devinfo_list);
if (!FT_SUCCESS(status)) {
// last_error is already set
return -3;
}
// libftdi ftdi_read_data does not block and returns whatever it currently
// has. Set the read timeout so the FT_Read returns (almost) immediately.
status = FTd_SetTimeouts(ftdi, 1, 1000);
_ASSERTE(FT_SUCCESS(status));
ftdi->usb_dev = ftdi->handle;
ftdi->type = type;
return 0;
}
int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
{
FT_STATUS status = FTd_Purge(ftdi, FT_PURGE_RX | FT_PURGE_TX);
if (!FT_SUCCESS(status))
return -1;
return 0;
}
int ftdi_usb_close(struct ftdi_context *ftdi)
{
_ASSERTE(ftdi->handle != NULL);
FT_STATUS status = FTd_Close(ftdi);
if (!FT_SUCCESS(status))
return -1;
ftdi->handle = ftdi->usb_dev = NULL;
return 0;
}
int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
{
DWORD transferred;
FT_STATUS status = FTd_Read(ftdi, buf, size, &transferred);
if (!FT_SUCCESS(status))
return -1;
return (int)transferred;
}
int ftdi_write_data(struct ftdi_context *ftdi, const unsigned char *buf, int size)
{
DWORD transferred;
FT_STATUS status = FTd_Write(ftdi, (void *)buf, size, &transferred);
if (!FT_SUCCESS(status))
return -1;
return (int)transferred;
}
int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode)
{
FT_STATUS status = FTd_SetBitMode(ftdi, bitmask, mode);
if (!FT_SUCCESS(status))
return -1;
return 0;
}
int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate)
{
FT_STATUS status = FTd_SetBaudRate(ftdi, baudrate);
if (!FT_SUCCESS(status))
return -1;
return 0;
}
int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency)
{
FT_STATUS status = FTd_SetLatencyTimer(ftdi, latency);
if (!FT_SUCCESS(status))
return -1;
return 0;
}
const char *ftdi_get_error_string(struct ftdi_context *ftdi)
{
if (ftdi == NULL)
return NULL;
return ftdi->last_error;
}