aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/src/usbh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/usbh')
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/TODO.txt21
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_aoa.c666
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_debug.c219
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_desciter.c161
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_ftdi.c710
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c311
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hub.c279
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_msd.c952
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_uvc.c718
9 files changed, 4037 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/usbh/TODO.txt b/lib/chibios-contrib/os/hal/src/usbh/TODO.txt
new file mode 100644
index 000000000..87269bee1
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/TODO.txt
@@ -0,0 +1,21 @@
1In decreasing order of priority:
2
3Bugs:
4- Synchronization on driver unload between usbhMainLoop and driver APIs
5 - MSD: ok
6 - AOA: not done
7 - HUB: ok
8 - FTDI: not done
9 - HID: ok
10 - UVC: not done
11
12
13Enhancements:
14- Way to return error from the load() functions in order to stop the enumeration process
15- Event sources from the low-level driver, in order to know when to call usbhMainLoop (from the low-level driver and from the HUB driver status callback)
16- Possibility of internal main loop
17- Linked list for drivers for dynamic registration
18- A way to automate matching (similar to linux)
19- Hooks to override driver loading and to inform the user of problems
20- for STM32 LLD: think of a way to prevent Bulk IN NAK interrupt flood.
21- Integrate VBUS power switching functionality to the API.
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_aoa.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_aoa.c
new file mode 100644
index 000000000..77ac4e733
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_aoa.c
@@ -0,0 +1,666 @@
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_AOA
21
22#if !HAL_USE_USBH
23#error "USBHAOA needs USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/aoa.h"
28#include "usbh/internal.h"
29
30//#pragma GCC optimize("Og")
31
32#define _USBH_DEBUG_HELPER_CLASS_DRIVER container_of(aoacp, USBHAOADriver, channel)
33#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHAOA_DEBUG_ENABLE_TRACE
34#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHAOA_DEBUG_ENABLE_INFO
35#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHAOA_DEBUG_ENABLE_WARNINGS
36#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHAOA_DEBUG_ENABLE_ERRORS
37#include "usbh/debug_helpers.h"
38
39/*===========================================================================*/
40/* Constants */
41/*===========================================================================*/
42
43#if !defined(HAL_USBHAOA_DEFAULT_MANUFACTURER)
44#define HAL_USBHAOA_DEFAULT_MANUFACTURER "ChibiOS"
45#endif
46
47#if !defined(HAL_USBHAOA_DEFAULT_MODEL)
48#define HAL_USBHAOA_DEFAULT_MODEL "USBH AOA Driver"
49#endif
50
51#if !defined(HAL_USBHAOA_DEFAULT_DESCRIPTION)
52#define HAL_USBHAOA_DEFAULT_DESCRIPTION "ChibiOS USBH AOA Driver"
53#endif
54
55#if !defined(HAL_USBHAOA_DEFAULT_VERSION)
56#define HAL_USBHAOA_DEFAULT_VERSION CH_KERNEL_VERSION
57#endif
58
59#if !defined(HAL_USBHAOA_DEFAULT_URI)
60#define HAL_USBHAOA_DEFAULT_URI NULL
61#endif
62
63#if !defined(HAL_USBHAOA_DEFAULT_SERIAL)
64#define HAL_USBHAOA_DEFAULT_SERIAL NULL
65#endif
66
67#if !defined(HAL_USBHAOA_DEFAULT_AUDIO_MODE)
68#define HAL_USBHAOA_DEFAULT_AUDIO_MODE USBHAOA_AUDIO_MODE_DISABLED
69#endif
70
71#define AOA_GOOGLE_VID 0x18D1
72#define AOA_GOOGLE_PID_ACCESSORY 0x2D00
73#define AOA_GOOGLE_PID_ACCESSORY_ABD 0x2D01
74#define AOA_GOOGLE_PID_AUDIO 0x2D02
75#define AOA_GOOGLE_PID_AUDIO_ABD 0x2D03
76#define AOA_GOOGLE_PID_ACCESSORY_AUDIO 0x2D04
77#define AOA_GOOGLE_PID_ACCESSORY_AUDIO_ABD 0x2D05
78
79#define AOA_ACCESSORY_GET_PROTOCOL 51
80#define AOA_ACCESSORY_SEND_STRING 52
81#define AOA_ACCESSORY_START 53
82
83#define AOA_SET_AUDIO_MODE 58
84
85static bool _get_protocol(usbh_device_t *dev, uint16_t *protocol);
86static bool _accessory_start(usbh_device_t *dev);
87static bool _set_audio_mode(usbh_device_t *dev, uint16_t mode);
88static bool _send_string(usbh_device_t *dev, uint8_t index, const char *string);
89
90
91static void _stop_channelS(USBHAOAChannel *aoacp);
92
93/*===========================================================================*/
94/* USB Class driver loader for AOA */
95/*===========================================================================*/
96USBHAOADriver USBHAOAD[HAL_USBHAOA_MAX_INSTANCES];
97
98static usbh_baseclassdriver_t *_aoa_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
99static void _aoa_unload(usbh_baseclassdriver_t *drv);
100static void _aoa_init(void);
101
102static const usbh_classdriver_vmt_t class_driver_vmt = {
103 _aoa_init,
104 _aoa_load,
105 _aoa_unload
106};
107
108const usbh_classdriverinfo_t usbhaoaClassDriverInfo = {
109 "AOA", &class_driver_vmt
110};
111
112#if defined(HAL_USBHAOA_FILTER_CALLBACK)
113extern usbhaoa_filter_callback_t HAL_USBHAOA_FILTER_CALLBACK;
114#endif
115
116static usbh_baseclassdriver_t *_aoa_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
117 int i;
118 USBHAOADriver *aoap;
119
120 if (dev->devDesc.idVendor != AOA_GOOGLE_VID) {
121 uint16_t protocol;
122 static USBHAOAConfig config = {
123 {
124 HAL_USBHAOA_DEFAULT_MANUFACTURER,
125 HAL_USBHAOA_DEFAULT_MODEL,
126 HAL_USBHAOA_DEFAULT_DESCRIPTION,
127 HAL_USBHAOA_DEFAULT_VERSION,
128 HAL_USBHAOA_DEFAULT_URI,
129 HAL_USBHAOA_DEFAULT_SERIAL
130 },
131
132 {
133 HAL_USBHAOA_DEFAULT_AUDIO_MODE,
134 }
135 };
136
137 if (descriptor[1] != USBH_DT_DEVICE) {
138 udevinfo("AOA: Won't try to detect Android device at interface level");
139 return NULL;
140 }
141
142 udevinfo("AOA: Unrecognized VID");
143
144#if defined(HAL_USBHAOA_FILTER_CALLBACK)
145 if (!HAL_USBHAOA_FILTER_CALLBACK(dev, descriptor, rem, &config)) {
146 return NULL;
147 }
148#endif
149
150 udevinfo("AOA: Try if it's an Android device");
151 if (_get_protocol(dev, &protocol) != HAL_SUCCESS) {
152 udevinfo("AOA: not an Android device");
153 return NULL;
154 }
155 udevinfof("AOA: Possible Android device found (protocol=%d)", protocol);
156
157 if (config.channel.manufacturer != NULL) {
158 if ((_send_string(dev, USBHAOA_ACCESSORY_STRING_MANUFACTURER, config.channel.manufacturer) != HAL_SUCCESS)
159 || (_send_string(dev, USBHAOA_ACCESSORY_STRING_MODEL, config.channel.model) != HAL_SUCCESS)
160 || (_send_string(dev, USBHAOA_ACCESSORY_STRING_DESCRIPTION, config.channel.description) != HAL_SUCCESS)
161 || (_send_string(dev, USBHAOA_ACCESSORY_STRING_VERSION, config.channel.version) != HAL_SUCCESS)
162 || (_send_string(dev, USBHAOA_ACCESSORY_STRING_URI, config.channel.uri) != HAL_SUCCESS)
163 || (_send_string(dev, USBHAOA_ACCESSORY_STRING_SERIAL, config.channel.serial) != HAL_SUCCESS)) {
164 udeverr("AOA: Can't send string; abort start");
165 return NULL;
166 }
167 }
168
169 if (protocol > 1) {
170 if (_set_audio_mode(dev, (uint16_t)(config.audio.mode)) != HAL_SUCCESS) {
171 udeverr("AOA: Can't set audio mode; abort channel start");
172 return NULL;
173 }
174 }
175
176 if (_accessory_start(dev) != HAL_SUCCESS) {
177 udeverr("AOA: Can't start accessory; abort channel start");
178 } else {
179 udevinfo("AOA: Accessory started");
180 }
181
182 return NULL;
183 }
184
185 /* AOAv2:
186 0x2D00 accessory Provides two bulk endpoints for communicating with an Android application.
187 0x2D01 accessory + adb For debugging purposes during accessory development. Available only if the user has enabled USB Debugging in the Android device settings.
188 0x2D02 audio For streaming audio from an Android device to an accessory.
189 0x2D03 audio + adb
190 0x2D04 accessory + audio
191 0x2D05 accessory + audio + adb
192 */
193
194 switch (dev->devDesc.idProduct) {
195 case AOA_GOOGLE_PID_ACCESSORY:
196 case AOA_GOOGLE_PID_ACCESSORY_ABD:
197// case AOA_GOOGLE_PID_AUDIO:
198// case AOA_GOOGLE_PID_AUDIO_ABD:
199 case AOA_GOOGLE_PID_ACCESSORY_AUDIO:
200 case AOA_GOOGLE_PID_ACCESSORY_AUDIO_ABD:
201 break;
202 default:
203 udeverr("AOA: Unrecognized PID");
204 return NULL;
205 }
206
207 const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
208 if ((_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE, 0xFF, 0xFF, 0x00) != HAL_SUCCESS)
209 || (ifdesc->bNumEndpoints < 2)) {
210 udeverr("AOA: This IF is not the Accessory IF");
211 return NULL;
212 }
213
214 udevinfof("AOA: Found Accessory Interface #%d", ifdesc->bInterfaceNumber);
215
216 for (i = 0; i < HAL_USBHAOA_MAX_INSTANCES; i++) {
217 if (USBHAOAD[i].dev == NULL) {
218 aoap = &USBHAOAD[i];
219 goto alloc_ok;
220 }
221 }
222
223 udevwarn("AOA: Can't alloc driver");
224
225 /* can't alloc */
226 return NULL;
227
228alloc_ok:
229 /* initialize the driver's variables */
230 usbhEPSetName(&dev->ctrl, "AOA[CTRL]");
231 aoap->state = USBHAOA_STATE_ACTIVE;
232
233 generic_iterator_t iep;
234 if_iterator_t iif;
235 iif.iad = 0;
236 iif.curr = descriptor;
237 iif.rem = rem;
238
239 aoap->channel.epin.status = USBH_EPSTATUS_UNINITIALIZED;
240 aoap->channel.epout.status = USBH_EPSTATUS_UNINITIALIZED;
241
242 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
243 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
244 if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
245 udevinfof("AOA: BULK IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
246 usbhEPObjectInit(&aoap->channel.epin, dev, epdesc);
247 usbhEPSetName(&aoap->channel.epin, "AOA[BIN ]");
248 } else if (((epdesc->bEndpointAddress & 0x80) == 0)
249 && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
250 udevinfof("AOA: BULK OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
251 usbhEPObjectInit(&aoap->channel.epout, dev, epdesc);
252 usbhEPSetName(&aoap->channel.epout, "AOA[BOUT]");
253 } else {
254 udevinfof("AOA: unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
255 epdesc->bEndpointAddress, epdesc->bmAttributes);
256 }
257 }
258
259 if ((aoap->channel.epin.status != USBH_EPSTATUS_CLOSED)
260 || (aoap->channel.epout.status != USBH_EPSTATUS_CLOSED)) {
261 udevwarn("AOA: Couldn't find endpoints");
262 aoap->state = USBHAOA_STATE_STOP;
263 return NULL;
264 }
265
266 aoap->state = USBHAOA_STATE_READY;
267 aoap->channel.state = USBHAOA_CHANNEL_STATE_ACTIVE;
268 udevwarn("AOA: Ready");
269 return (usbh_baseclassdriver_t *)aoap;
270}
271
272static void _aoa_unload(usbh_baseclassdriver_t *drv) {
273 osalDbgCheck(drv != NULL);
274 USBHAOADriver *const aoap = (USBHAOADriver *)drv;
275 osalSysLock();
276 _stop_channelS(&aoap->channel);
277 aoap->channel.state = USBHAOA_CHANNEL_STATE_STOP;
278 aoap->state = USBHAOA_STATE_STOP;
279 osalSysUnlock();
280}
281
282/* ------------------------------------ */
283/* Accessory data channel */
284/* ------------------------------------ */
285
286static void _submitOutI(USBHAOAChannel *aoacp, uint32_t len) {
287 uclassdrvdbgf("AOA: Submit OUT %d", len);
288 aoacp->oq_urb.requestedLength = len;
289 usbhURBObjectResetI(&aoacp->oq_urb);
290 usbhURBSubmitI(&aoacp->oq_urb);
291}
292
293static void _out_cb(usbh_urb_t *urb) {
294 USBHAOAChannel *const aoacp = (USBHAOAChannel *)urb->userData;
295 switch (urb->status) {
296 case USBH_URBSTATUS_OK:
297 aoacp->oq_ptr = aoacp->oq_buff;
298 aoacp->oq_counter = 64;
299 chThdDequeueNextI(&aoacp->oq_waiting, Q_OK);
300 chnAddFlagsI(aoacp, CHN_OUTPUT_EMPTY | CHN_TRANSMISSION_END);
301 return;
302 case USBH_URBSTATUS_DISCONNECTED:
303 uclassdrvwarn("AOA: URB OUT disconnected");
304 chThdDequeueAllI(&aoacp->oq_waiting, Q_RESET);
305 chnAddFlagsI(aoacp, CHN_OUTPUT_EMPTY);
306 return;
307 default:
308 uclassdrverrf("AOA: URB OUT status unexpected = %d", urb->status);
309 break;
310 }
311 usbhURBObjectResetI(&aoacp->oq_urb);
312 usbhURBSubmitI(&aoacp->oq_urb);
313}
314
315static size_t _write_timeout(USBHAOAChannel *aoacp, const uint8_t *bp,
316 size_t n, systime_t timeout) {
317 chDbgCheck(n > 0U);
318
319 size_t w = 0;
320 osalSysLock();
321 while (true) {
322 if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
323 osalSysUnlock();
324 return w;
325 }
326 while (usbhURBIsBusy(&aoacp->oq_urb)) {
327 if (chThdEnqueueTimeoutS(&aoacp->oq_waiting, timeout) != Q_OK) {
328 osalSysUnlock();
329 return w;
330 }
331 }
332
333 *aoacp->oq_ptr++ = *bp++;
334 if (--aoacp->oq_counter == 0) {
335 _submitOutI(aoacp, 64);
336 osalOsRescheduleS();
337 }
338 osalSysUnlock(); /* Gives a preemption chance in a controlled point.*/
339
340 w++;
341 if (--n == 0U)
342 return w;
343
344 osalSysLock();
345 }
346}
347
348static msg_t _put_timeout(USBHAOAChannel *aoacp, uint8_t b, systime_t timeout) {
349
350 osalSysLock();
351 if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
352 osalSysUnlock();
353 return Q_RESET;
354 }
355
356 while (usbhURBIsBusy(&aoacp->oq_urb)) {
357 msg_t msg = chThdEnqueueTimeoutS(&aoacp->oq_waiting, timeout);
358 if (msg < Q_OK) {
359 osalSysUnlock();
360 return msg;
361 }
362 }
363
364 *aoacp->oq_ptr++ = b;
365 if (--aoacp->oq_counter == 0) {
366 _submitOutI(aoacp, 64);
367 osalOsRescheduleS();
368 }
369 osalSysUnlock();
370 return Q_OK;
371}
372
373static size_t _write(USBHAOAChannel *aoacp, const uint8_t *bp, size_t n) {
374 return _write_timeout(aoacp, bp, n, TIME_INFINITE);
375}
376
377static msg_t _put(USBHAOAChannel *aoacp, uint8_t b) {
378 return _put_timeout(aoacp, b, TIME_INFINITE);
379}
380
381static void _submitInI(USBHAOAChannel *aoacp) {
382 uclassdrvdbg("AOA: Submit IN");
383 usbhURBObjectResetI(&aoacp->iq_urb);
384 usbhURBSubmitI(&aoacp->iq_urb);
385}
386
387static void _in_cb(usbh_urb_t *urb) {
388 USBHAOAChannel *const aoacp = (USBHAOAChannel *)urb->userData;
389 switch (urb->status) {
390 case USBH_URBSTATUS_OK:
391 if (urb->actualLength == 0) {
392 uurbdbgf("AOA: URB IN no data");
393 } else {
394 uurbdbgf("AOA: URB IN data len=%d", urb->actualLength);
395 aoacp->iq_ptr = aoacp->iq_buff;
396 aoacp->iq_counter = urb->actualLength;
397 chThdDequeueNextI(&aoacp->iq_waiting, Q_OK);
398 chnAddFlagsI(aoacp, CHN_INPUT_AVAILABLE);
399 }
400 break;
401 case USBH_URBSTATUS_DISCONNECTED:
402 uurbwarn("AOA: URB IN disconnected");
403 chThdDequeueAllI(&aoacp->iq_waiting, Q_RESET);
404 chnAddFlagsI(aoacp, CHN_DISCONNECTED);
405 aoacp->state = USBHAOA_CHANNEL_STATE_ACTIVE;
406 container_of(aoacp, USBHAOADriver, channel)->state = USBHAOA_STATE_ACTIVE;
407 break;
408 default:
409 uurberrf("AOA: URB IN status unexpected = %d", urb->status);
410 _submitInI(aoacp);
411 break;
412 }
413}
414
415static size_t _read_timeout(USBHAOAChannel *aoacp, uint8_t *bp,
416 size_t n, systime_t timeout) {
417 size_t r = 0;
418
419 chDbgCheck(n > 0U);
420
421 osalSysLock();
422 while (true) {
423 if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
424 osalSysUnlock();
425 return r;
426 }
427 while (aoacp->iq_counter == 0) {
428 if (!usbhURBIsBusy(&aoacp->iq_urb))
429 _submitInI(aoacp);
430 if (chThdEnqueueTimeoutS(&aoacp->iq_waiting, timeout) != Q_OK) {
431 osalSysUnlock();
432 return r;
433 }
434 }
435 *bp++ = *aoacp->iq_ptr++;
436 if (--aoacp->iq_counter == 0) {
437 _submitInI(aoacp);
438 osalOsRescheduleS();
439 }
440 osalSysUnlock();
441
442 r++;
443 if (--n == 0U)
444 return r;
445
446 osalSysLock();
447 }
448}
449
450static msg_t _get_timeout(USBHAOAChannel *aoacp, systime_t timeout) {
451 uint8_t b;
452
453 osalSysLock();
454 if (aoacp->state != USBHAOA_CHANNEL_STATE_READY) {
455 osalSysUnlock();
456 return Q_RESET;
457 }
458 while (aoacp->iq_counter == 0) {
459 if (!usbhURBIsBusy(&aoacp->iq_urb))
460 _submitInI(aoacp);
461 msg_t msg = chThdEnqueueTimeoutS(&aoacp->iq_waiting, timeout);
462 if (msg < Q_OK) {
463 osalSysUnlock();
464 return msg;
465 }
466 }
467 b = *aoacp->iq_ptr++;
468 if (--aoacp->iq_counter == 0) {
469 _submitInI(aoacp);
470 osalOsRescheduleS();
471 }
472 osalSysUnlock();
473
474 return (msg_t)b;
475}
476
477static msg_t _get(USBHAOAChannel *aoacp) {
478 return _get_timeout(aoacp, TIME_INFINITE);
479}
480
481static size_t _read(USBHAOAChannel *aoacp, uint8_t *bp, size_t n) {
482 return _read_timeout(aoacp, bp, n, TIME_INFINITE);
483}
484
485static msg_t _ctl(USBHAOAChannel *ftdipp, unsigned int operation, void *arg) {
486 (void)ftdipp;
487 (void)operation;
488 (void)arg;
489 return MSG_OK;
490}
491
492static const struct AOADriverVMT async_channel_vmt = {
493 (size_t)0,
494 (size_t (*)(void *, const uint8_t *, size_t))_write,
495 (size_t (*)(void *, uint8_t *, size_t))_read,
496 (msg_t (*)(void *, uint8_t))_put,
497 (msg_t (*)(void *))_get,
498 (msg_t (*)(void *, uint8_t, systime_t))_put_timeout,
499 (msg_t (*)(void *, systime_t))_get_timeout,
500 (size_t (*)(void *, const uint8_t *, size_t, systime_t))_write_timeout,
501 (size_t (*)(void *, uint8_t *, size_t, systime_t))_read_timeout,
502 (msg_t (*)(void *, unsigned int, void *))_ctl
503};
504
505static void _stop_channelS(USBHAOAChannel *aoacp) {
506 if (aoacp->state != USBHAOA_CHANNEL_STATE_READY)
507 return;
508 uclassdrvwarn("AOA: Stop channel");
509 chVTResetI(&aoacp->vt);
510 usbhEPCloseS(&aoacp->epin);
511 usbhEPCloseS(&aoacp->epout);
512 chThdDequeueAllI(&aoacp->iq_waiting, Q_RESET);
513 chThdDequeueAllI(&aoacp->oq_waiting, Q_RESET);
514 chnAddFlagsI(aoacp, CHN_DISCONNECTED);
515 aoacp->state = USBHAOA_CHANNEL_STATE_ACTIVE;
516 osalOsRescheduleS();
517}
518
519static void _vt(void *p) {
520 USBHAOAChannel *const aoacp = (USBHAOAChannel *)p;
521 osalSysLockFromISR();
522 if (aoacp->state == USBHAOA_CHANNEL_STATE_READY) {
523 uint32_t len = aoacp->oq_ptr - aoacp->oq_buff;
524 if (len && !usbhURBIsBusy(&aoacp->oq_urb)) {
525 _submitOutI(aoacp, len);
526 }
527 if ((aoacp->iq_counter == 0) && !usbhURBIsBusy(&aoacp->iq_urb)) {
528 _submitInI(aoacp);
529 }
530 chVTSetI(&aoacp->vt, OSAL_MS2I(16), _vt, aoacp);
531 }
532 osalSysUnlockFromISR();
533}
534
535void usbhaoaChannelStart(USBHAOADriver *aoap) {
536
537 osalDbgCheck(aoap);
538
539 USBHAOAChannel *const aoacp = (USBHAOAChannel *)&aoap->channel;
540
541 osalDbgCheck(aoap->state == USBHAOA_STATE_READY);
542
543 osalDbgCheck((aoacp->state == USBHAOA_CHANNEL_STATE_ACTIVE)
544 || (aoacp->state == USBHAOA_CHANNEL_STATE_READY));
545
546 if (aoacp->state == USBHAOA_CHANNEL_STATE_READY)
547 return;
548
549 usbhURBObjectInit(&aoacp->oq_urb, &aoacp->epout, _out_cb, aoacp, aoacp->oq_buff, 0);
550 chThdQueueObjectInit(&aoacp->oq_waiting);
551 aoacp->oq_counter = 64;
552 aoacp->oq_ptr = aoacp->oq_buff;
553 usbhEPOpen(&aoacp->epout);
554
555 usbhURBObjectInit(&aoacp->iq_urb, &aoacp->epin, _in_cb, aoacp, aoacp->iq_buff, 64);
556 chThdQueueObjectInit(&aoacp->iq_waiting);
557 aoacp->iq_counter = 0;
558 aoacp->iq_ptr = aoacp->iq_buff;
559 usbhEPOpen(&aoacp->epin);
560 usbhURBSubmit(&aoacp->iq_urb);
561
562 chVTObjectInit(&aoacp->vt);
563 chVTSet(&aoacp->vt, OSAL_MS2I(16), _vt, aoacp);
564
565 aoacp->state = USBHAOA_CHANNEL_STATE_READY;
566
567 osalEventBroadcastFlags(&aoacp->event, CHN_CONNECTED | CHN_OUTPUT_EMPTY);
568}
569
570void usbhaoaChannelStop(USBHAOADriver *aoap) {
571 osalDbgCheck((aoap->channel.state == USBHAOA_CHANNEL_STATE_ACTIVE)
572 || (aoap->channel.state == USBHAOA_CHANNEL_STATE_READY));
573 osalSysLock();
574 _stop_channelS(&aoap->channel);
575 osalSysUnlock();
576}
577
578/* ------------------------------------ */
579/* General AOA functions */
580/* ------------------------------------ */
581static bool _get_protocol(usbh_device_t *dev, uint16_t *protocol) {
582 USBH_DEFINE_BUFFER(uint16_t proto);
583
584 usbh_urbstatus_t ret = usbhControlRequest(dev,
585 USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
586 AOA_ACCESSORY_GET_PROTOCOL,
587 0,
588 0,
589 2,
590 (uint8_t *)&proto);
591
592 if ((ret != USBH_URBSTATUS_OK) || (proto > 2))
593 return HAL_FAILED;
594
595 *protocol = proto;
596 return HAL_SUCCESS;
597}
598
599static bool _accessory_start(usbh_device_t *dev) {
600 usbh_urbstatus_t ret = usbhControlRequest(dev,
601 USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
602 AOA_ACCESSORY_START,
603 0,
604 0,
605 0,
606 NULL);
607
608 if (ret != USBH_URBSTATUS_OK)
609 return HAL_FAILED;
610
611 return HAL_SUCCESS;
612}
613
614static bool _set_audio_mode(usbh_device_t *dev, uint16_t mode) {
615 usbh_urbstatus_t ret = usbhControlRequest(dev,
616 USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
617 AOA_SET_AUDIO_MODE,
618 mode,
619 0,
620 0,
621 NULL);
622
623 if (ret != USBH_URBSTATUS_OK)
624 return HAL_FAILED;
625
626 return HAL_SUCCESS;
627}
628
629static bool _send_string(usbh_device_t *dev, uint8_t index, const char *string)
630{
631 USBH_DEFINE_BUFFER(const char nullstr[1]) = {0};
632 if (string == NULL)
633 string = nullstr;
634
635 usbh_urbstatus_t ret = usbhControlRequest(dev,
636 USBH_REQTYPE_DIR_OUT | USBH_REQTYPE_TYPE_VENDOR | USBH_REQTYPE_RECIP_DEVICE,
637 AOA_ACCESSORY_SEND_STRING,
638 0,
639 index,
640 strlen(string) + 1,
641 (uint8_t *)string);
642
643 if (ret != USBH_URBSTATUS_OK)
644 return HAL_FAILED;
645
646 return HAL_SUCCESS;
647}
648
649static void _object_init(USBHAOADriver *aoap) {
650 osalDbgCheck(aoap != NULL);
651 memset(aoap, 0, sizeof(*aoap));
652 aoap->info = &usbhaoaClassDriverInfo;
653 aoap->state = USBHAOA_STATE_STOP;
654 aoap->channel.vmt = &async_channel_vmt;
655 osalEventObjectInit(&aoap->channel.event);
656 aoap->channel.state = USBHAOA_CHANNEL_STATE_STOP;
657}
658
659static void _aoa_init(void) {
660 uint8_t i;
661 for (i = 0; i < HAL_USBHAOA_MAX_INSTANCES; i++) {
662 _object_init(&USBHAOAD[i]);
663 }
664}
665
666#endif
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_debug.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_debug.c
new file mode 100644
index 000000000..225489df8
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_debug.c
@@ -0,0 +1,219 @@
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_USE_USBH && USBH_DEBUG_ENABLE
21
22#include "ch.h"
23#include "usbh/debug.h"
24#include "chprintf.h"
25#include <stdarg.h>
26
27#define TEMP_BUFF_LEN 255
28
29/* ************************ */
30/* Circular queue structure */
31/* ************************ */
32static int dq_append_string(usbh_dq_t *q, const uint8_t *s, int len) {
33 if (len <= 0) return 0;
34 if (len > TEMP_BUFF_LEN) len = TEMP_BUFF_LEN;
35 if (q->rem < len + 1) return -1;
36 q->rem -= len + 1;
37
38 uint8_t *d = q->next;
39 *d++ = len;
40 if (d == q->end) d = q->start;
41 while (len--) {
42 *d++ = *s++;
43 if (d == q->end) d = q->start;
44 }
45 q->next = d;
46 return 0;
47}
48
49static void dq_remove_oldest_string(usbh_dq_t *q) {
50 int len = *q->first;
51 if (len) {
52 ++len;
53 q->rem += len;
54 q->first += len;
55 if (q->first >= q->end) {
56 q->first -= q->sz;
57 }
58 if (q->rem == q->sz) {
59 *q->first = 0;
60 }
61 }
62}
63
64static int dq_read_oldest_string(usbh_dq_t *q, uint8_t *d) {
65 uint8_t *s = q->first;
66 int len;
67 int sz;
68 len = sz = *s++;
69 while (len--) {
70 *d++ = *s++;
71 if (d == q->end) d = q->start;
72 }
73 *d = 0;
74 return sz;
75}
76
77static void dq_init(usbh_dq_t *q, uint8_t *buff, int len) {
78 q->start = q->first = q->next = buff;
79 q->end = q->start + len;
80 q->sz = q->rem = len;
81 *buff = 0;
82}
83
84
85static uint8_t buff[TEMP_BUFF_LEN + 1];
86
87static inline syssts_t _dbg_prologue(struct usbh_debug_helper *debug,
88 uint32_t hfnum, uint16_t hfir, const char *s, int *len) {
89 syssts_t sts = chSysGetStatusAndLockX();
90
91 debug->last = osalOsGetSystemTimeX();
92 if (debug->ena) {
93 debug->first = debug->last;
94 }
95
96 if (((hfnum & 0x3fff) == 0x3fff) && (hfir == (hfnum >> 16))) {
97 *len = chsnprintf((char *)buff, sizeof(buff), "+%08d ", debug->last - debug->first);
98 debug->ena = FALSE;
99 } else {
100 uint32_t f = hfnum & 0xffff;
101 uint32_t p = 1000 - ((hfnum >> 16) / (hfir / 1000));
102 *len = chsnprintf((char *)buff, sizeof(buff), "%05d.%03d %s", f, p, s);
103 debug->ena = TRUE;
104 }
105
106 return sts;
107}
108
109static inline void dbg_epilogue(struct usbh_debug_helper *debug,
110 syssts_t sts, int len) {
111
112 while (dq_append_string(&debug->dq, buff, len) < 0) {
113 dq_remove_oldest_string(&debug->dq);
114 }
115
116 if (debug->on) {
117 chThdResumeI(&debug->tr, MSG_OK);
118 }
119
120 chSysRestoreStatusX(sts);
121}
122
123#if USBH_DEBUG_MULTI_HOST
124void usbDbgPrintf(USBHDriver *host, const char *fmt, ...) {
125 if (!host) return;
126 struct usbh_debug_helper *const debug = &host->debug;
127 uint32_t hfnum = host->otg->HFNUM;
128 uint16_t hfir = host->otg->HFIR;
129#else
130void usbDbgPrintf(const char *fmt, ...) {
131 struct usbh_debug_helper *const debug = &usbh_debug;
132 uint32_t hfnum = USBH_DEBUG_SINGLE_HOST_SELECTION.otg->HFNUM;
133 uint16_t hfir = USBH_DEBUG_SINGLE_HOST_SELECTION.otg->HFIR;
134#endif
135 int len;
136
137 syssts_t sts = _dbg_prologue(debug, hfnum, hfir, "", &len);
138
139 va_list ap;
140 va_start(ap, fmt);
141 len += chvsnprintf((char *)buff + len, sizeof(buff) - len, fmt, ap);
142 va_end(ap);
143
144 dbg_epilogue(debug, sts, len);
145}
146
147#if USBH_DEBUG_MULTI_HOST
148void usbDbgPuts(USBHDriver *host, const char *s) {
149 if (!host) return;
150 struct usbh_debug_helper *const debug = &host->debug;
151 uint32_t hfnum = host->otg->HFNUM;
152 uint16_t hfir = host->otg->HFIR;
153#else
154void usbDbgPuts(const char *s) {
155 struct usbh_debug_helper *const debug = &usbh_debug;
156 uint32_t hfnum = USBH_DEBUG_SINGLE_HOST_SELECTION.otg->HFNUM;
157 uint16_t hfir = USBH_DEBUG_SINGLE_HOST_SELECTION.otg->HFIR;
158#endif
159 int len;
160 syssts_t sts = _dbg_prologue(debug, hfnum, hfir, s, &len);
161 dbg_epilogue(debug, sts, len);
162}
163
164#if USBH_DEBUG_MULTI_HOST
165void usbDbgEnable(USBHDriver *host, bool enable) {
166 struct usbh_debug_helper *const debug = &host->debug;
167#else
168void usbDbgEnable(bool enable) {
169 struct usbh_debug_helper *const debug = &usbh_debug;
170#endif
171 debug->on = enable;
172}
173
174static void usb_debug_thread(void *arg) {
175#if USBH_DEBUG_MULTI_HOST
176 USBHDriver *const host = (USBHDriver *)arg;
177 struct usbh_debug_helper *const debug = &host->debug;
178#else
179 (void)arg;
180 struct usbh_debug_helper *const debug = &usbh_debug;
181#endif
182
183 uint8_t rdbuff[TEMP_BUFF_LEN + 1];
184
185 chRegSetThreadName("USBH_DBG");
186 while (true) {
187 chSysLock();
188 int len = dq_read_oldest_string(&debug->dq, rdbuff);
189 if (!len) {
190 chThdSuspendS(&debug->tr);
191 chSysUnlock();
192 } else {
193 dq_remove_oldest_string(&debug->dq);
194 chSysUnlock();
195#if USBH_DEBUG_MULTI_HOST
196 USBH_DEBUG_OUTPUT_CALLBACK(host, rdbuff, len);
197#else
198 USBH_DEBUG_OUTPUT_CALLBACK(rdbuff, len);
199#endif
200 }
201 }
202}
203
204#if USBH_DEBUG_MULTI_HOST
205void usbDbgInit(USBHDriver *host) {
206 struct usbh_debug_helper *const debug = &host->debug;
207 void *param = host;
208#else
209void usbDbgInit(void) {
210 struct usbh_debug_helper *const debug = &usbh_debug;
211 void *param = NULL;
212#endif
213 dq_init(&debug->dq, debug->buff, sizeof(debug->buff));
214 debug->on = true;
215 chThdCreateStatic(debug->thd_wa, sizeof(debug->thd_wa),
216 NORMALPRIO, usb_debug_thread, param);
217}
218
219#endif
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_desciter.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_desciter.c
new file mode 100644
index 000000000..0ccf4e46b
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_desciter.c
@@ -0,0 +1,161 @@
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_USE_USBH
21
22#include "usbh/defs.h"
23#include "usbh/desciter.h"
24
25void cfg_iter_init(generic_iterator_t *icfg, const uint8_t *buff, uint16_t rem) {
26 icfg->valid = 0;
27
28 if ((rem < 2) || (buff[0] < 2) || (rem < buff[0])
29 || (buff[0] < USBH_DT_CONFIG_SIZE)
30 || (buff[1] != USBH_DT_CONFIG))
31 return;
32
33 if (rem > ((usbh_config_descriptor_t *)buff)->wTotalLength) {
34 rem = ((usbh_config_descriptor_t *)buff)->wTotalLength;
35 }
36
37 icfg->valid = 1;
38 icfg->rem = rem;
39 icfg->curr = buff;
40}
41
42void if_iter_next(if_iterator_t *iif) {
43 const uint8_t *curr = iif->curr;
44 uint16_t rem = iif->rem;
45
46 iif->valid = 0;
47
48 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
49 return;
50
51 for (;;) {
52 rem -= curr[0];
53 curr += curr[0];
54
55 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
56 return;
57
58 if (curr[1] == USBH_DT_INTERFACE_ASSOCIATION) {
59 if (curr[0] < USBH_DT_INTERFACE_ASSOCIATION_SIZE)
60 return;
61
62 iif->iad = (usbh_ia_descriptor_t *)curr;
63
64 } else if (curr[1] == USBH_DT_INTERFACE) {
65 if (curr[0] < USBH_DT_INTERFACE_SIZE)
66 return;
67
68 if (iif->iad) {
69 if ((curr[2] < iif->iad->bFirstInterface)
70 || (curr[2] >= (iif->iad->bFirstInterface + iif->iad->bInterfaceCount)))
71 iif->iad = 0;
72 }
73 break;
74 }
75 }
76
77 iif->valid = 1;
78 iif->rem = rem;
79 iif->curr = curr;
80}
81
82void if_iter_init(if_iterator_t *iif, const generic_iterator_t *icfg) {
83 iif->iad = 0;
84 iif->curr = icfg->curr;
85 iif->rem = icfg->rem;
86 if_iter_next(iif);
87}
88
89void ep_iter_next(generic_iterator_t *iep) {
90 const uint8_t *curr = iep->curr;
91 uint16_t rem = iep->rem;
92
93 iep->valid = 0;
94
95 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
96 return;
97
98 for (;;) {
99 rem -= curr[0];
100 curr += curr[0];
101
102 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
103 return;
104
105 if ((curr[1] == USBH_DT_INTERFACE_ASSOCIATION)
106 || (curr[1] == USBH_DT_INTERFACE)
107 || (curr[1] == USBH_DT_CONFIG)) {
108 return;
109 } else if (curr[1] == USBH_DT_ENDPOINT) {
110 if (curr[0] < USBH_DT_ENDPOINT_SIZE)
111 return;
112
113 break;
114 }
115 }
116
117 iep->valid = 1;
118 iep->rem = rem;
119 iep->curr = curr;
120}
121
122void ep_iter_init(generic_iterator_t *iep, const if_iterator_t *iif) {
123 iep->curr = iif->curr;
124 iep->rem = iif->rem;
125 ep_iter_next(iep);
126}
127
128void cs_iter_next(generic_iterator_t *ics) {
129 const uint8_t *curr = ics->curr;
130 uint16_t rem = ics->rem;
131
132 ics->valid = 0;
133
134 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
135 return;
136
137 rem -= curr[0];
138 curr += curr[0];
139
140 if ((rem < 2) || (curr[0] < 2) || (rem < curr[0]))
141 return;
142
143 if ((curr[1] == USBH_DT_INTERFACE_ASSOCIATION)
144 || (curr[1] == USBH_DT_INTERFACE)
145 || (curr[1] == USBH_DT_CONFIG)
146 || (curr[1] == USBH_DT_ENDPOINT)) {
147 return;
148 }
149
150 ics->valid = 1;
151 ics->rem = rem;
152 ics->curr = curr;
153}
154
155void cs_iter_init(generic_iterator_t *ics, const generic_iterator_t *iter) {
156 ics->curr = iter->curr;
157 ics->rem = iter->rem;
158 cs_iter_next(ics);
159}
160
161#endif
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
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c
new file mode 100644
index 000000000..f381b90ec
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c
@@ -0,0 +1,311 @@
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_HID
21
22#if !HAL_USE_USBH
23#error "USBHHID needs USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/hid.h"
28#include "usbh/internal.h"
29
30#define _USBH_DEBUG_HELPER_CLASS_DRIVER hidp
31#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHHID_DEBUG_ENABLE_TRACE
32#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHHID_DEBUG_ENABLE_INFO
33#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHHID_DEBUG_ENABLE_WARNINGS
34#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHHID_DEBUG_ENABLE_ERRORS
35#include "usbh/debug_helpers.h"
36
37#define USBH_HID_REQ_GET_REPORT 0x01
38#define USBH_HID_REQ_GET_IDLE 0x02
39#define USBH_HID_REQ_GET_PROTOCOL 0x03
40#define USBH_HID_REQ_SET_REPORT 0x09
41#define USBH_HID_REQ_SET_IDLE 0x0A
42#define USBH_HID_REQ_SET_PROTOCOL 0x0B
43
44/*===========================================================================*/
45/* USB Class driver loader for HID */
46/*===========================================================================*/
47
48USBHHIDDriver USBHHIDD[HAL_USBHHID_MAX_INSTANCES];
49
50static void _hid_init(void);
51static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
52static void _hid_unload(usbh_baseclassdriver_t *drv);
53static void _stop_locked(USBHHIDDriver *hidp);
54
55static const usbh_classdriver_vmt_t class_driver_vmt = {
56 _hid_init,
57 _hid_load,
58 _hid_unload
59};
60
61const usbh_classdriverinfo_t usbhhidClassDriverInfo = {
62 "HID", &class_driver_vmt
63};
64
65static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
66 int i;
67 USBHHIDDriver *hidp;
68
69 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
70 0x03, -1, -1) != HAL_SUCCESS)
71 return NULL;
72
73 const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
74
75 if ((ifdesc->bAlternateSetting != 0)
76 || (ifdesc->bNumEndpoints < 1)) {
77 return NULL;
78 }
79
80
81 /* alloc driver */
82 for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
83 if (USBHHIDD[i].dev == NULL) {
84 hidp = &USBHHIDD[i];
85 goto alloc_ok;
86 }
87 }
88
89 udevwarn("Can't alloc HID driver");
90
91 /* can't alloc */
92 return NULL;
93
94alloc_ok:
95 /* initialize the driver's variables */
96 hidp->epin.status = USBH_EPSTATUS_UNINITIALIZED;
97#if HAL_USBHHID_USE_INTERRUPT_OUT
98 hidp->epout.status = USBH_EPSTATUS_UNINITIALIZED;
99#endif
100 hidp->ifnum = ifdesc->bInterfaceNumber;
101 usbhEPSetName(&dev->ctrl, "HID[CTRL]");
102
103 /* parse the configuration descriptor */
104 if_iterator_t iif;
105 generic_iterator_t iep;
106 iif.iad = 0;
107 iif.curr = descriptor;
108 iif.rem = rem;
109 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
110 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
111 if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
112 udevinfof("INT IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
113 usbhEPObjectInit(&hidp->epin, dev, epdesc);
114 usbhEPSetName(&hidp->epin, "HID[IIN ]");
115#if HAL_USBHHID_USE_INTERRUPT_OUT
116 } else if (((epdesc->bEndpointAddress & 0x80) == 0)
117 && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
118 uinfof("INT OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
119 usbhEPObjectInit(&hidp->epout, dev, epdesc);
120 usbhEPSetName(&hidp->epout, "HID[IOUT]");
121#endif
122 } else {
123 udevinfof("unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
124 epdesc->bEndpointAddress, epdesc->bmAttributes);
125 }
126 }
127 if (hidp->epin.status != USBH_EPSTATUS_CLOSED) {
128 goto deinit;
129 }
130
131 if (ifdesc->bInterfaceSubClass != 0x01) {
132 hidp->type = USBHHID_DEVTYPE_GENERIC;
133 udevinfof("HID: bInterfaceSubClass=%02x, generic HID", ifdesc->bInterfaceSubClass);
134 if (ifdesc->bInterfaceSubClass != 0x00) {
135 udevinfof("HID: bInterfaceSubClass=%02x is an invalid bInterfaceSubClass value",
136 ifdesc->bInterfaceSubClass);
137 }
138 } else if (ifdesc->bInterfaceProtocol == 0x01) {
139 hidp->type = USBHHID_DEVTYPE_BOOT_KEYBOARD;
140 udevinfo("HID: BOOT protocol keyboard found");
141 } else if (ifdesc->bInterfaceProtocol == 0x02) {
142 hidp->type = USBHHID_DEVTYPE_BOOT_MOUSE;
143 udevinfo("HID: BOOT protocol mouse found");
144 } else {
145 udeverrf("HID: bInterfaceProtocol=%02x is an invalid boot protocol, abort",
146 ifdesc->bInterfaceProtocol);
147 goto deinit;
148 }
149
150 hidp->state = USBHHID_STATE_ACTIVE;
151
152 return (usbh_baseclassdriver_t *)hidp;
153
154deinit:
155 /* Here, the enpoints are closed, and the driver is unlinked */
156 return NULL;
157}
158
159static void _hid_unload(usbh_baseclassdriver_t *drv) {
160 USBHHIDDriver *const hidp = (USBHHIDDriver *)drv;
161 chSemWait(&hidp->sem);
162 _stop_locked(hidp);
163 hidp->state = USBHHID_STATE_STOP;
164 chSemSignal(&hidp->sem);
165}
166
167static void _in_cb(usbh_urb_t *urb) {
168 USBHHIDDriver *const hidp = (USBHHIDDriver *)urb->userData;
169 switch (urb->status) {
170 case USBH_URBSTATUS_OK:
171 if (hidp->config->cb_report) {
172 hidp->config->cb_report(hidp, urb->actualLength);
173 }
174 break;
175 case USBH_URBSTATUS_DISCONNECTED:
176 uurbwarn("HID: URB IN disconnected");
177
178 return;
179 case USBH_URBSTATUS_TIMEOUT:
180 //no data
181 break;
182 default:
183 uurberrf("HID: URB IN status unexpected = %d", urb->status);
184 break;
185 }
186 usbhURBObjectResetI(&hidp->in_urb);
187 usbhURBSubmitI(&hidp->in_urb);
188}
189
190void usbhhidStart(USBHHIDDriver *hidp, const USBHHIDConfig *cfg) {
191 osalDbgCheck(hidp && cfg);
192 osalDbgCheck(cfg->report_buffer && (cfg->protocol <= USBHHID_PROTOCOL_REPORT));
193
194 chSemWait(&hidp->sem);
195 if (hidp->state == USBHHID_STATE_READY) {
196 chSemSignal(&hidp->sem);
197 return;
198 }
199 osalDbgCheck(hidp->state == USBHHID_STATE_ACTIVE);
200
201 hidp->config = cfg;
202
203 /* init the URBs */
204 uint32_t report_len = hidp->epin.wMaxPacketSize;
205 if (report_len > cfg->report_len)
206 report_len = cfg->report_len;
207 usbhURBObjectInit(&hidp->in_urb, &hidp->epin, _in_cb, hidp,
208 cfg->report_buffer, report_len);
209
210 /* open the int IN/OUT endpoints */
211 usbhEPOpen(&hidp->epin);
212#if HAL_USBHHID_USE_INTERRUPT_OUT
213 if (hidp->epout.status == USBH_EPSTATUS_CLOSED) {
214 usbhEPOpen(&hidp->epout);
215 }
216#endif
217
218 usbhhidSetProtocol(hidp, cfg->protocol);
219
220 usbhURBSubmit(&hidp->in_urb);
221
222 hidp->state = USBHHID_STATE_READY;
223 chSemSignal(&hidp->sem);
224}
225
226static void _stop_locked(USBHHIDDriver *hidp) {
227 if (hidp->state == USBHHID_STATE_ACTIVE)
228 return;
229
230 osalDbgCheck(hidp->state == USBHHID_STATE_READY);
231
232 usbhEPClose(&hidp->epin);
233#if HAL_USBHHID_USE_INTERRUPT_OUT
234 if (hidp->epout.status != USBH_EPSTATUS_UNINITIALIZED) {
235 usbhEPClose(&hidp->epout);
236 }
237#endif
238 hidp->state = USBHHID_STATE_ACTIVE;
239}
240
241void usbhhidStop(USBHHIDDriver *hidp) {
242 chSemWait(&hidp->sem);
243 _stop_locked(hidp);
244 chSemSignal(&hidp->sem);
245}
246
247usbh_urbstatus_t usbhhidGetReport(USBHHIDDriver *hidp,
248 uint8_t report_id, usbhhid_reporttype_t report_type,
249 void *data, uint16_t len) {
250 osalDbgCheck(hidp);
251 osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
252 return usbhControlRequest(hidp->dev,
253 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_REPORT,
254 ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, data);
255}
256
257usbh_urbstatus_t usbhhidSetReport(USBHHIDDriver *hidp,
258 uint8_t report_id, usbhhid_reporttype_t report_type,
259 const void *data, uint16_t len) {
260 osalDbgCheck(hidp);
261 osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
262 return usbhControlRequest(hidp->dev,
263 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_REPORT,
264 ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, (void *)data);
265}
266
267usbh_urbstatus_t usbhhidGetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t *duration) {
268 osalDbgCheck(hidp);
269 return usbhControlRequest(hidp->dev,
270 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_IDLE,
271 report_id, hidp->ifnum, 1, duration);
272}
273
274usbh_urbstatus_t usbhhidSetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t duration) {
275 osalDbgCheck(hidp);
276 return usbhControlRequest(hidp->dev,
277 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_IDLE,
278 (duration << 8) | report_id, hidp->ifnum, 0, NULL);
279}
280
281usbh_urbstatus_t usbhhidGetProtocol(USBHHIDDriver *hidp, uint8_t *protocol) {
282 osalDbgCheck(hidp);
283 return usbhControlRequest(hidp->dev,
284 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_PROTOCOL,
285 0, hidp->ifnum, 1, protocol);
286}
287
288usbh_urbstatus_t usbhhidSetProtocol(USBHHIDDriver *hidp, uint8_t protocol) {
289 osalDbgCheck(hidp);
290 osalDbgAssert(protocol <= 1, "invalid protocol");
291 return usbhControlRequest(hidp->dev,
292 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_PROTOCOL,
293 protocol, hidp->ifnum, 0, NULL);
294}
295
296static void _hid_object_init(USBHHIDDriver *hidp) {
297 osalDbgCheck(hidp != NULL);
298 memset(hidp, 0, sizeof(*hidp));
299 hidp->info = &usbhhidClassDriverInfo;
300 hidp->state = USBHHID_STATE_STOP;
301 chSemObjectInit(&hidp->sem, 1);
302}
303
304static void _hid_init(void) {
305 uint8_t i;
306 for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
307 _hid_object_init(&USBHHIDD[i]);
308 }
309}
310
311#endif
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hub.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hub.c
new file mode 100644
index 000000000..b8937fad7
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hub.c
@@ -0,0 +1,279 @@
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_HUB
21
22#if !HAL_USE_USBH
23#error "USBHHUB needs HAL_USE_USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/hub.h"
28#include "usbh/internal.h"
29
30#define _USBH_DEBUG_HELPER_CLASS_DRIVER hubp
31#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHHUB_DEBUG_ENABLE_TRACE
32#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHHUB_DEBUG_ENABLE_INFO
33#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHHUB_DEBUG_ENABLE_WARNINGS
34#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHHUB_DEBUG_ENABLE_ERRORS
35#include "usbh/debug_helpers.h"
36
37USBHHubDriver USBHHUBD[HAL_USBHHUB_MAX_INSTANCES];
38static usbh_port_t USBHPorts[HAL_USBHHUB_MAX_PORTS];
39
40static void _hub_init(void);
41static usbh_baseclassdriver_t *_hub_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
42static void _hub_unload(usbh_baseclassdriver_t *drv);
43static const usbh_classdriver_vmt_t usbhhubClassDriverVMT = {
44 _hub_init,
45 _hub_load,
46 _hub_unload
47};
48
49const usbh_classdriverinfo_t usbhhubClassDriverInfo = {
50 "HUB", &usbhhubClassDriverVMT
51};
52
53
54void _usbhub_port_object_init(usbh_port_t *port, USBHDriver *usbh,
55 USBHHubDriver *hub, uint8_t number) {
56 memset(port, 0, sizeof(*port));
57 port->number = number;
58 port->device.host = usbh;
59 port->hub = hub;
60}
61
62usbh_urbstatus_t usbhhubControlRequest(USBHDriver *host, USBHHubDriver *hub,
63 uint8_t bmRequestType,
64 uint8_t bRequest,
65 uint16_t wValue,
66 uint16_t wIndex,
67 uint16_t wLength,
68 uint8_t *buf) {
69 if (hub == NULL)
70 return usbh_lld_root_hub_request(host, bmRequestType, bRequest, wValue, wIndex, wLength, buf);
71
72 return usbhControlRequest(hub->dev,
73 bmRequestType, bRequest, wValue, wIndex, wLength, buf);
74}
75
76
77static void _urb_complete(usbh_urb_t *urb) {
78
79 USBHHubDriver *const hubdp = (USBHHubDriver *)urb->userData;
80 switch (urb->status) {
81 case USBH_URBSTATUS_TIMEOUT:
82 /* the device NAKed */
83 uurbdbg("HUB: no info");
84 //hubdp->statuschange = 0;
85 break;
86 case USBH_URBSTATUS_OK: {
87 uint8_t len = hubdp->hubDesc.bNbrPorts / 8 + 1;
88 if (urb->actualLength != len) {
89 uurbwarnf("Expected %d status change bytes but got %d", len, urb->actualLength);
90 }
91
92 if (urb->actualLength < len)
93 len = urb->actualLength;
94
95 if (len > 4)
96 len = 4;
97
98 uint8_t *sc = (uint8_t *)&hubdp->statuschange;
99 uint8_t *r = hubdp->scbuff;
100 while (len--)
101 *sc++ |= *r++;
102
103 uurbinfof("HUB: change, %08x", hubdp->statuschange);
104 } break;
105 case USBH_URBSTATUS_DISCONNECTED:
106 uurbwarn("HUB: URB disconnected, aborting poll");
107 return;
108 default:
109 uurberrf("HUB: URB status unexpected = %d", urb->status);
110 break;
111 }
112
113 usbhURBObjectResetI(urb);
114 usbhURBSubmitI(urb);
115}
116
117static usbh_baseclassdriver_t *_hub_load(usbh_device_t *dev,
118 const uint8_t *descriptor, uint16_t rem) {
119 int i;
120
121 USBHHubDriver *hubdp;
122
123 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_DEVICE,
124 0x09, 0x00, 0x00) != HAL_SUCCESS)
125 return NULL;
126
127 generic_iterator_t iep, icfg;
128 if_iterator_t iif;
129
130 cfg_iter_init(&icfg, dev->fullConfigurationDescriptor,
131 dev->basicConfigDesc.wTotalLength);
132
133 if_iter_init(&iif, &icfg);
134 if (!iif.valid)
135 return NULL;
136
137 if (_usbh_match_descriptor(iif.curr, iif.rem, USBH_DT_INTERFACE,
138 0x09, 0x00, 0x00) != HAL_SUCCESS)
139 return NULL;
140
141 ep_iter_init(&iep, &iif);
142 if (!iep.valid)
143 return NULL;
144 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
145 if ((epdesc->bmAttributes & 0x03) != USBH_EPTYPE_INT) {
146 return NULL;
147 }
148
149
150 /* alloc driver */
151 for (i = 0; i < HAL_USBHHUB_MAX_INSTANCES; i++) {
152 if (USBHHUBD[i].dev == NULL) {
153 hubdp = &USBHHUBD[i];
154 goto alloc_ok;
155 }
156 }
157
158 udevwarn("Can't alloc HUB driver");
159
160 /* can't alloc */
161 return NULL;
162
163alloc_ok:
164 /* initialize the driver's variables */
165 hubdp->epint.status = USBH_EPSTATUS_UNINITIALIZED;
166 hubdp->dev = dev;
167 hubdp->ports = 0;
168
169 usbhEPSetName(&dev->ctrl, "HUB[CTRL]");
170
171 /* read Hub descriptor */
172 udevinfo("Read Hub descriptor");
173 if (usbhhubControlRequest(dev->host, hubdp,
174 USBH_REQTYPE_DIR_IN | USBH_REQTYPE_TYPE_CLASS | USBH_REQTYPE_RECIP_DEVICE,
175 USBH_REQ_GET_DESCRIPTOR,
176 (USBH_DT_HUB << 8), 0, sizeof(hubdp->hubDesc),
177 (uint8_t *)&hubdp->hubDesc) != USBH_URBSTATUS_OK) {
178 hubdp->dev = NULL;
179 return NULL;
180 }
181
182 const usbh_hub_descriptor_t *const hubdesc = &hubdp->hubDesc;
183
184 udevinfof("Hub descriptor loaded; %d ports, wHubCharacteristics=%04x, bPwrOn2PwrGood=%d, bHubContrCurrent=%d",
185 hubdesc->bNbrPorts,
186 hubdesc->wHubCharacteristics,
187 hubdesc->bPwrOn2PwrGood,
188 hubdesc->bHubContrCurrent);
189
190 /* Alloc ports */
191 uint8_t ports = hubdesc->bNbrPorts;
192 for (i = 0; (ports > 0) && (i < HAL_USBHHUB_MAX_PORTS); i++) {
193 if (USBHPorts[i].hub == NULL) {
194 udevinfof("Alloc port %d", ports);
195 _usbhub_port_object_init(&USBHPorts[i], dev->host, hubdp, ports);
196 USBHPorts[i].next = hubdp->ports;
197 hubdp->ports = &USBHPorts[i];
198 --ports;
199 }
200 }
201
202 if (ports) {
203 udevwarn("Could not alloc all ports");
204 }
205
206 /* link hub to the host's list */
207 list_add_tail(&hubdp->node, &dev->host->hubs);
208
209 /* enable power to ports */
210 usbh_port_t *port = hubdp->ports;
211 while (port) {
212 udevinfof("Enable power for port %d", port->number);
213 usbhhubSetFeaturePort(port, USBH_PORT_FEAT_POWER);
214 port = port->next;
215 }
216
217 if (hubdesc->bPwrOn2PwrGood)
218 osalThreadSleepMilliseconds(2 * hubdesc->bPwrOn2PwrGood);
219
220 /* initialize the status change endpoint and trigger the first transfer */
221 usbhEPObjectInit(&hubdp->epint, dev, epdesc);
222 usbhEPSetName(&hubdp->epint, "HUB[INT ]");
223 usbhEPOpen(&hubdp->epint);
224
225 usbhURBObjectInit(&hubdp->urb, &hubdp->epint,
226 _urb_complete, hubdp, hubdp->scbuff,
227 (hubdesc->bNbrPorts + 8) / 8);
228
229 usbhURBSubmit(&hubdp->urb);
230
231 hubdp->dev = NULL;
232 return (usbh_baseclassdriver_t *)hubdp;
233}
234
235static void _hub_unload(usbh_baseclassdriver_t *drv) {
236 osalDbgCheck(drv != NULL);
237 USBHHubDriver *const hubdp = (USBHHubDriver *)drv;
238
239 /* close the status change endpoint (this cancels ongoing URBs) */
240 usbhEPClose(&hubdp->epint);
241
242 /* de-alloc ports and unload drivers */
243 usbh_port_t *port = hubdp->ports;
244 while (port) {
245 _usbh_port_disconnected(port);
246 port->hub = NULL;
247 port = port->next;
248 }
249
250 /* unlink the hub from the host's list */
251 list_del(&hubdp->node);
252
253}
254
255static void _object_init(USBHHubDriver *hubdp) {
256 osalDbgCheck(hubdp != NULL);
257 memset(hubdp, 0, sizeof(*hubdp));
258 hubdp->info = &usbhhubClassDriverInfo;
259}
260
261static void _hub_init(void) {
262 uint8_t i;
263 for (i = 0; i < HAL_USBHHUB_MAX_INSTANCES; i++) {
264 _object_init(&USBHHUBD[i]);
265 }
266}
267
268#else
269
270#if HAL_USE_USBH
271#include <string.h>
272void _usbhub_port_object_init(usbh_port_t *port, USBHDriver *usbh, uint8_t number) {
273 memset(port, 0, sizeof(*port));
274 port->number = number;
275 port->device.host = usbh;
276}
277#endif
278
279#endif
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_msd.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_msd.c
new file mode 100644
index 000000000..081cccb8a
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_msd.c
@@ -0,0 +1,952 @@
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_MSD
21
22#if !HAL_USE_USBH
23#error "USBHMSD needs USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/msd.h"
28#include "usbh/internal.h"
29
30#define _USBH_DEBUG_HELPER_CLASS_DRIVER msdp
31#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHMSD_DEBUG_ENABLE_TRACE
32#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHMSD_DEBUG_ENABLE_INFO
33#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHMSD_DEBUG_ENABLE_WARNINGS
34#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHMSD_DEBUG_ENABLE_ERRORS
35#include "usbh/debug_helpers.h"
36
37static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp);
38
39/*===========================================================================*/
40/* USB Class driver loader for MSD */
41/*===========================================================================*/
42
43struct USBHMassStorageDriver {
44 /* inherited from abstract class driver */
45 _usbh_base_classdriver_data
46
47 usbh_ep_t epin;
48 usbh_ep_t epout;
49 uint8_t ifnum;
50 uint8_t max_lun;
51 uint32_t tag;
52
53 USBHMassStorageLUNDriver *luns;
54};
55
56static USBHMassStorageDriver USBHMSD[HAL_USBHMSD_MAX_INSTANCES];
57
58static void _msd_init(void);
59static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
60static void _msd_unload(usbh_baseclassdriver_t *drv);
61
62static const usbh_classdriver_vmt_t class_driver_vmt = {
63 _msd_init,
64 _msd_load,
65 _msd_unload
66};
67
68const usbh_classdriverinfo_t usbhmsdClassDriverInfo = {
69 "MSD", &class_driver_vmt
70};
71
72#define MSD_REQ_RESET 0xFF
73#define MSD_GET_MAX_LUN 0xFE
74
75static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
76 int i;
77 USBHMassStorageDriver *msdp;
78 uint8_t luns;
79 usbh_urbstatus_t stat;
80
81 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
82 0x08, 0x06, 0x50) != HAL_SUCCESS)
83 return NULL;
84
85 const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
86
87 if ((ifdesc->bAlternateSetting != 0)
88 || (ifdesc->bNumEndpoints < 2)) {
89 return NULL;
90 }
91
92 /* alloc driver */
93 for (i = 0; i < HAL_USBHMSD_MAX_INSTANCES; i++) {
94 if (USBHMSD[i].dev == NULL) {
95 msdp = &USBHMSD[i];
96 goto alloc_ok;
97 }
98 }
99
100 udevwarn("Can't alloc MSD driver");
101
102 /* can't alloc */
103 return NULL;
104
105alloc_ok:
106 /* initialize the driver's variables */
107 msdp->epin.status = USBH_EPSTATUS_UNINITIALIZED;
108 msdp->epout.status = USBH_EPSTATUS_UNINITIALIZED;
109 msdp->max_lun = 0;
110 msdp->tag = 0;
111 msdp->luns = 0;
112 msdp->ifnum = ifdesc->bInterfaceNumber;
113 usbhEPSetName(&dev->ctrl, "MSD[CTRL]");
114
115 /* parse the configuration descriptor */
116 if_iterator_t iif;
117 generic_iterator_t iep;
118 iif.iad = 0;
119 iif.curr = descriptor;
120 iif.rem = rem;
121 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
122 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
123 if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
124 udevinfof("BULK IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
125 usbhEPObjectInit(&msdp->epin, dev, epdesc);
126 usbhEPSetName(&msdp->epin, "MSD[BIN ]");
127 } else if (((epdesc->bEndpointAddress & 0x80) == 0)
128 && (epdesc->bmAttributes == USBH_EPTYPE_BULK)) {
129 udevinfof("BULK OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
130 usbhEPObjectInit(&msdp->epout, dev, epdesc);
131 usbhEPSetName(&msdp->epout, "MSD[BOUT]");
132 } else {
133 udevinfof("unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
134 epdesc->bEndpointAddress, epdesc->bmAttributes);
135 }
136 }
137 if ((msdp->epin.status != USBH_EPSTATUS_CLOSED) || (msdp->epout.status != USBH_EPSTATUS_CLOSED)) {
138 goto deinit;
139 }
140
141 /* read the number of LUNs */
142 udevinfo("Reading Max LUN:");
143 USBH_DEFINE_BUFFER(uint8_t buff[4]);
144 stat = usbhControlRequest(dev,
145 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE),
146 MSD_GET_MAX_LUN, 0, msdp->ifnum, 1, buff);
147 if (stat == USBH_URBSTATUS_OK) {
148 msdp->max_lun = buff[0] + 1;
149 udevinfof("\tmax_lun = %d", msdp->max_lun);
150 if (msdp->max_lun > HAL_USBHMSD_MAX_LUNS) {
151 msdp->max_lun = HAL_USBHMSD_MAX_LUNS;
152 udevwarnf("\tUsing max_lun = %d", msdp->max_lun);
153 }
154 } else if (stat == USBH_URBSTATUS_STALL) {
155 udevwarn("\tStall, max_lun = 1");
156 msdp->max_lun = 1;
157 } else {
158 udeverr("\tError");
159 goto deinit;
160 }
161
162 /* open the bulk IN/OUT endpoints */
163 usbhEPOpen(&msdp->epin);
164 usbhEPOpen(&msdp->epout);
165
166 /* Alloc one block device per logical unit found */
167 luns = msdp->max_lun;
168 for (i = 0; (luns > 0) && (i < HAL_USBHMSD_MAX_LUNS); i++) {
169 if (MSBLKD[i].msdp == NULL) {
170 /* link the new block driver to the list */
171 MSBLKD[i].next = msdp->luns;
172 msdp->luns = &MSBLKD[i];
173 MSBLKD[i].msdp = msdp;
174 MSBLKD[i].state = BLK_ACTIVE;
175 luns--;
176 }
177 }
178
179 return (usbh_baseclassdriver_t *)msdp;
180
181deinit:
182 /* Here, the enpoints are closed, and the driver is unlinked */
183 return NULL;
184}
185
186static void _msd_unload(usbh_baseclassdriver_t *drv) {
187 osalDbgCheck(drv != NULL);
188 USBHMassStorageDriver *const msdp = (USBHMassStorageDriver *)drv;
189 USBHMassStorageLUNDriver *lunp = msdp->luns;
190
191 /* disconnect all LUNs */
192 while (lunp) {
193 usbhmsdLUNDisconnect(lunp);
194 _lun_object_deinit(lunp);
195 lunp = lunp->next;
196 }
197
198 usbhEPClose(&msdp->epin);
199 usbhEPClose(&msdp->epout);
200}
201
202
203/*===========================================================================*/
204/* MSD Class driver operations (Bulk-Only transport) */
205/*===========================================================================*/
206
207/* USB Bulk Only Transport SCSI Command block wrapper */
208typedef PACKED_STRUCT {
209 uint32_t dCBWSignature;
210 uint32_t dCBWTag;
211 uint32_t dCBWDataTransferLength;
212 uint8_t bmCBWFlags;
213 uint8_t bCBWLUN;
214 uint8_t bCBWCBLength;
215 uint8_t CBWCB[16];
216} msd_cbw_t;
217#define MSD_CBW_SIGNATURE 0x43425355
218#define MSD_CBWFLAGS_D2H 0x80
219#define MSD_CBWFLAGS_H2D 0x00
220
221/* USB Bulk Only Transport SCSI Command status wrapper */
222typedef PACKED_STRUCT {
223 uint32_t dCSWSignature;
224 uint32_t dCSWTag;
225 uint32_t dCSWDataResidue;
226 uint8_t bCSWStatus;
227} msd_csw_t;
228#define MSD_CSW_SIGNATURE 0x53425355
229
230typedef struct {
231 msd_cbw_t *cbw;
232 uint8_t csw_status;
233 uint32_t data_processed;
234} msd_transaction_t;
235
236typedef enum {
237 MSD_BOTRESULT_OK,
238 MSD_BOTRESULT_DISCONNECTED,
239 MSD_BOTRESULT_ERROR
240} msd_bot_result_t;
241
242typedef enum {
243 MSD_RESULT_OK = MSD_BOTRESULT_OK,
244 MSD_RESULT_DISCONNECTED = MSD_BOTRESULT_DISCONNECTED,
245 MSD_RESULT_TRANSPORT_ERROR = MSD_BOTRESULT_ERROR,
246 MSD_RESULT_FAILED
247} msd_result_t;
248
249
250#define CSW_STATUS_PASSED 0
251#define CSW_STATUS_FAILED 1
252#define CSW_STATUS_PHASE_ERROR 2
253
254static bool _msd_bot_reset(USBHMassStorageDriver *msdp) {
255
256 usbh_urbstatus_t res;
257 res = usbhControlRequest(msdp->dev,
258 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE),
259 0xFF, 0, msdp->ifnum, 0, NULL);
260 if (res != USBH_URBSTATUS_OK) {
261 return FALSE;
262 }
263
264 osalThreadSleepMilliseconds(100);
265
266 return usbhEPReset(&msdp->epin) && usbhEPReset(&msdp->epout);
267}
268
269static msd_bot_result_t _msd_bot_transaction(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp, void *data) {
270
271 USBHMassStorageDriver *const msdp = lunp->msdp;
272
273 uint32_t data_actual_len, actual_len;
274 usbh_urbstatus_t status;
275 USBH_DEFINE_BUFFER(msd_csw_t csw);
276
277 tran->cbw->bCBWLUN = (uint8_t)(lunp - &msdp->luns[0]);
278 tran->cbw->dCBWSignature = MSD_CBW_SIGNATURE;
279 tran->cbw->dCBWTag = ++msdp->tag;
280 tran->data_processed = 0;
281
282 /* control phase */
283 status = usbhBulkTransfer(&msdp->epout, tran->cbw,
284 sizeof(*tran->cbw), &actual_len, OSAL_MS2I(1000));
285
286 if (status == USBH_URBSTATUS_CANCELLED) {
287 uclassdrverr("\tMSD: Control phase: USBH_URBSTATUS_CANCELLED");
288 return MSD_BOTRESULT_DISCONNECTED;
289 }
290
291 if ((status != USBH_URBSTATUS_OK) || (actual_len != sizeof(*tran->cbw))) {
292 uclassdrverrf("\tMSD: Control phase: status = %d (!= OK), actual_len = %d (expected to send %d)",
293 status, actual_len, sizeof(*tran->cbw));
294 _msd_bot_reset(msdp);
295 return MSD_BOTRESULT_ERROR;
296 }
297
298
299 /* data phase */
300 data_actual_len = 0;
301 if (tran->cbw->dCBWDataTransferLength) {
302 usbh_ep_t *const ep = tran->cbw->bmCBWFlags & MSD_CBWFLAGS_D2H ? &msdp->epin : &msdp->epout;
303 status = usbhBulkTransfer(
304 ep,
305 data,
306 tran->cbw->dCBWDataTransferLength,
307 &data_actual_len, OSAL_MS2I(20000));
308
309 if (status == USBH_URBSTATUS_CANCELLED) {
310 uclassdrverr("\tMSD: Data phase: USBH_URBSTATUS_CANCELLED");
311 return MSD_BOTRESULT_DISCONNECTED;
312 }
313
314 if (status == USBH_URBSTATUS_STALL) {
315 uclassdrverrf("\tMSD: Data phase: USBH_URBSTATUS_STALL, clear halt");
316 status = (usbhEPReset(ep) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR;
317 }
318
319 if (status != USBH_URBSTATUS_OK) {
320 uclassdrverrf("\tMSD: Data phase: status = %d (!= OK), resetting", status);
321 _msd_bot_reset(msdp);
322 return MSD_BOTRESULT_ERROR;
323 }
324 }
325
326
327 /* status phase */
328 status = usbhBulkTransfer(&msdp->epin, &csw,
329 sizeof(csw), &actual_len, OSAL_MS2I(1000));
330
331 if (status == USBH_URBSTATUS_STALL) {
332 uclassdrvwarn("\tMSD: Status phase: USBH_URBSTATUS_STALL, clear halt and retry");
333
334 status = (usbhEPReset(&msdp->epin) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR;
335
336 if (status == USBH_URBSTATUS_OK) {
337 status = usbhBulkTransfer(&msdp->epin, &csw,
338 sizeof(csw), &actual_len, OSAL_MS2I(1000));
339 }
340 }
341
342 if (status == USBH_URBSTATUS_CANCELLED) {
343 uclassdrverr("\tMSD: Status phase: USBH_URBSTATUS_CANCELLED");
344 return MSD_BOTRESULT_DISCONNECTED;
345 }
346
347 if (status != USBH_URBSTATUS_OK) {
348 uclassdrverrf("\tMSD: Status phase: status = %d (!= OK), resetting", status);
349 _msd_bot_reset(msdp);
350 return MSD_BOTRESULT_ERROR;
351 }
352
353 /* validate CSW */
354 if ((actual_len != sizeof(csw))
355 || (csw.dCSWSignature != MSD_CSW_SIGNATURE)
356 || (csw.dCSWTag != msdp->tag)
357 || (csw.bCSWStatus >= CSW_STATUS_PHASE_ERROR)) {
358 /* CSW is not valid */
359 uclassdrverrf("\tMSD: Status phase: Invalid CSW: len=%d, dCSWSignature=%x, dCSWTag=%x (expected %x), bCSWStatus=%d, resetting",
360 actual_len,
361 csw.dCSWSignature,
362 csw.dCSWTag,
363 msdp->tag,
364 csw.bCSWStatus);
365 _msd_bot_reset(msdp);
366 return MSD_BOTRESULT_ERROR;
367 }
368
369 /* check if CSW is meaningful */
370 if ((csw.bCSWStatus != CSW_STATUS_PHASE_ERROR)
371 && (csw.dCSWDataResidue > tran->cbw->dCBWDataTransferLength)) {
372 /* CSW is not meaningful */
373 uclassdrverrf("\tMSD: Status phase: CSW not meaningful: bCSWStatus=%d, dCSWDataResidue=%u, dCBWDataTransferLength=%u, resetting",
374 csw.bCSWStatus,
375 csw.dCSWDataResidue,
376 tran->cbw->dCBWDataTransferLength);
377 _msd_bot_reset(msdp);
378 return MSD_BOTRESULT_ERROR;
379 }
380
381 if (csw.bCSWStatus == CSW_STATUS_PHASE_ERROR) {
382 uclassdrverr("\tMSD: Status phase: Phase error, resetting");
383 _msd_bot_reset(msdp);
384 return MSD_BOTRESULT_ERROR;
385 }
386
387 tran->data_processed = tran->cbw->dCBWDataTransferLength - csw.dCSWDataResidue;
388 if (data_actual_len < tran->data_processed) {
389 tran->data_processed = data_actual_len;
390 }
391
392 tran->csw_status = csw.bCSWStatus;
393
394 return MSD_BOTRESULT_OK;
395}
396
397
398/* ----------------------------------------------------- */
399/* SCSI Commands */
400/* ----------------------------------------------------- */
401
402/* Read 10 and Write 10 */
403#define SCSI_CMD_READ_10 0x28
404#define SCSI_CMD_WRITE_10 0x2A
405
406/* Request sense */
407#define SCSI_CMD_REQUEST_SENSE 0x03
408typedef PACKED_STRUCT {
409 uint8_t byte[18];
410} scsi_sense_response_t;
411
412#define SCSI_SENSE_KEY_GOOD 0x00
413#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01
414#define SCSI_SENSE_KEY_NOT_READY 0x02
415#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03
416#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04
417#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05
418#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06
419#define SCSI_SENSE_KEY_DATA_PROTECT 0x07
420#define SCSI_SENSE_KEY_BLANK_CHECK 0x08
421#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09
422#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A
423#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B
424#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D
425#define SCSI_SENSE_KEY_MISCOMPARE 0x0E
426#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00
427#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04
428#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24
429#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28
430#define SCSI_ASENSE_WRITE_PROTECTED 0x27
431#define SCSI_ASENSE_FORMAT_ERROR 0x31
432#define SCSI_ASENSE_INVALID_COMMAND 0x20
433#define SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
434#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A
435#define SCSI_ASENSEQ_NO_QUALIFIER 0x00
436#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01
437#define SCSI_ASENSEQ_INITIALIZING_COMMAND_REQUIRED 0x02
438#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07
439
440/* Inquiry */
441#define SCSI_CMD_INQUIRY 0x12
442typedef PACKED_STRUCT {
443 uint8_t peripheral;
444 uint8_t removable;
445 uint8_t version;
446 uint8_t response_data_format;
447 uint8_t additional_length;
448 uint8_t sccstp;
449 uint8_t bqueetc;
450 uint8_t cmdque;
451 uint8_t vendorID[8];
452 uint8_t productID[16];
453 uint8_t productRev[4];
454} scsi_inquiry_response_t;
455
456/* Read Capacity 10 */
457#define SCSI_CMD_READ_CAPACITY_10 0x25
458typedef PACKED_STRUCT {
459 uint32_t last_block_addr;
460 uint32_t block_size;
461} scsi_readcapacity10_response_t;
462
463/* Start/Stop Unit */
464#define SCSI_CMD_START_STOP_UNIT 0x1B
465typedef PACKED_STRUCT {
466 uint8_t op_code;
467 uint8_t lun_immed;
468 uint8_t res1;
469 uint8_t res2;
470 uint8_t loej_start;
471 uint8_t control;
472} scsi_startstopunit_request_t;
473
474/* test unit ready */
475#define SCSI_CMD_TEST_UNIT_READY 0x00
476
477static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp);
478
479static msd_result_t _scsi_perform_transaction(USBHMassStorageLUNDriver *lunp,
480 msd_transaction_t *transaction, void *data) {
481
482 USBHMassStorageDriver *const msdp = lunp->msdp;
483 (void)msdp;
484
485 msd_bot_result_t res;
486 res = _msd_bot_transaction(transaction, lunp, data);
487 if (res != MSD_BOTRESULT_OK) {
488 return (msd_result_t)res;
489 }
490
491 if (transaction->csw_status == CSW_STATUS_FAILED) {
492 if (transaction->cbw->CBWCB[0] != SCSI_CMD_REQUEST_SENSE) {
493 /* do auto-sense (except for SCSI_CMD_REQUEST_SENSE!) */
494 uclassdrvwarn("\tMSD: Command failed, auto-sense");
495 USBH_DEFINE_BUFFER(scsi_sense_response_t sense);
496 if (scsi_requestsense(lunp, &sense) == MSD_RESULT_OK) {
497 uclassdrvwarnf("\tMSD: REQUEST SENSE: Sense key=%x, ASC=%02x, ASCQ=%02x",
498 sense.byte[2] & 0xf, sense.byte[12], sense.byte[13]);
499 }
500 }
501 return MSD_RESULT_FAILED;
502 }
503
504 return MSD_RESULT_OK;
505}
506
507static msd_result_t scsi_inquiry(USBHMassStorageLUNDriver *lunp, scsi_inquiry_response_t *resp) {
508 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
509 msd_transaction_t transaction;
510 msd_result_t res;
511
512 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
513 cbw.dCBWDataTransferLength = sizeof(scsi_inquiry_response_t);
514 cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
515 cbw.bCBWCBLength = 6;
516 cbw.CBWCB[0] = SCSI_CMD_INQUIRY;
517 cbw.CBWCB[4] = sizeof(scsi_inquiry_response_t);
518 transaction.cbw = &cbw;
519
520 res = _scsi_perform_transaction(lunp, &transaction, resp);
521 if (res == MSD_RESULT_OK) {
522 //transaction is OK; check length
523 if (transaction.data_processed < cbw.dCBWDataTransferLength) {
524 res = MSD_RESULT_TRANSPORT_ERROR;
525 }
526 }
527
528 return res;
529}
530
531static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp) {
532 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
533 msd_transaction_t transaction;
534 msd_result_t res;
535
536 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
537 cbw.dCBWDataTransferLength = sizeof(scsi_sense_response_t);
538 cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
539 cbw.bCBWCBLength = 12;
540 cbw.CBWCB[0] = SCSI_CMD_REQUEST_SENSE;
541 cbw.CBWCB[4] = sizeof(scsi_sense_response_t);
542 transaction.cbw = &cbw;
543
544 res = _scsi_perform_transaction(lunp, &transaction, resp);
545 if (res == MSD_RESULT_OK) {
546 //transaction is OK; check length
547 if (transaction.data_processed < cbw.dCBWDataTransferLength) {
548 res = MSD_RESULT_TRANSPORT_ERROR;
549 }
550 }
551
552 return res;
553}
554
555static msd_result_t scsi_testunitready(USBHMassStorageLUNDriver *lunp) {
556 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
557 msd_transaction_t transaction;
558
559 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
560 cbw.dCBWDataTransferLength = 0;
561 cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
562 cbw.bCBWCBLength = 6;
563 cbw.CBWCB[0] = SCSI_CMD_TEST_UNIT_READY;
564 transaction.cbw = &cbw;
565
566 return _scsi_perform_transaction(lunp, &transaction, NULL);
567}
568
569static msd_result_t scsi_readcapacity10(USBHMassStorageLUNDriver *lunp, scsi_readcapacity10_response_t *resp) {
570 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
571 msd_transaction_t transaction;
572 msd_result_t res;
573
574 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
575 cbw.dCBWDataTransferLength = sizeof(scsi_readcapacity10_response_t);
576 cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
577 cbw.bCBWCBLength = 12;
578 cbw.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10;
579 transaction.cbw = &cbw;
580
581 res = _scsi_perform_transaction(lunp, &transaction, resp);
582 if (res == MSD_RESULT_OK) {
583 //transaction is OK; check length
584 if (transaction.data_processed < cbw.dCBWDataTransferLength) {
585 res = MSD_RESULT_TRANSPORT_ERROR;
586 }
587 }
588
589 return res;
590}
591
592
593static msd_result_t scsi_read10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, uint8_t *data, uint32_t *actual_len) {
594 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
595 msd_transaction_t transaction;
596 msd_result_t res;
597
598 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
599 cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
600 cbw.bmCBWFlags = MSD_CBWFLAGS_D2H;
601 cbw.bCBWCBLength = 10;
602 cbw.CBWCB[0] = SCSI_CMD_READ_10;
603 cbw.CBWCB[2] = (uint8_t)(lba >> 24);
604 cbw.CBWCB[3] = (uint8_t)(lba >> 16);
605 cbw.CBWCB[4] = (uint8_t)(lba >> 8);
606 cbw.CBWCB[5] = (uint8_t)(lba);
607 cbw.CBWCB[7] = (uint8_t)(n >> 8);
608 cbw.CBWCB[8] = (uint8_t)(n);
609 transaction.cbw = &cbw;
610
611 res = _scsi_perform_transaction(lunp, &transaction, data);
612 if (actual_len) {
613 *actual_len = transaction.data_processed;
614 }
615 if (res == MSD_RESULT_OK) {
616 //transaction is OK; check length
617 if (transaction.data_processed < cbw.dCBWDataTransferLength) {
618 res = MSD_RESULT_TRANSPORT_ERROR;
619 }
620 }
621
622 return res;
623}
624
625static msd_result_t scsi_write10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, const uint8_t *data, uint32_t *actual_len) {
626 USBH_DEFINE_BUFFER(msd_cbw_t cbw);
627 msd_transaction_t transaction;
628 msd_result_t res;
629
630 memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB));
631 cbw.dCBWDataTransferLength = n * lunp->info.blk_size;
632 cbw.bmCBWFlags = MSD_CBWFLAGS_H2D;
633 cbw.bCBWCBLength = 10;
634 cbw.CBWCB[0] = SCSI_CMD_WRITE_10;
635 cbw.CBWCB[2] = (uint8_t)(lba >> 24);
636 cbw.CBWCB[3] = (uint8_t)(lba >> 16);
637 cbw.CBWCB[4] = (uint8_t)(lba >> 8);
638 cbw.CBWCB[5] = (uint8_t)(lba);
639 cbw.CBWCB[7] = (uint8_t)(n >> 8);
640 cbw.CBWCB[8] = (uint8_t)(n);
641 transaction.cbw = &cbw;
642
643 res = _scsi_perform_transaction(lunp, &transaction, (void *)data);
644 if (actual_len) {
645 *actual_len = transaction.data_processed;
646 }
647 if (res == MSD_RESULT_OK) {
648 //transaction is OK; check length
649 if (transaction.data_processed < cbw.dCBWDataTransferLength) {
650 res = MSD_RESULT_TRANSPORT_ERROR;
651 }
652 }
653
654 return res;
655}
656
657
658
659/*===========================================================================*/
660/* Block driver data/functions */
661/*===========================================================================*/
662
663USBHMassStorageLUNDriver MSBLKD[HAL_USBHMSD_MAX_LUNS];
664
665static const struct USBHMassStorageDriverVMT blk_vmt = {
666 (size_t)0,
667 (bool (*)(void *))usbhmsdLUNIsInserted,
668 (bool (*)(void *))usbhmsdLUNIsProtected,
669 (bool (*)(void *))usbhmsdLUNConnect,
670 (bool (*)(void *))usbhmsdLUNDisconnect,
671 (bool (*)(void *, uint32_t, uint8_t *, uint32_t))usbhmsdLUNRead,
672 (bool (*)(void *, uint32_t, const uint8_t *, uint32_t))usbhmsdLUNWrite,
673 (bool (*)(void *))usbhmsdLUNSync,
674 (bool (*)(void *, BlockDeviceInfo *))usbhmsdLUNGetInfo
675};
676
677static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp) {
678 osalDbgCheck(lunp != NULL);
679 chSemWait(&lunp->sem);
680 lunp->msdp = NULL;
681 lunp->next = NULL;
682 memset(&lunp->info, 0, sizeof(lunp->info));
683 lunp->state = BLK_STOP;
684 chSemSignal(&lunp->sem);
685}
686
687static void _lun_object_init(USBHMassStorageLUNDriver *lunp) {
688 osalDbgCheck(lunp != NULL);
689 memset(lunp, 0, sizeof(*lunp));
690 lunp->vmt = &blk_vmt;
691 lunp->state = BLK_STOP;
692 chSemObjectInit(&lunp->sem, 1);
693 /* Unnecessary because of the memset:
694 lunp->msdp = NULL;
695 lunp->next = NULL;
696 lunp->info.* = 0;
697 */
698}
699
700bool usbhmsdLUNConnect(USBHMassStorageLUNDriver *lunp) {
701 osalDbgCheck(lunp != NULL);
702 osalDbgCheck(lunp->msdp != NULL);
703 msd_result_t res;
704
705 USBHMassStorageDriver *const msdp = lunp->msdp;
706 (void)msdp;
707
708 chSemWait(&lunp->sem);
709 osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state");
710 if (lunp->state == BLK_READY) {
711 chSemSignal(&lunp->sem);
712 return HAL_SUCCESS;
713 }
714 lunp->state = BLK_CONNECTING;
715
716 {
717 USBH_DEFINE_BUFFER(scsi_inquiry_response_t inq);
718 uclassdrvinfo("INQUIRY...");
719 res = scsi_inquiry(lunp, &inq);
720 if (res == MSD_RESULT_DISCONNECTED) {
721 goto failed;
722 } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
723 //retry?
724 goto failed;
725 } else if (res == MSD_RESULT_FAILED) {
726 //retry?
727 goto failed;
728 }
729
730 uclassdrvinfof("\tPDT=%02x", inq.peripheral & 0x1f);
731 if (inq.peripheral != 0) {
732 uclassdrverr("\tUnsupported PDT");
733 goto failed;
734 }
735 }
736
737 // Test if unit ready
738 uint8_t i;
739 for (i = 0; i < 10; i++) {
740 uclassdrvinfo("TEST UNIT READY...");
741 res = scsi_testunitready(lunp);
742 if (res == MSD_RESULT_DISCONNECTED) {
743 goto failed;
744 } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
745 //retry?
746 goto failed;
747 } else if (res == MSD_RESULT_FAILED) {
748 uclassdrvinfo("\tTEST UNIT READY: Command Failed, retry");
749 osalThreadSleepMilliseconds(200);
750 continue;
751 }
752 uclassdrvinfo("\tReady.");
753 break;
754 }
755 if (i == 10) goto failed;
756
757 {
758 USBH_DEFINE_BUFFER(scsi_readcapacity10_response_t cap);
759 // Read capacity
760 uclassdrvinfo("READ CAPACITY(10)...");
761 res = scsi_readcapacity10(lunp, &cap);
762 if (res == MSD_RESULT_DISCONNECTED) {
763 goto failed;
764 } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
765 //retry?
766 goto failed;
767 } else if (res == MSD_RESULT_FAILED) {
768 //retry?
769 goto failed;
770 }
771
772 lunp->info.blk_size = __REV(cap.block_size);
773 lunp->info.blk_num = __REV(cap.last_block_addr) + 1;
774 }
775
776 uclassdrvinfof("\tBlock size=%dbytes, blocks=%u (~%u MB)", lunp->info.blk_size, lunp->info.blk_num,
777 (uint32_t)(((uint64_t)lunp->info.blk_size * lunp->info.blk_num) / (1024UL * 1024UL)));
778
779 uclassdrvinfo("MSD Connected.");
780 lunp->state = BLK_READY;
781 chSemSignal(&lunp->sem);
782 return HAL_SUCCESS;
783
784 /* Connection failed, state reset to BLK_ACTIVE.*/
785failed:
786 uclassdrvinfo("MSD Connect failed.");
787 lunp->state = BLK_ACTIVE;
788 chSemSignal(&lunp->sem);
789 return HAL_FAILED;
790}
791
792bool usbhmsdLUNDisconnect(USBHMassStorageLUNDriver *lunp) {
793 osalDbgCheck(lunp != NULL);
794
795 chSemWait(&lunp->sem);
796 osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state");
797 if (lunp->state == BLK_ACTIVE) {
798 chSemSignal(&lunp->sem);
799 return HAL_SUCCESS;
800 }
801 lunp->state = BLK_DISCONNECTING;
802
803 //TODO: complete: sync, etc.
804
805 lunp->state = BLK_ACTIVE;
806 chSemSignal(&lunp->sem);
807
808 return HAL_SUCCESS;
809}
810
811bool usbhmsdLUNRead(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
812 uint8_t *buffer, uint32_t n) {
813
814 osalDbgCheck(lunp != NULL);
815 bool ret = HAL_FAILED;
816 uint16_t blocks;
817 msd_result_t res;
818 uint32_t actual_len;
819
820 chSemWait(&lunp->sem);
821 if (lunp->state != BLK_READY) {
822 chSemSignal(&lunp->sem);
823 return ret;
824 }
825 lunp->state = BLK_READING;
826
827 while (n) {
828 if (n > 0xffff) {
829 blocks = 0xffff;
830 } else {
831 blocks = (uint16_t)n;
832 }
833 res = scsi_read10(lunp, startblk, blocks, buffer, &actual_len);
834 if (res == MSD_RESULT_DISCONNECTED) {
835 goto exit;
836 } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
837 //retry?
838 goto exit;
839 } else if (res == MSD_RESULT_FAILED) {
840 //retry?
841 goto exit;
842 }
843 n -= blocks;
844 startblk += blocks;
845 buffer += blocks * lunp->info.blk_size;
846 }
847
848 ret = HAL_SUCCESS;
849
850exit:
851 lunp->state = BLK_READY;
852 chSemSignal(&lunp->sem);
853 return ret;
854}
855
856bool usbhmsdLUNWrite(USBHMassStorageLUNDriver *lunp, uint32_t startblk,
857 const uint8_t *buffer, uint32_t n) {
858
859 osalDbgCheck(lunp != NULL);
860 bool ret = HAL_FAILED;
861 uint16_t blocks;
862 msd_result_t res;
863 uint32_t actual_len;
864
865 chSemWait(&lunp->sem);
866 if (lunp->state != BLK_READY) {
867 chSemSignal(&lunp->sem);
868 return ret;
869 }
870 lunp->state = BLK_WRITING;
871
872 while (n) {
873 if (n > 0xffff) {
874 blocks = 0xffff;
875 } else {
876 blocks = (uint16_t)n;
877 }
878 res = scsi_write10(lunp, startblk, blocks, buffer, &actual_len);
879 if (res == MSD_RESULT_DISCONNECTED) {
880 goto exit;
881 } else if (res == MSD_RESULT_TRANSPORT_ERROR) {
882 //retry?
883 goto exit;
884 } else if (res == MSD_RESULT_FAILED) {
885 //retry?
886 goto exit;
887 }
888 n -= blocks;
889 startblk += blocks;
890 buffer += blocks * lunp->info.blk_size;
891 }
892
893 ret = HAL_SUCCESS;
894
895exit:
896 lunp->state = BLK_READY;
897 chSemSignal(&lunp->sem);
898 return ret;
899}
900
901bool usbhmsdLUNSync(USBHMassStorageLUNDriver *lunp) {
902 osalDbgCheck(lunp != NULL);
903 (void)lunp;
904 //TODO: Do SCSI Sync
905 return HAL_SUCCESS;
906}
907
908bool usbhmsdLUNGetInfo(USBHMassStorageLUNDriver *lunp, BlockDeviceInfo *bdip) {
909 osalDbgCheck(lunp != NULL);
910 osalDbgCheck(bdip != NULL);
911
912 osalSysLock();
913 if (lunp->state >= BLK_READY) {
914 *bdip = lunp->info;
915 osalSysUnlock();
916 return HAL_SUCCESS;
917 }
918 osalSysUnlock();
919 return HAL_FAILED;
920}
921
922bool usbhmsdLUNIsInserted(USBHMassStorageLUNDriver *lunp) {
923 osalDbgCheck(lunp != NULL);
924 return (lunp->state >= BLK_ACTIVE);
925}
926
927bool usbhmsdLUNIsProtected(USBHMassStorageLUNDriver *lunp) {
928 osalDbgCheck(lunp != NULL);
929 //TODO: Implement
930 return FALSE;
931}
932
933USBHDriver *usbhmsdLUNGetHost(const USBHMassStorageLUNDriver *lunp) {
934 return lunp->msdp->dev->host;
935}
936
937static void _msd_object_init(USBHMassStorageDriver *msdp) {
938 osalDbgCheck(msdp != NULL);
939 memset(msdp, 0, sizeof(*msdp));
940 msdp->info = &usbhmsdClassDriverInfo;
941}
942
943static void _msd_init(void) {
944 uint8_t i;
945 for (i = 0; i < HAL_USBHMSD_MAX_INSTANCES; i++) {
946 _msd_object_init(&USBHMSD[i]);
947 }
948 for (i = 0; i < HAL_USBHMSD_MAX_LUNS; i++) {
949 _lun_object_init(&MSBLKD[i]);
950 }
951}
952#endif
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_uvc.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_uvc.c
new file mode 100644
index 000000000..3a8b488b6
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_uvc.c
@@ -0,0 +1,718 @@
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_UVC
21
22#if !HAL_USE_USBH
23#error "USBHUVC needs HAL_USE_USBH"
24#endif
25
26#if !HAL_USBH_USE_IAD
27#error "USBHUVC needs HAL_USBH_USE_IAD"
28#endif
29
30#include <string.h>
31#include "usbh/dev/uvc.h"
32#include "usbh/internal.h"
33
34#define _USBH_DEBUG_HELPER_CLASS_DRIVER uvcdp
35#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHUVC_DEBUG_ENABLE_TRACE
36#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHUVC_DEBUG_ENABLE_INFO
37#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHUVC_DEBUG_ENABLE_WARNINGS
38#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHUVC_DEBUG_ENABLE_ERRORS
39#include "usbh/debug_helpers.h"
40
41USBHUVCDriver USBHUVCD[HAL_USBHUVC_MAX_INSTANCES];
42
43static void _uvc_init(void);
44static usbh_baseclassdriver_t *_uvc_load(usbh_device_t *dev,
45 const uint8_t *descriptor, uint16_t rem);
46static void _uvc_unload(usbh_baseclassdriver_t *drv);
47
48static const usbh_classdriver_vmt_t class_driver_vmt = {
49 _uvc_init,
50 _uvc_load,
51 _uvc_unload
52};
53const usbh_classdriverinfo_t usbhuvcClassDriverInfo = {
54 "UVC", &class_driver_vmt
55};
56
57static bool _request(USBHUVCDriver *uvcdp,
58 uint8_t bRequest, uint8_t entity, uint8_t control,
59 uint16_t wLength, uint8_t *data, uint8_t interf) {
60
61 usbh_urbstatus_t res;
62
63 if (bRequest & 0x80) {
64 res = usbhControlRequest(uvcdp->dev,
65 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE),
66 bRequest,
67 ((control) << 8),
68 (interf) | ((entity) << 8),
69 wLength, data);
70 } else {
71 res = usbhControlRequest(uvcdp->dev,
72 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE),
73 bRequest,
74 ((control) << 8),
75 (interf) | ((entity) << 8),
76 wLength, data);
77 }
78
79 if (res != USBH_URBSTATUS_OK)
80 return HAL_FAILED;
81
82 return HAL_SUCCESS;
83}
84
85bool usbhuvcVCRequest(USBHUVCDriver *uvcdp,
86 uint8_t bRequest, uint8_t entity, uint8_t control,
87 uint16_t wLength, uint8_t *data) {
88 return _request(uvcdp, bRequest, entity, control, wLength, data, if_get(&uvcdp->ivc)->bInterfaceNumber);
89}
90
91bool usbhuvcVSRequest(USBHUVCDriver *uvcdp,
92 uint8_t bRequest, uint8_t control,
93 uint16_t wLength, uint8_t *data) {
94
95 return _request(uvcdp, bRequest, 0, control, wLength, data, if_get(&uvcdp->ivs)->bInterfaceNumber);
96}
97
98static bool _set_vs_alternate(USBHUVCDriver *uvcdp, uint16_t min_ep_size) {
99
100 if (min_ep_size == 0) {
101 uclassdrvinfo("Selecting Alternate setting 0");
102 return usbhStdReqSetInterface(uvcdp->dev, if_get(&uvcdp->ivs)->bInterfaceNumber, 0);
103 }
104
105 if_iterator_t iif = uvcdp->ivs;
106 generic_iterator_t iep;
107 const usbh_endpoint_descriptor_t *ep = NULL;
108 uint8_t alt = 0;
109 uint16_t sz = 0xffff;
110
111 uclassdrvinfof("Searching alternate setting with min_ep_size=%d", min_ep_size);
112
113 for (; iif.valid; if_iter_next(&iif)) {
114 const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
115
116 if ((ifdesc->bInterfaceClass != UVC_CC_VIDEO)
117 || (ifdesc->bInterfaceSubClass != UVC_SC_VIDEOSTREAMING))
118 continue;
119
120 uclassdrvinfof("\tScanning alternate setting=%d", ifdesc->bAlternateSetting);
121
122 if (ifdesc->bNumEndpoints == 0)
123 continue;
124
125 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
126 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
127 if (((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_ISO)
128 && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
129
130 uclassdrvinfof("\t Endpoint wMaxPacketSize = %d", epdesc->wMaxPacketSize);
131
132 if (epdesc->wMaxPacketSize >= min_ep_size) {
133 if (epdesc->wMaxPacketSize < sz) {
134 uclassdrvinfo("\t Found new optimal alternate setting");
135 sz = epdesc->wMaxPacketSize;
136 alt = ifdesc->bAlternateSetting;
137 ep = epdesc;
138 }
139 }
140 }
141 }
142 }
143
144 if (ep && alt) {
145 uclassdrvinfof("\tSelecting Alternate setting %d", alt);
146 if (usbhStdReqSetInterface(uvcdp->dev, if_get(&uvcdp->ivs)->bInterfaceNumber, alt) == HAL_SUCCESS) {
147 usbhEPObjectInit(&uvcdp->ep_iso, uvcdp->dev, ep);
148 usbhEPSetName(&uvcdp->ep_iso, "UVC[ISO ]");
149 return HAL_SUCCESS;
150 }
151 }
152
153 return HAL_FAILED;
154}
155
156#if USBH_DEBUG_ENABLE && USBHUVC_DEBUG_ENABLE_INFO
157void usbhuvcPrintProbeCommit(USBHUVCDriver *uvcdp,
158 const usbh_uvc_ctrl_vs_probecommit_data_t *pc) {
159 (void)uvcdp;
160
161 //uinfof("UVC: probe/commit data:");
162 uclassdrvinfof("\tbmHint=%04x", pc->bmHint);
163 uclassdrvinfof("\tbFormatIndex=%d, bFrameIndex=%d, dwFrameInterval=%u",
164 pc->bFormatIndex, pc->bFrameIndex, pc->dwFrameInterval);
165 uclassdrvinfof("\twKeyFrameRate=%d, wPFrameRate=%d, wCompQuality=%u, wCompWindowSize=%u",
166 pc->wKeyFrameRate, pc->wPFrameRate, pc->wCompQuality, pc->wCompWindowSize);
167 uclassdrvinfof("\twDelay=%d", pc->wDelay);
168 uclassdrvinfof("\tdwMaxVideoFrameSize=%u", pc->dwMaxVideoFrameSize);
169 uclassdrvinfof("\tdwMaxPayloadTransferSize=%u", pc->dwMaxPayloadTransferSize);
170/* uclassdrvinfof("\tdwClockFrequency=%u", pc->dwClockFrequency);
171 uclassdrvinfof("\tbmFramingInfo=%02x", pc->bmFramingInfo);
172 uclassdrvinfof("\tbPreferedVersion=%d, bMinVersion=%d, bMaxVersion=%d",
173 pc->bPreferedVersion, pc->bMinVersion, pc->bMaxVersion); */
174}
175#endif
176
177static void _post(USBHUVCDriver *uvcdp, usbh_urb_t *urb, memory_pool_t *mp, uint16_t type) {
178 usbhuvc_message_base_t *const msg = (usbhuvc_message_base_t *)((uint8_t *)urb->buff - offsetof(usbhuvc_message_data_t, data));
179 msg->timestamp = osalOsGetSystemTimeX();
180
181 usbhuvc_message_base_t *const new_msg = (usbhuvc_message_base_t *)chPoolAllocI(mp);
182 if (new_msg != NULL) {
183 /* allocated the new buffer, now try to post the message to the mailbox */
184 msg_t r = chMBPostI(&uvcdp->mb, (msg_t)msg);
185 if (r == MSG_OK) {
186 /* everything OK, complete the missing fields */
187 msg->type = type;
188 msg->length = urb->actualLength;
189
190 /* change the URB's buffer to the newly allocated one */
191 urb->buff = ((usbhuvc_message_data_t *)new_msg)->data;
192 } else {
193 if (r == MSG_RESET) {
194 uurbwarn("UVC: error, mailbox reset");
195 } else {
196 uurberr("UVC: error, mailbox overrun");
197 }
198 /* couldn't post the message, free the newly allocated buffer */
199 chPoolFreeI(&uvcdp->mp_status, new_msg);
200 }
201 } else {
202 uurberrf("UVC: error, %s pool overrun", mp == &uvcdp->mp_data ? "data" : "status");
203 }
204}
205
206static void _cb_int(usbh_urb_t *urb) {
207 USBHUVCDriver *uvcdp = (USBHUVCDriver *)urb->userData;
208
209 switch (urb->status) {
210 case USBH_URBSTATUS_OK:
211 if (urb->actualLength >= 2) {
212 _post(uvcdp, urb, &uvcdp->mp_status, USBHUVC_MESSAGETYPE_STATUS);
213 } else {
214 uurberrf("UVC: INT IN, actualLength=%d", urb->actualLength);
215 }
216 break;
217 case USBH_URBSTATUS_TIMEOUT: /* the device NAKed */
218 uurbdbg("UVC: INT IN no info");
219 break;
220 case USBH_URBSTATUS_DISCONNECTED:
221 case USBH_URBSTATUS_CANCELLED:
222 uurbwarn("UVC: INT IN status = DISCONNECTED/CANCELLED, aborting");
223 return;
224 default:
225 uurberrf("UVC: INT IN error, unexpected status = %d", urb->status);
226 break;
227 }
228
229 usbhURBObjectResetI(urb);
230 usbhURBSubmitI(urb);
231}
232
233static void _cb_iso(usbh_urb_t *urb) {
234 USBHUVCDriver *uvcdp = (USBHUVCDriver *)urb->userData;
235
236 if ((urb->status == USBH_URBSTATUS_DISCONNECTED)
237 || (urb->status == USBH_URBSTATUS_CANCELLED)) {
238 uurbwarn("UVC: ISO IN status = DISCONNECTED/CANCELLED, aborting");
239 return;
240 }
241
242 if (urb->status != USBH_URBSTATUS_OK) {
243 uurberrf("UVC: ISO IN error, unexpected status = %d", urb->status);
244 } else if (urb->actualLength >= 2) {
245 const uint8_t *const buff = (const uint8_t *)urb->buff;
246 if (buff[0] < 2) {
247 uurberrf("UVC: ISO IN, bHeaderLength=%d", buff[0]);
248 } else if (buff[0] > urb->actualLength) {
249 uurberrf("UVC: ISO IN, bHeaderLength=%d > actualLength=%d", buff[0], urb->actualLength);
250 } else {
251 uurbdbgf("UVC: ISO IN len=%d, hdr=%d, FID=%d, EOF=%d, ERR=%d, EOH=%d",
252 urb->actualLength,
253 buff[0],
254 buff[1] & UVC_HDR_FID,
255 buff[1] & UVC_HDR_EOF,
256 buff[1] & UVC_HDR_ERR,
257 buff[1] & UVC_HDR_EOH);
258
259 if ((urb->actualLength > buff[0])
260 || (buff[1] & (UVC_HDR_EOF | UVC_HDR_ERR))) {
261 _post(uvcdp, urb, &uvcdp->mp_data, USBHUVC_MESSAGETYPE_DATA);
262 } else {
263 uurbdbgf("UVC: ISO IN skip: len=%d, hdr=%d, FID=%d, EOF=%d, ERR=%d, EOH=%d",
264 urb->actualLength,
265 buff[0],
266 buff[1] & UVC_HDR_FID,
267 buff[1] & UVC_HDR_EOF,
268 buff[1] & UVC_HDR_ERR,
269 buff[1] & UVC_HDR_EOH);
270 }
271 }
272 } else if (urb->actualLength > 0) {
273 uurberrf("UVC: ISO IN, actualLength=%d", urb->actualLength);
274 }
275
276 usbhURBObjectResetI(urb);
277 usbhURBSubmitI(urb);
278}
279
280
281bool usbhuvcStreamStart(USBHUVCDriver *uvcdp, uint16_t min_ep_sz) {
282 bool ret = HAL_FAILED;
283
284 osalSysLock();
285 osalDbgCheck(uvcdp && (uvcdp->state != USBHUVC_STATE_UNINITIALIZED) &&
286 (uvcdp->state != USBHUVC_STATE_BUSY));
287 if (uvcdp->state == USBHUVC_STATE_STREAMING) {
288 osalSysUnlock();
289 return HAL_SUCCESS;
290 }
291 if (uvcdp->state != USBHUVC_STATE_READY) {
292 osalSysUnlock();
293 return HAL_FAILED;
294 }
295 uvcdp->state = USBHUVC_STATE_BUSY;
296 osalSysUnlock();
297
298 uint32_t workramsz;
299 const uint8_t *elem;
300 uint32_t datapackets;
301 uint32_t data_sz;
302
303 //set the alternate setting
304 if (_set_vs_alternate(uvcdp, min_ep_sz) != HAL_SUCCESS)
305 goto exit;
306
307 //reserve working RAM
308 data_sz = (uvcdp->ep_iso.wMaxPacketSize + sizeof(usbhuvc_message_data_t) + 3) & ~3;
309 datapackets = HAL_USBHUVC_WORK_RAM_SIZE / data_sz;
310 if (datapackets == 0) {
311 uclassdrverr("Not enough work RAM");
312 goto failed;
313 }
314
315 workramsz = datapackets * data_sz;
316 uclassdrvinfof("Reserving %u bytes of RAM (%d data packets of %d bytes)", workramsz, datapackets, data_sz);
317 if (datapackets > (HAL_USBHUVC_MAX_MAILBOX_SZ - HAL_USBHUVC_STATUS_PACKETS_COUNT)) {
318 uclassdrvwarn("Mailbox may overflow, use a larger HAL_USBHUVC_MAX_MAILBOX_SZ. UVC will under-utilize the assigned work RAM.");
319 }
320 chMBResumeX(&uvcdp->mb);
321
322 uvcdp->mp_data_buffer = chHeapAlloc(NULL, workramsz);
323 if (uvcdp->mp_data_buffer == NULL) {
324 uclassdrverr("Couldn't reserve RAM");
325 goto failed;
326 }
327
328 //initialize the mempool
329 chPoolObjectInit(&uvcdp->mp_data, data_sz, NULL);
330 elem = (const uint8_t *)uvcdp->mp_data_buffer;
331 while (datapackets--) {
332 chPoolFree(&uvcdp->mp_data, (void *)elem);
333 elem += data_sz;
334 }
335
336 //open the endpoint
337 usbhEPOpen(&uvcdp->ep_iso);
338
339 //allocate 1 buffer and submit the first transfer
340 {
341 usbhuvc_message_data_t *const msg = (usbhuvc_message_data_t *)chPoolAlloc(&uvcdp->mp_data);
342 osalDbgCheck(msg);
343 usbhURBObjectInit(&uvcdp->urb_iso, &uvcdp->ep_iso, _cb_iso, uvcdp, msg->data, uvcdp->ep_iso.wMaxPacketSize);
344 }
345
346 usbhURBSubmit(&uvcdp->urb_iso);
347
348 ret = HAL_SUCCESS;
349 goto exit;
350
351failed:
352 _set_vs_alternate(uvcdp, 0);
353 if (uvcdp->mp_data_buffer)
354 chHeapFree(uvcdp->mp_data_buffer);
355
356exit:
357 osalSysLock();
358 if (ret == HAL_SUCCESS)
359 uvcdp->state = USBHUVC_STATE_STREAMING;
360 else
361 uvcdp->state = USBHUVC_STATE_READY;
362 osalSysUnlock();
363 return ret;
364}
365
366bool usbhuvcStreamStop(USBHUVCDriver *uvcdp) {
367 osalSysLock();
368 osalDbgCheck(uvcdp && (uvcdp->state != USBHUVC_STATE_UNINITIALIZED) &&
369 (uvcdp->state != USBHUVC_STATE_BUSY));
370 if (uvcdp->state != USBHUVC_STATE_STREAMING) {
371 osalSysUnlock();
372 return HAL_SUCCESS;
373 }
374 uvcdp->state = USBHUVC_STATE_BUSY;
375
376 //close the ISO endpoint
377 usbhEPCloseS(&uvcdp->ep_iso);
378
379 //purge the mailbox
380 chMBResetI(&uvcdp->mb); //TODO: the status messages are lost!!
381 chMtxLockS(&uvcdp->mtx);
382 osalSysUnlock();
383
384 //free the working memory
385 chHeapFree(uvcdp->mp_data_buffer);
386 uvcdp->mp_data_buffer = 0;
387
388 //set alternate setting to 0
389 _set_vs_alternate(uvcdp, 0);
390
391 osalSysLock();
392 uvcdp->state = USBHUVC_STATE_READY;
393 chMtxUnlockS(&uvcdp->mtx);
394 osalSysUnlock();
395 return HAL_SUCCESS;
396}
397
398bool usbhuvcFindVSDescriptor(USBHUVCDriver *uvcdp,
399 generic_iterator_t *ics,
400 uint8_t bDescriptorSubtype,
401 bool start) {
402
403 if (start)
404 cs_iter_init(ics, (generic_iterator_t *)&uvcdp->ivs);
405 else
406 cs_iter_next(ics);
407
408 for (; ics->valid; cs_iter_next(ics)) {
409 if (ics->curr[1] != UVC_CS_INTERFACE)
410 break;
411 if (ics->curr[2] == bDescriptorSubtype)
412 return HAL_SUCCESS;
413 if (!start)
414 break;
415 }
416 return HAL_FAILED;
417}
418
419void usbhuvcResetPC(USBHUVCDriver *uvcdp) {
420 memset(&uvcdp->pc, 0, sizeof(uvcdp->pc));
421}
422
423bool usbhuvcProbe(USBHUVCDriver *uvcdp) {
424// memset(&uvcdp->pc_min, 0, sizeof(uvcdp->pc_min));
425// memset(&uvcdp->pc_max, 0, sizeof(uvcdp->pc_max));
426
427 if (usbhuvcVSRequest(uvcdp, UVC_SET_CUR, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
428 return HAL_FAILED;
429 if (usbhuvcVSRequest(uvcdp, UVC_GET_CUR, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
430 return HAL_FAILED;
431 if (usbhuvcVSRequest(uvcdp, UVC_GET_MAX, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc_max), (uint8_t *)&uvcdp->pc_max) != HAL_SUCCESS)
432 return HAL_FAILED;
433 if (usbhuvcVSRequest(uvcdp, UVC_GET_MIN, UVC_CTRL_VS_PROBE_CONTROL, sizeof(uvcdp->pc_min), (uint8_t *)&uvcdp->pc_min) != HAL_SUCCESS)
434 return HAL_FAILED;
435 return HAL_SUCCESS;
436}
437
438bool usbhuvcCommit(USBHUVCDriver *uvcdp) {
439 if (usbhuvcVSRequest(uvcdp, UVC_SET_CUR, UVC_CTRL_VS_COMMIT_CONTROL, sizeof(uvcdp->pc), (uint8_t *)&uvcdp->pc) != HAL_SUCCESS)
440 return HAL_FAILED;
441
442 osalSysLock();
443 if (uvcdp->state == USBHUVC_STATE_ACTIVE)
444 uvcdp->state = USBHUVC_STATE_READY;
445 osalSysUnlock();
446 return HAL_SUCCESS;
447}
448
449uint32_t usbhuvcEstimateRequiredEPSize(USBHUVCDriver *uvcdp, const uint8_t *formatdesc,
450 const uint8_t *framedesc, uint32_t dwFrameInterval) {
451
452 osalDbgCheck(framedesc);
453 osalDbgCheck(framedesc[0] > 3);
454 osalDbgCheck(framedesc[1] == UVC_CS_INTERFACE);
455 osalDbgCheck(formatdesc);
456 osalDbgCheck(formatdesc[0] > 3);
457 osalDbgCheck(formatdesc[1] == UVC_CS_INTERFACE);
458
459 uint16_t w, h, div, mul;
460 uint8_t bpp;
461
462 switch (framedesc[2]) {
463 case UVC_VS_FRAME_MJPEG: {
464 const usbh_uvc_frame_mjpeg_t *frame = (const usbh_uvc_frame_mjpeg_t *)framedesc;
465 //const usbh_uvc_format_mjpeg_t *fmt = (const usbh_uvc_format_mjpeg_t *)formatdesc;
466 w = frame->wWidth;
467 h = frame->wHeight;
468 bpp = 16; //TODO: check this!!
469 mul = 1;
470 div = 5; //TODO: check this estimate
471 } break;
472 case UVC_VS_FRAME_UNCOMPRESSED: {
473 const usbh_uvc_frame_uncompressed_t *frame = (const usbh_uvc_frame_uncompressed_t *)framedesc;
474 const usbh_uvc_format_uncompressed *fmt = (const usbh_uvc_format_uncompressed *)formatdesc;
475 w = frame->wWidth;
476 h = frame->wHeight;
477 bpp = fmt->bBitsPerPixel;
478 mul = div = 1;
479 } break;
480 default:
481 uclassdrvwarn("Unsupported format");
482 return 0xffffffff;
483 }
484
485 uint32_t sz = w * h / 8 * bpp;
486 sz *= 10000000UL / dwFrameInterval;
487 sz /= 1000;
488
489 if (uvcdp->dev->speed == USBH_DEVSPEED_HIGH)
490 div *= 8;
491
492 return (sz * mul) / div + 12;
493}
494
495static usbh_baseclassdriver_t *_uvc_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
496
497 USBHUVCDriver *uvcdp;
498 uint8_t i;
499
500 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE_ASSOCIATION,
501 0x0e, 0x03, 0x00) != HAL_SUCCESS)
502 return NULL;
503
504 /* alloc driver */
505 for (i = 0; i < HAL_USBHUVC_MAX_INSTANCES; i++) {
506 if (USBHUVCD[i].dev == NULL) {
507 uvcdp = &USBHUVCD[i];
508 goto alloc_ok;
509 }
510 }
511
512 udevwarn("Can't alloc UVC driver");
513
514 /* can't alloc */
515 return NULL;
516
517alloc_ok:
518 /* initialize the driver's variables */
519 uvcdp->ivc.curr = uvcdp->ivs.curr = NULL;
520
521 usbhEPSetName(&dev->ctrl, "UVC[CTRL]");
522
523 const usbh_ia_descriptor_t *iad = (const usbh_ia_descriptor_t *)descriptor;
524 if_iterator_t iif;
525 generic_iterator_t ics;
526 generic_iterator_t iep;
527
528 iif.iad = iad;
529 iif.curr = descriptor;
530 iif.rem = rem;
531
532 for (if_iter_next(&iif); iif.valid; if_iter_next(&iif)) {
533 if (iif.iad != iad) break;
534
535 const usbh_interface_descriptor_t *const ifdesc = if_get(&iif);
536 if (ifdesc->bInterfaceClass != UVC_CC_VIDEO) {
537 udevwarnf("Skipping Interface %d (class != UVC_CC_VIDEO)",
538 ifdesc->bInterfaceNumber);
539 continue;
540 }
541
542 udevinfof("Interface %d, Alt=%d, Class=UVC_CC_VIDEO, Subclass=%02x",
543 ifdesc->bInterfaceNumber,
544 ifdesc->bAlternateSetting,
545 ifdesc->bInterfaceSubClass);
546
547 switch (ifdesc->bInterfaceSubClass) {
548 case UVC_SC_VIDEOCONTROL:
549 if (uvcdp->ivc.curr == NULL) {
550 uvcdp->ivc = iif;
551 }
552 for (cs_iter_init(&ics, (generic_iterator_t *)&iif); ics.valid; cs_iter_next(&ics)) {
553 if (ics.curr[1] != UVC_CS_INTERFACE) {
554 udevwarnf("Unknown descriptor=%02X", ics.curr[1]);
555 continue;
556 }
557 switch (ics.curr[2]) {
558 case UVC_VC_HEADER:
559 udevinfo(" VC_HEADER"); break;
560 case UVC_VC_INPUT_TERMINAL:
561 udevinfof(" VC_INPUT_TERMINAL, ID=%d", ics.curr[3]); break;
562 case UVC_VC_OUTPUT_TERMINAL:
563 udevinfof(" VC_OUTPUT_TERMINAL, ID=%d", ics.curr[3]); break;
564 case UVC_VC_SELECTOR_UNIT:
565 udevinfof(" VC_SELECTOR_UNIT, ID=%d", ics.curr[3]); break;
566 case UVC_VC_PROCESSING_UNIT:
567 udevinfof(" VC_PROCESSING_UNIT, ID=%d", ics.curr[3]); break;
568 case UVC_VC_EXTENSION_UNIT:
569 udevinfof(" VC_EXTENSION_UNIT, ID=%d", ics.curr[3]); break;
570 default:
571 udevwarnf("Unknown video bDescriptorSubtype=%02x", ics.curr[2]);
572 break;
573 }
574 }
575 break;
576 case UVC_SC_VIDEOSTREAMING:
577 if (uvcdp->ivs.curr == NULL) {
578 uvcdp->ivs = iif;
579 }
580 for (cs_iter_init(&ics, (generic_iterator_t *)&iif); ics.valid; cs_iter_next(&ics)) {
581 if (ics.curr[1] != UVC_CS_INTERFACE) {
582 udevwarnf("Unknown descriptor=%02X", ics.curr[1]);
583 continue;
584 }
585 switch (ics.curr[2]) {
586 case UVC_VS_INPUT_HEADER:
587 udevinfo(" VS_INPUT_HEADER"); break;
588 case UVC_VS_OUTPUT_HEADER:
589 udevinfo(" VS_OUTPUT_HEADER"); break;
590 case UVC_VS_STILL_IMAGE_FRAME:
591 udevinfo(" VS_STILL_IMAGE_FRAME"); break;
592
593 case UVC_VS_FORMAT_UNCOMPRESSED:
594 udevinfof(" VS_FORMAT_UNCOMPRESSED, bFormatIndex=%d", ics.curr[3]); break;
595 case UVC_VS_FORMAT_MPEG2TS:
596 udevinfof(" VS_FORMAT_MPEG2TS, bFormatIndex=%d", ics.curr[3]); break;
597 case UVC_VS_FORMAT_DV:
598 udevinfof(" VS_FORMAT_DV, bFormatIndex=%d", ics.curr[3]); break;
599 case UVC_VS_FORMAT_MJPEG:
600 udevinfof(" VS_FORMAT_MJPEG, bFormatIndex=%d", ics.curr[3]); break;
601 case UVC_VS_FORMAT_FRAME_BASED:
602 udevinfof(" VS_FORMAT_FRAME_BASED, bFormatIndex=%d", ics.curr[3]); break;
603 case UVC_VS_FORMAT_STREAM_BASED:
604 udevinfof(" VS_FORMAT_STREAM_BASED, bFormatIndex=%d", ics.curr[3]); break;
605
606 case UVC_VS_FRAME_UNCOMPRESSED:
607 udevinfof(" VS_FRAME_UNCOMPRESSED, bFrameIndex=%d", ics.curr[3]); break;
608 case UVC_VS_FRAME_MJPEG:
609 udevinfof(" VS_FRAME_MJPEG, bFrameIndex=%d", ics.curr[3]); break;
610 case UVC_VS_FRAME_FRAME_BASED:
611 udevinfof(" VS_FRAME_FRAME_BASED, bFrameIndex=%d", ics.curr[3]); break;
612
613 case UVC_VS_COLOR_FORMAT:
614 udevinfo(" VS_COLOR_FORMAT"); break;
615 default:
616 udevwarnf("Unknown video bDescriptorSubtype=%02x", ics.curr[2]);
617 break;
618 }
619 }
620 break;
621 default:
622 udevwarnf("Unknown video bInterfaceSubClass=%02x", ifdesc->bInterfaceSubClass);
623 break;
624 }
625
626 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
627 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
628
629 if ((ifdesc->bInterfaceSubClass == UVC_SC_VIDEOCONTROL)
630 && ((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_INT)
631 && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
632 /* found VC interrupt endpoint */
633 udevinfof(" VC Interrupt endpoint; %02x, bInterval=%d",
634 epdesc->bEndpointAddress, epdesc->bInterval);
635 usbhEPObjectInit(&uvcdp->ep_int, dev, epdesc);
636 usbhEPSetName(&uvcdp->ep_int, "UVC[INT ]");
637 } else if ((ifdesc->bInterfaceSubClass == UVC_SC_VIDEOSTREAMING)
638 && ((epdesc->bmAttributes & 0x03) == USBH_EPTYPE_ISO)
639 && ((epdesc->bEndpointAddress & 0x80) == USBH_EPDIR_IN)) {
640 /* found VS isochronous endpoint */
641 udevinfof(" VS Isochronous endpoint; %02x, bInterval=%d, bmAttributes=%02x",
642 epdesc->bEndpointAddress, epdesc->bInterval, epdesc->bmAttributes);
643 } else {
644 /* unknown EP */
645 udevwarnf(" <unknown endpoint>, bEndpointAddress=%02x, bmAttributes=%02x",
646 epdesc->bEndpointAddress, epdesc->bmAttributes);
647 }
648
649 for (cs_iter_init(&ics, &iep); ics.valid; cs_iter_next(&ics)) {
650 udevinfof(" CS_ENDPOINT bLength=%d, bDescriptorType=%02X",
651 ics.curr[0], ics.curr[1]);
652 }
653 }
654 }
655
656 if ((uvcdp->ivc.curr == NULL) || (uvcdp->ivs.curr == NULL)) {
657 return NULL;
658 }
659
660// uvcdp->dev = dev;
661
662 _set_vs_alternate(uvcdp, 0);
663
664 /* initialize the INT endpoint */
665 chPoolObjectInit(&uvcdp->mp_status, sizeof(usbhuvc_message_status_t), NULL);
666 for(i = 0; i < HAL_USBHUVC_STATUS_PACKETS_COUNT; i++)
667 chPoolFree(&uvcdp->mp_status, &uvcdp->mp_status_buffer[i]);
668
669 usbhEPOpen(&uvcdp->ep_int);
670
671 usbhuvc_message_status_t *const msg = (usbhuvc_message_status_t *)chPoolAlloc(&uvcdp->mp_status);
672 osalDbgCheck(msg);
673 usbhURBObjectInit(&uvcdp->urb_int, &uvcdp->ep_int, _cb_int, uvcdp, msg->data, USBHUVC_MAX_STATUS_PACKET_SZ);
674 osalSysLock();
675 usbhURBSubmitI(&uvcdp->urb_int);
676 uvcdp->state = USBHUVC_STATE_ACTIVE;
677 osalOsRescheduleS(); /* because of usbhURBSubmitI */
678 osalSysUnlock();
679
680 dev->keepFullCfgDesc++;
681 return (usbh_baseclassdriver_t *)uvcdp;
682}
683
684static void _uvc_unload(usbh_baseclassdriver_t *drv) {
685 USBHUVCDriver *const uvcdp = (USBHUVCDriver *)drv;
686
687 usbhuvcStreamStop(uvcdp);
688
689 usbhEPClose(&uvcdp->ep_int);
690
691 //TODO: free
692
693 if (drv->dev->keepFullCfgDesc)
694 drv->dev->keepFullCfgDesc--;
695
696 osalSysLock();
697 uvcdp->state = USBHUVC_STATE_STOP;
698 osalSysUnlock();
699}
700
701static void _object_init(USBHUVCDriver *uvcdp) {
702 osalDbgCheck(uvcdp != NULL);
703 memset(uvcdp, 0, sizeof(*uvcdp));
704 uvcdp->info = &usbhuvcClassDriverInfo;
705 chMBObjectInit(&uvcdp->mb, uvcdp->mb_buff, HAL_USBHUVC_MAX_MAILBOX_SZ);
706 chMtxObjectInit(&uvcdp->mtx);
707 uvcdp->state = USBHUVC_STATE_STOP;
708}
709
710static void _uvc_init(void) {
711 uint8_t i;
712 for (i = 0; i < HAL_USBHUVC_MAX_INSTANCES; i++) {
713 _object_init(&USBHUVCD[i]);
714 }
715}
716
717#endif
718