diff options
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.c | 710 |
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 | |||
38 | static void _ftdip_object_init(USBHFTDIPortDriver *ftdipp); | ||
39 | |||
40 | /*===========================================================================*/ | ||
41 | /* USB Class driver loader for FTDI */ | ||
42 | /*===========================================================================*/ | ||
43 | USBHFTDIDriver USBHFTDID[HAL_USBHFTDI_MAX_INSTANCES]; | ||
44 | |||
45 | static void _ftdi_init(void); | ||
46 | static usbh_baseclassdriver_t *_ftdi_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem); | ||
47 | static void _ftdi_unload(usbh_baseclassdriver_t *drv); | ||
48 | |||
49 | static const usbh_classdriver_vmt_t class_driver_vmt = { | ||
50 | _ftdi_init, | ||
51 | _ftdi_load, | ||
52 | _ftdi_unload | ||
53 | }; | ||
54 | |||
55 | const usbh_classdriverinfo_t usbhftdiClassDriverInfo = { | ||
56 | "FTDI", &class_driver_vmt | ||
57 | }; | ||
58 | |||
59 | static 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 | |||
68 | static 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 | |||
109 | alloc_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 | |||
189 | static void _stopS(USBHFTDIPortDriver *ftdipp); | ||
190 | static 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 | |||
215 | USBHFTDIPortDriver 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 | |||
289 | static 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 | |||
315 | static 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 | |||
361 | static 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 | |||
379 | static 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 | |||
386 | static 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 | |||
406 | static 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 | |||
439 | static 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 | |||
464 | static size_t _write(USBHFTDIPortDriver *ftdipp, const uint8_t *bp, size_t n) { | ||
465 | return _write_timeout(ftdipp, bp, n, TIME_INFINITE); | ||
466 | } | ||
467 | |||
468 | static msg_t _put(USBHFTDIPortDriver *ftdipp, uint8_t b) { | ||
469 | return _put_timeout(ftdipp, b, TIME_INFINITE); | ||
470 | } | ||
471 | |||
472 | static void _submitInI(USBHFTDIPortDriver *ftdipp) { | ||
473 | uclassdrvdbg("FTDI: Submit IN"); | ||
474 | usbhURBObjectResetI(&ftdipp->iq_urb); | ||
475 | usbhURBSubmitI(&ftdipp->iq_urb); | ||
476 | } | ||
477 | |||
478 | static 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 | |||
511 | static 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 | |||
546 | static 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 | |||
573 | static msg_t _get(USBHFTDIPortDriver *ftdipp) { | ||
574 | return _get_timeout(ftdipp, TIME_INFINITE); | ||
575 | } | ||
576 | |||
577 | static size_t _read(USBHFTDIPortDriver *ftdipp, uint8_t *bp, size_t n) { | ||
578 | return _read_timeout(ftdipp, bp, n, TIME_INFINITE); | ||
579 | } | ||
580 | |||
581 | static 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 | |||
588 | static 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 | |||
602 | static 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 | |||
616 | static 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 | |||
628 | void 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 | |||
639 | void 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 | |||
686 | static 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 | |||
693 | static 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 | |||
700 | static 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 | ||