aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c')
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c710
1 files changed, 710 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c
new file mode 100644
index 000000000..1b06405be
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c
@@ -0,0 +1,710 @@
1/*
2 ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
3 Copyright (C) 2015..2019 Diego Ismirlian, (dismirlian(at)google's mail)
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16*/
17
18#include "hal.h"
19
20#if HAL_USBH_USE_FTDI
21
22#if !HAL_USE_USBH
23#error "USBHFTDI needs USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/ftdi.h"
28#include "usbh/internal.h"
29
30#define _USBH_DEBUG_HELPER_CLASS_DRIVER ftdipp->ftdip
31#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHFTDI_DEBUG_ENABLE_TRACE
32#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHFTDI_DEBUG_ENABLE_INFO
33#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHFTDI_DEBUG_ENABLE_WARNINGS
34#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHFTDI_DEBUG_ENABLE_ERRORS
35#include "usbh/debug_helpers.h"
36
37
38static void _ftdip_object_init(USBHFTDIPortDriver *ftdipp);
39
40/*===========================================================================*/
41/* USB Class driver loader for FTDI */
42/*===========================================================================*/
43USBHFTDIDriver USBHFTDID[HAL_USBHFTDI_MAX_INSTANCES];
44
45static void _ftdi_init(void);
46static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
47static void _ftdi_unload(usbh_baseclassdriver_t *drv);
48
49static const usbh_classdriver_vmt_t class_driver_vmt = {
50 _ftdi_init,
51 _ftdi_load,
52 _ftdi_unload
53};
54
55const usbh_classdriverinfo_t usbhftdiClassDriverInfo = {
56 "FTDI", &class_driver_vmt
57};
58
59static USBHFTDIPortDriver *_find_port(void) {
60 uint8_t i;
61 for (i = 0; i < HAL_USBHFTDI_MAX_PORTS; i++) {
62 if (FTDIPD[i].ftdip == NULL)
63 return &FTDIPD[i];
64 }
65 return NULL;
66}
67
68static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
69 int i;
70 USBHFTDIDriver *ftdip;
71
72 if (_usbh_match_vid_pid(dev, 0x0403, -1) != HAL_SUCCESS)
73 return NULL;
74
75 switch (dev->devDesc.idProduct) {
76 case 0x6001:
77 case 0x6010:
78 case 0x6011:
79 case 0x6014:
80 case 0x6015:
81 case 0xE2E6:
82 break;
83 default:
84 udeverr("FTDI: Unrecognized PID");
85 return NULL;
86 }
87
88 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
89 0xff, 0xff, 0xff) != HAL_SUCCESS)
90 return NULL;
91
92 if (((const usbh_interface_descriptor_t *)descriptor)->bInterfaceNumber != 0) {
93 udevwarn("FTDI: Will allocate driver along with IF #0");
94 }
95
96 /* alloc driver */
97 for (i = 0; i < HAL_USBHFTDI_MAX_INSTANCES; i++) {
98 if (USBHFTDID[i].dev == NULL) {
99 ftdip = &USBHFTDID[i];
100 goto alloc_ok;
101 }
102 }
103
104 udevwarn("FTDI: Can't alloc driver");
105
106 /* can't alloc */
107 return NULL;
108
109alloc_ok:
110 /* initialize the driver's variables */
111 ftdip->ports = 0;
112 switch (dev->devDesc.bcdDevice) {
113 case 0x200: //AM
114 udevinfo("FTDI: Type A chip");
115 ftdip->type = USBHFTDI_TYPE_A;
116 break;
117 case 0x400: //BM
118 case 0x500: //2232C
119 case 0x600: //R
120 case 0x1000: //230X
121 udevinfo("FTDI: Type B chip");
122 ftdip->type = USBHFTDI_TYPE_B;
123 break;
124 case 0x700: //2232H;
125 case 0x800: //4232H;
126 case 0x900: //232H;
127 udevinfo("FTDI: Type H chip");
128 ftdip->type = USBHFTDI_TYPE_H;
129 break;
130 default:
131 udeverr("FTDI: Unrecognized chip type");
132 return NULL;
133 }
134 usbhEPSetName(&dev->ctrl, "FTD[CTRL]");
135
136 /* parse the configuration descriptor */
137 generic_iterator_t iep, icfg;
138 if_iterator_t iif;
139 cfg_iter_init(&icfg, dev->fullConfigurationDescriptor, dev->basicConfigDesc.wTotalLength);
140 for (if_iter_init(&iif, &icfg); iif.valid; if_iter_next(&iif)) {
141 const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
142 udevinfof("FTDI: Interface #%d", ifdesc->bInterfaceNumber);
143
144 USBHFTDIPortDriver *const prt = _find_port();
145 if (prt == NULL) {
146 udevwarn("\tCan't alloc port for this interface");
147 break;
148 }
149
150 prt->ifnum = ifdesc->bInterfaceNumber;
151 prt->epin.status = USBH_EPSTATUS_UNINITIALIZED;
152 prt->epout.status = USBH_EPSTATUS_UNINITIALIZED;
153
154 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
155 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
156 if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
157 udevinfof("BULK IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
158 usbhEPObjectInit(&prt->epin, dev, epdesc);
159 usbhEPSetName(&prt->epin, "FTD[BIN ]");
160 } else if (((epdesc->bEndpointAddress & 0x80) == 0)
161 && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
162 udevinfof("BULK OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
163 usbhEPObjectInit(&prt->epout, dev, epdesc);
164 usbhEPSetName(&prt->epout, "FTD[BOUT]");
165 } else {
166 udevinfof("unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
167 epdesc->bEndpointAddress, epdesc->bmAttributes);
168 }
169 }
170
171 if ((prt->epin.status != USBH_EPSTATUS_CLOSED)
172 || (prt->epout.status != USBH_EPSTATUS_CLOSED)) {
173 udevwarn("\tCouldn't find endpoints; can't alloc port for this interface");
174 continue;
175 }
176
177 /* link the new block driver to the list */
178 prt->next = ftdip->ports;
179 ftdip->ports = prt;
180 prt->ftdip = ftdip;
181
182 prt->state = USBHFTDIP_STATE_ACTIVE;
183 }
184
185 return (usbh_baseclassdriver_t *)ftdip;
186
187}
188
189static void _stopS(USBHFTDIPortDriver *ftdipp);
190static void _ftdi_unload(usbh_baseclassdriver_t *drv) {
191 osalDbgCheck(drv != NULL);
192 USBHFTDIDriver *const ftdip = (USBHFTDIDriver *)drv;
193 USBHFTDIPortDriver *ftdipp = ftdip->ports;
194
195 osalMutexLock(&ftdip->mtx);
196 while (ftdipp) {
197 osalSysLock();
198 _stopS(ftdipp);
199 osalSysUnlock();
200 ftdipp = ftdipp->next;
201 }
202
203 ftdipp = ftdip->ports;
204 osalSysLock();
205 while (ftdipp) {
206 USBHFTDIPortDriver *next = ftdipp->next;
207 _ftdip_object_init(ftdipp);
208 ftdipp = next;
209 }
210 osalSysUnlock();
211 osalMutexUnlock(&ftdip->mtx);
212}
213
214
215USBHFTDIPortDriver FTDIPD[HAL_USBHFTDI_MAX_PORTS];
216
217
218#define FTDI_COMMAND_RESET 0
219#define FTDI_RESET_ALL 0
220#define FTDI_RESET_PURGE_RX 1
221#define FTDI_RESET_PURGE_TX 2
222
223#define FTDI_COMMAND_SETFLOW 2
224
225#define FTDI_COMMAND_SETBAUD 3
226
227#define FTDI_COMMAND_SETDATA 4
228#define FTDI_SETDATA_BREAK (0x1 << 14)
229
230#if 0
231#define FTDI_COMMAND_MODEMCTRL 1
232#define FTDI_COMMAND_GETMODEMSTATUS 5 /* Retrieve current value of modem status register */
233#define FTDI_COMMAND_SETEVENTCHAR 6 /* Set the event character */
234#define FTDI_COMMAND_SETERRORCHAR 7 /* Set the error character */
235#define FTDI_COMMAND_SETLATENCYTIMER 9 /* Set the latency timer */
236#define FTDI_COMMAND_GETLATENCYTIMER 10 /* Get the latency timer */
237#endif
238
239/*
240 * DATA FORMAT
241 *
242 * IN Endpoint
243 *
244 * The device reserves the first two bytes of data on this endpoint to contain
245 * the current values of the modem and line status registers. In the absence of
246 * data, the device generates a message consisting of these two status bytes
247 * every 40 ms
248 *
249 * Byte 0: Modem Status
250 *
251 * Offset Description
252 * B0 Reserved - must be 1
253 * B1 Reserved - must be 0
254 * B2 Reserved - must be 0
255 * B3 Reserved - must be 0
256 * B4 Clear to Send (CTS)
257 * B5 Data Set Ready (DSR)
258 * B6 Ring Indicator (RI)
259 * B7 Receive Line Signal Detect (RLSD)
260 *
261 * Byte 1: Line Status
262 *
263 * Offset Description
264 * B0 Data Ready (DR)
265 * B1 Overrun Error (OE)
266 * B2 Parity Error (PE)
267 * B3 Framing Error (FE)
268 * B4 Break Interrupt (BI)
269 * B5 Transmitter Holding Register (THRE)
270 * B6 Transmitter Empty (TEMT)
271 * B7 Error in RCVR FIFO
272 *
273 */
274#define FTDI_RS0_CTS (1 << 4)
275#define FTDI_RS0_DSR (1 << 5)
276#define FTDI_RS0_RI (1 << 6)
277#define FTDI_RS0_RLSD (1 << 7)
278
279#define FTDI_RS_DR 1
280#define FTDI_RS_OE (1<<1)
281#define FTDI_RS_PE (1<<2)
282#define FTDI_RS_FE (1<<3)
283#define FTDI_RS_BI (1<<4)
284#define FTDI_RS_THRE (1<<5)
285#define FTDI_RS_TEMT (1<<6)
286#define FTDI_RS_FIFO (1<<7)
287
288
289static usbh_urbstatus_t _ftdi_port_control(USBHFTDIPortDriver *ftdipp,
290 uint8_t bRequest, uint8_t wValue, uint8_t bHIndex, uint16_t wLength,
291 uint8_t *buff) {
292
293 static const uint8_t bmRequestType[] = {
294 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //0 FTDI_COMMAND_RESET
295 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //1 FTDI_COMMAND_MODEMCTRL
296 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //2 FTDI_COMMAND_SETFLOW
297 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //3 FTDI_COMMAND_SETBAUD
298 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE, //4 FTDI_COMMAND_SETDATA
299 };
300
301 osalDbgCheck(bRequest < sizeof_array(bmRequestType));
302 osalDbgCheck(bRequest != 1);
303
304 USBH_DEFINE_BUFFER(const usbh_control_request_t req) = {
305 bmRequestType[bRequest],
306 bRequest,
307 wValue,
308 (bHIndex << 8) | (ftdipp->ifnum + 1),
309 wLength
310 };
311
312 return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, buff, NULL, OSAL_MS2I(1000));
313}
314
315static uint32_t _get_divisor(const USBHFTDIPortDriver *ftdipp, uint32_t baud) {
316 usbhftdi_type_t type = ftdipp->ftdip->type;
317 static const uint8_t divfrac[8] = {0, 3, 2, 4, 1, 5, 6, 7};
318 uint32_t divisor;
319
320 if (type == USBHFTDI_TYPE_A) {
321 uint32_t divisor3 = ((48000000UL / 2) + baud / 2) / baud;
322 uclassdrvinfof("FTDI: desired=%dbps, real=%dbps", baud, (48000000UL / 2) / divisor3);
323 if ((divisor3 & 0x7) == 7)
324 divisor3++; /* round x.7/8 up to x+1 */
325
326 divisor = divisor3 >> 3;
327 divisor3 &= 0x7;
328 if (divisor3 == 1)
329 divisor |= 0xc000;
330 else if (divisor3 >= 4)
331 divisor |= 0x4000;
332 else if (divisor3 != 0)
333 divisor |= 0x8000;
334 else if (divisor == 1)
335 divisor = 0; /* special case for maximum baud rate */
336 } else {
337 if (type == USBHFTDI_TYPE_B) {
338 divisor = ((48000000UL / 2) + baud / 2) / baud;
339 uclassdrvinfof("FTDI: desired=%dbps, real=%dbps", baud, (48000000UL / 2) / divisor);
340 } else {
341 /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
342 if (baud < 1200)
343 baud = 1200;
344 divisor = (120000000UL * 8 + baud * 5) / (baud * 10);
345 uclassdrvinfof("FTDI: desired=%dbps, real=%dbps", baud, (120000000UL * 8) / divisor / 10);
346 }
347 divisor = (divisor >> 3) | (divfrac[divisor & 0x7] << 14);
348
349 /* Deal with special cases for highest baud rates. */
350 if (divisor == 1)
351 divisor = 0;
352 else if (divisor == 0x4001)
353 divisor = 1;
354
355 if (type == USBHFTDI_TYPE_H)
356 divisor |= 0x00020000;
357 }
358 return divisor;
359}
360
361static usbh_urbstatus_t _set_baudrate(USBHFTDIPortDriver *ftdipp, uint32_t baudrate) {
362 uint32_t divisor = _get_divisor(ftdipp, baudrate);
363 uint16_t wValue = (uint16_t)divisor;
364 uint16_t wIndex = (uint16_t)(divisor >> 16);
365 if (ftdipp->ftdip->dev->basicConfigDesc.bNumInterfaces > 1)
366 wIndex = (wIndex << 8) | (ftdipp->ifnum + 1);
367
368 USBH_DEFINE_BUFFER(const usbh_control_request_t req) = {
369 USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_RECIP_DEVICE,
370 FTDI_COMMAND_SETBAUD,
371 wValue,
372 wIndex,
373 0
374 };
375 return usbhControlRequestExtended(ftdipp->ftdip->dev, &req, NULL, NULL, OSAL_MS2I(1000));
376}
377
378
379static void _submitOutI(USBHFTDIPortDriver *ftdipp, uint32_t len) {
380 uclassdrvdbgf("FTDI: Submit OUT %d", len);
381 ftdipp->oq_urb.requestedLength = len;
382 usbhURBObjectResetI(&ftdipp->oq_urb);
383 usbhURBSubmitI(&ftdipp->oq_urb);
384}
385
386static void _out_cb(usbh_urb_t *urb) {
387 USBHFTDIPortDriver *const ftdipp = (USBHFTDIPortDriver *)urb->userData;
388 switch (urb->status) {
389 case USBH_URBSTATUS_OK:
390 ftdipp->oq_ptr = ftdipp->oq_buff;
391 ftdipp->oq_counter = 64;
392 chThdDequeueNextI(&ftdipp->oq_waiting, Q_OK);
393 return;
394 case USBH_URBSTATUS_DISCONNECTED:
395 uurbwarn("FTDI: URB OUT disconnected");
396 chThdDequeueAllI(&ftdipp->oq_waiting, Q_RESET);
397 return;
398 default:
399 uurberrf("FTDI: URB OUT status unexpected = %d", urb->status);
400 break;
401 }
402 usbhURBObjectResetI(&ftdipp->oq_urb);
403 usbhURBSubmitI(&ftdipp->oq_urb);
404}
405
406static size_t _write_timeout(USBHFTDIPortDriver *ftdipp, const uint8_t *bp,
407 size_t n, systime_t timeout) {
408 chDbgCheck(n > 0U);
409
410 size_t w = 0;
411 osalSysLock();
412 while (true) {
413 if (ftdipp->state != USBHFTDIP_STATE_READY) {
414 osalSysUnlock();
415 return w;
416 }
417 while (usbhURBIsBusy(&ftdipp->oq_urb)) {
418 if (chThdEnqueueTimeoutS(&ftdipp->oq_waiting, timeout) != Q_OK) {
419 osalSysUnlock();
420 return w;
421 }
422 }
423
424 *ftdipp->oq_ptr++ = *bp++;
425 if (--ftdipp->oq_counter == 0) {
426 _submitOutI(ftdipp, 64);
427 osalOsRescheduleS();
428 }
429 osalSysUnlock(); /* Gives a preemption chance in a controlled point.*/
430
431 w++;
432 if (--n == 0U)
433 return w;
434
435 osalSysLock();
436 }
437}
438
439static msg_t _put_timeout(USBHFTDIPortDriver *ftdipp, uint8_t b, systime_t timeout) {
440
441 osalSysLock();
442 if (ftdipp->state != USBHFTDIP_STATE_READY) {
443 osalSysUnlock();
444 return Q_RESET;
445 }
446
447 while (usbhURBIsBusy(&ftdipp->oq_urb)) {
448 msg_t msg = chThdEnqueueTimeoutS(&ftdipp->oq_waiting, timeout);
449 if (msg < Q_OK) {
450 osalSysUnlock();
451 return msg;
452 }
453 }
454
455 *ftdipp->oq_ptr++ = b;
456 if (--ftdipp->oq_counter == 0) {
457 _submitOutI(ftdipp, 64);
458 osalOsRescheduleS();
459 }
460 osalSysUnlock();
461 return Q_OK;
462}
463
464static size_t _write(USBHFTDIPortDriver *ftdipp, const uint8_t *bp, size_t n) {
465 return _write_timeout(ftdipp, bp, n, TIME_INFINITE);
466}
467
468static msg_t _put(USBHFTDIPortDriver *ftdipp, uint8_t b) {
469 return _put_timeout(ftdipp, b, TIME_INFINITE);
470}
471
472static void _submitInI(USBHFTDIPortDriver *ftdipp) {
473 uclassdrvdbg("FTDI: Submit IN");
474 usbhURBObjectResetI(&ftdipp->iq_urb);
475 usbhURBSubmitI(&ftdipp->iq_urb);
476}
477
478static void _in_cb(usbh_urb_t *urb) {
479 USBHFTDIPortDriver *const ftdipp = (USBHFTDIPortDriver *)urb->userData;
480 switch (urb->status) {
481 case USBH_URBSTATUS_OK:
482 if (urb->actualLength < 2) {
483 uurbwarnf("FTDI: URB IN actualLength = %d, < 2", urb->actualLength);
484 } else if (urb->actualLength > 2) {
485 uurbdbgf("FTDI: URB IN data len=%d, status=%02x %02x",
486 urb->actualLength - 2,
487 ((uint8_t *)urb->buff)[0],
488 ((uint8_t *)urb->buff)[1]);
489 ftdipp->iq_ptr = ftdipp->iq_buff + 2;
490 ftdipp->iq_counter = urb->actualLength - 2;
491 chThdDequeueNextI(&ftdipp->iq_waiting, Q_OK);
492 return;
493 } else {
494 uurbdbgf("FTDI: URB IN no data, status=%02x %02x",
495 ((uint8_t *)urb->buff)[0],
496 ((uint8_t *)urb->buff)[1]);
497 // return;
498 }
499 break;
500 case USBH_URBSTATUS_DISCONNECTED:
501 uurbwarn("FTDI: URB IN disconnected");
502 chThdDequeueAllI(&ftdipp->iq_waiting, Q_RESET);
503 return;
504 default:
505 uurberrf("FTDI: URB IN status unexpected = %d", urb->status);
506 break;
507 }
508 _submitInI(ftdipp);
509}
510
511static size_t _read_timeout(USBHFTDIPortDriver *ftdipp, uint8_t *bp,
512 size_t n, systime_t timeout) {
513 size_t r = 0;
514
515 chDbgCheck(n > 0U);
516
517 osalSysLock();
518 while (true) {
519 if (ftdipp->state != USBHFTDIP_STATE_READY) {
520 osalSysUnlock();
521 return r;
522 }
523 while (ftdipp->iq_counter == 0) {
524 if (!usbhURBIsBusy(&ftdipp->iq_urb))
525 _submitInI(ftdipp);
526 if (chThdEnqueueTimeoutS(&ftdipp->iq_waiting, timeout) != Q_OK) {
527 osalSysUnlock();
528 return r;
529 }
530 }
531 *bp++ = *ftdipp->iq_ptr++;
532 if (--ftdipp->iq_counter == 0) {
533 _submitInI(ftdipp);
534 osalOsRescheduleS();
535 }
536 osalSysUnlock();
537
538 r++;
539 if (--n == 0U)
540 return r;
541
542 osalSysLock();
543 }
544}
545
546static msg_t _get_timeout(USBHFTDIPortDriver *ftdipp, systime_t timeout) {
547 uint8_t b;
548
549 osalSysLock();
550 if (ftdipp->state != USBHFTDIP_STATE_READY) {
551 osalSysUnlock();
552 return Q_RESET;
553 }
554 while (ftdipp->iq_counter == 0) {
555 if (!usbhURBIsBusy(&ftdipp->iq_urb))
556 _submitInI(ftdipp);
557 msg_t msg = chThdEnqueueTimeoutS(&ftdipp->iq_waiting, timeout);
558 if (msg < Q_OK) {
559 osalSysUnlock();
560 return msg;
561 }
562 }
563 b = *ftdipp->iq_ptr++;
564 if (--ftdipp->iq_counter == 0) {
565 _submitInI(ftdipp);
566 osalOsRescheduleS();
567 }
568 osalSysUnlock();
569
570 return (msg_t)b;
571}
572
573static msg_t _get(USBHFTDIPortDriver *ftdipp) {
574 return _get_timeout(ftdipp, TIME_INFINITE);
575}
576
577static size_t _read(USBHFTDIPortDriver *ftdipp, uint8_t *bp, size_t n) {
578 return _read_timeout(ftdipp, bp, n, TIME_INFINITE);
579}
580
581static msg_t _ctl(USBHFTDIPortDriver *ftdipp, unsigned int operation, void *arg) {
582 (void)ftdipp;
583 (void)operation;
584 (void)arg;
585 return MSG_OK;
586}
587
588static void _vt(void *p) {
589 USBHFTDIPortDriver *const ftdipp = (USBHFTDIPortDriver *)p;
590 osalSysLockFromISR();
591 uint32_t len = ftdipp->oq_ptr - ftdipp->oq_buff;
592 if (len && !usbhURBIsBusy(&ftdipp->oq_urb)) {
593 _submitOutI(ftdipp, len);
594 }
595 if ((ftdipp->iq_counter == 0) && !usbhURBIsBusy(&ftdipp->iq_urb)) {
596 _submitInI(ftdipp);
597 }
598 chVTSetI(&ftdipp->vt, OSAL_MS2I(16), _vt, ftdipp);
599 osalSysUnlockFromISR();
600}
601
602static const struct FTDIPortDriverVMT async_channel_vmt = {
603 (size_t)0,
604 (size_t (*)(void *, const uint8_t *, size_t))_write,
605 (size_t (*)(void *, uint8_t *, size_t))_read,
606 (msg_t (*)(void *, uint8_t))_put,
607 (msg_t (*)(void *))_get,
608 (msg_t (*)(void *, uint8_t, systime_t))_put_timeout,
609 (msg_t (*)(void *, systime_t))_get_timeout,
610 (size_t (*)(void *, const uint8_t *, size_t, systime_t))_write_timeout,
611 (size_t (*)(void *, uint8_t *, size_t, systime_t))_read_timeout,
612 (msg_t (*)(void *, unsigned int, void *))_ctl
613};
614
615
616static void _stopS(USBHFTDIPortDriver *ftdipp) {
617 if (ftdipp->state != USBHFTDIP_STATE_READY)
618 return;
619 chVTResetI(&ftdipp->vt);
620 usbhEPCloseS(&ftdipp->epin);
621 usbhEPCloseS(&ftdipp->epout);
622 chThdDequeueAllI(&ftdipp->iq_waiting, Q_RESET);
623 chThdDequeueAllI(&ftdipp->oq_waiting, Q_RESET);
624 ftdipp->state = USBHFTDIP_STATE_ACTIVE;
625 osalOsRescheduleS();
626}
627
628void usbhftdipStop(USBHFTDIPortDriver *ftdipp) {
629 osalDbgCheck((ftdipp->state == USBHFTDIP_STATE_ACTIVE)
630 || (ftdipp->state == USBHFTDIP_STATE_READY));
631
632 osalSysLock();
633 chMtxLockS(&ftdipp->ftdip->mtx);
634 _stopS(ftdipp);
635 chMtxUnlockS(&ftdipp->ftdip->mtx);
636 osalSysUnlock();
637}
638
639void usbhftdipStart(USBHFTDIPortDriver *ftdipp, const USBHFTDIPortConfig *config) {
640 static const USBHFTDIPortConfig default_config = {
641 HAL_USBHFTDI_DEFAULT_SPEED,
642 HAL_USBHFTDI_DEFAULT_FRAMING,
643 HAL_USBHFTDI_DEFAULT_HANDSHAKE,
644 HAL_USBHFTDI_DEFAULT_XON,
645 HAL_USBHFTDI_DEFAULT_XOFF
646 };
647
648 osalDbgCheck((ftdipp->state == USBHFTDIP_STATE_ACTIVE)
649 || (ftdipp->state == USBHFTDIP_STATE_READY));
650
651 if (ftdipp->state == USBHFTDIP_STATE_READY)
652 return;
653
654 osalMutexLock(&ftdipp->ftdip->mtx);
655 if (config == NULL)
656 config = &default_config;
657
658 uint16_t wValue = 0;
659 _ftdi_port_control(ftdipp, FTDI_COMMAND_RESET, FTDI_RESET_ALL, 0, 0, NULL);
660 _set_baudrate(ftdipp, config->speed);
661 _ftdi_port_control(ftdipp, FTDI_COMMAND_SETDATA, config->framing, 0, 0, NULL);
662 if (config->handshake & USBHFTDI_HANDSHAKE_XON_XOFF)
663 wValue = (config->xoff_character << 8) | config->xon_character;
664 _ftdi_port_control(ftdipp, FTDI_COMMAND_SETFLOW, wValue, config->handshake, 0, NULL);
665
666 usbhURBObjectInit(&ftdipp->oq_urb, &ftdipp->epout, _out_cb, ftdipp, ftdipp->oq_buff, 0);
667 chThdQueueObjectInit(&ftdipp->oq_waiting);
668 ftdipp->oq_counter = 64;
669 ftdipp->oq_ptr = ftdipp->oq_buff;
670 usbhEPOpen(&ftdipp->epout);
671
672 usbhURBObjectInit(&ftdipp->iq_urb, &ftdipp->epin, _in_cb, ftdipp, ftdipp->iq_buff, 64);
673 chThdQueueObjectInit(&ftdipp->iq_waiting);
674 ftdipp->iq_counter = 0;
675 ftdipp->iq_ptr = ftdipp->iq_buff;
676 usbhEPOpen(&ftdipp->epin);
677 usbhURBSubmit(&ftdipp->iq_urb);
678
679 chVTObjectInit(&ftdipp->vt);
680 chVTSet(&ftdipp->vt, OSAL_MS2I(16), _vt, ftdipp);
681
682 ftdipp->state = USBHFTDIP_STATE_READY;
683 osalMutexUnlock(&ftdipp->ftdip->mtx);
684}
685
686static void _ftdi_object_init(USBHFTDIDriver *ftdip) {
687 osalDbgCheck(ftdip != NULL);
688 memset(ftdip, 0, sizeof(*ftdip));
689 ftdip->info = &usbhftdiClassDriverInfo;
690 osalMutexObjectInit(&ftdip->mtx);
691}
692
693static void _ftdip_object_init(USBHFTDIPortDriver *ftdipp) {
694 osalDbgCheck(ftdipp != NULL);
695 memset(ftdipp, 0, sizeof(*ftdipp));
696 ftdipp->vmt = &async_channel_vmt;
697 ftdipp->state = USBHFTDIP_STATE_STOP;
698}
699
700static void _ftdi_init(void) {
701 uint8_t i;
702 for (i = 0; i < HAL_USBHFTDI_MAX_INSTANCES; i++) {
703 _ftdi_object_init(&USBHFTDID[i]);
704 }
705 for (i = 0; i < HAL_USBHFTDI_MAX_PORTS; i++) {
706 _ftdip_object_init(&FTDIPD[i]);
707 }
708}
709
710#endif