LP-602 significant change to USB layer. force complete USB stack reset on replug...
[librepilot.git] / flight / pios / stm32f4xx / pios_usb_hid.c
blob0d1e7928cafc2c372a8ab1991e6de17f0d2c8431
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_USB_HID USB COM Functions
6 * @brief PIOS USB COM implementation for HID interfaces
7 * @notes This implements serial emulation over HID reports
8 * @{
10 * @file pios_usb_hid.c
11 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
12 * @brief USB COM functions (STM32 dependent code)
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <pios.h>
34 #ifdef PIOS_INCLUDE_USB_HID
36 #include <pios_usb_hid_priv.h>
37 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
38 #include <pios_usbhook.h> /* PIOS_USBHOOK_* */
39 #include <pios_delay.h>
40 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id, pios_com_callback tx_out_cb, uint32_t context);
41 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id, pios_com_callback rx_in_cb, uint32_t context);
42 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id, uint16_t tx_bytes_avail);
43 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id, uint16_t rx_bytes_avail);
44 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id);
46 const struct pios_com_driver pios_usb_hid_com_driver = {
47 .tx_start = PIOS_USB_HID_TxStart,
48 .rx_start = PIOS_USB_HID_RxStart,
49 .bind_tx_cb = PIOS_USB_HID_RegisterTxCallback,
50 .bind_rx_cb = PIOS_USB_HID_RegisterRxCallback,
51 .available = PIOS_USB_HID_Available,
54 enum pios_usb_hid_dev_magic {
55 PIOS_USB_HID_DEV_MAGIC = 0xAA00BB00,
58 struct pios_usb_hid_dev {
59 enum pios_usb_hid_dev_magic magic;
60 const struct pios_usb_hid_cfg *cfg;
62 uint32_t lower_id;
64 pios_com_callback rx_in_cb;
65 uint32_t rx_in_context;
66 pios_com_callback tx_out_cb;
67 uint32_t tx_out_context;
69 bool usb_if_enabled;
71 uint8_t rx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH] __attribute__((aligned(4)));
72 volatile bool rx_active;
74 uint8_t tx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH] __attribute__((aligned(4)));
75 volatile bool tx_active;
77 uint32_t rx_dropped;
78 uint32_t rx_oversize;
81 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev *usb_hid_dev)
83 return usb_hid_dev && (usb_hid_dev->magic == PIOS_USB_HID_DEV_MAGIC);
86 #if defined(PIOS_INCLUDE_FREERTOS)
87 static struct pios_usb_hid_dev *PIOS_USB_HID_alloc(void)
89 struct pios_usb_hid_dev *usb_hid_dev;
91 usb_hid_dev = (struct pios_usb_hid_dev *)pios_malloc(sizeof(struct pios_usb_hid_dev));
92 if (!usb_hid_dev) {
93 return NULL;
96 memset(usb_hid_dev, 0, sizeof(struct pios_usb_hid_dev));
97 usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
98 return usb_hid_dev;
100 #else
101 static struct pios_usb_hid_dev pios_usb_hid_devs[PIOS_USB_HID_MAX_DEVS];
102 static uint8_t pios_usb_hid_num_devs;
103 static struct pios_usb_hid_dev *PIOS_USB_HID_alloc(void)
105 struct pios_usb_hid_dev *usb_hid_dev;
107 if (pios_usb_hid_num_devs >= PIOS_USB_HID_MAX_DEVS) {
108 return NULL;
111 usb_hid_dev = &pios_usb_hid_devs[pios_usb_hid_num_devs++];
113 memset(usb_hid_dev, 0, sizeof(struct pios_usb_hid_dev));
114 usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
116 return usb_hid_dev;
118 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
120 static void PIOS_USB_HID_IF_Init(uint32_t usb_hid_id);
121 static void PIOS_USB_HID_IF_DeInit(uint32_t usb_hid_id);
122 static bool PIOS_USB_HID_IF_Setup(uint32_t usb_hid_id, struct usb_setup_request *req);
123 static void PIOS_USB_HID_IF_CtrlDataOut(uint32_t usb_hid_id, const struct usb_setup_request *req);
125 static struct pios_usb_ifops usb_hid_ifops = {
126 .init = PIOS_USB_HID_IF_Init,
127 .deinit = PIOS_USB_HID_IF_DeInit,
128 .setup = PIOS_USB_HID_IF_Setup,
129 .ctrl_data_out = PIOS_USB_HID_IF_CtrlDataOut,
132 static bool PIOS_USB_HID_EP_IN_Callback(uint32_t usb_hid_id, uint8_t epnum, uint16_t len);
133 static bool PIOS_USB_HID_EP_OUT_Callback(uint32_t usb_hid_id, uint8_t epnum, uint16_t len);
135 int32_t PIOS_USB_HID_Init(uint32_t *usbhid_id, const struct pios_usb_hid_cfg *cfg, uint32_t lower_id)
137 PIOS_Assert(usbhid_id);
138 PIOS_Assert(cfg);
140 struct pios_usb_hid_dev *usb_hid_dev;
142 usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_USB_HID_alloc();
143 if (!usb_hid_dev) {
144 goto out_fail;
147 /* Bind the configuration to the device instance */
148 usb_hid_dev->cfg = cfg;
149 usb_hid_dev->lower_id = lower_id;
151 /* Rx and Tx are not active yet */
152 usb_hid_dev->rx_active = false;
153 usb_hid_dev->tx_active = false;
155 /* Register class specific interface callbacks with the USBHOOK layer */
156 usb_hid_dev->usb_if_enabled = false;
157 PIOS_USBHOOK_RegisterIfOps(cfg->data_if, &usb_hid_ifops, (uint32_t)usb_hid_dev);
159 *usbhid_id = (uint32_t)usb_hid_dev;
161 return 0;
163 out_fail:
164 return -1;
168 static struct pios_usbhook_descriptor hid_desc;
170 void PIOS_USB_HID_RegisterHidDescriptor(const uint8_t *desc, uint16_t length)
172 hid_desc.descriptor = desc;
173 hid_desc.length = length;
176 static struct pios_usbhook_descriptor hid_report_desc;
178 void PIOS_USB_HID_RegisterHidReport(const uint8_t *desc, uint16_t length)
180 hid_report_desc.descriptor = desc;
181 hid_report_desc.length = length;
184 static bool PIOS_USB_HID_SendReport(struct pios_usb_hid_dev *usb_hid_dev)
186 uint16_t bytes_to_tx;
188 if (!usb_hid_dev->tx_out_cb) {
189 return false;
191 READ_MEMORY_BARRIER();
192 bool need_yield = false;
193 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
194 bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
195 &usb_hid_dev->tx_packet_buffer[1],
196 sizeof(usb_hid_dev->tx_packet_buffer) - 1,
197 NULL,
198 &need_yield);
199 #else
200 bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
201 &usb_hid_dev->tx_packet_buffer[2],
202 sizeof(usb_hid_dev->tx_packet_buffer) - 2,
203 NULL,
204 &need_yield);
205 #endif
206 if (bytes_to_tx == 0) {
207 return false;
211 * Mark this endpoint as being tx active _before_ actually transmitting
212 * to make sure we don't race with the Tx completion interrupt
214 usb_hid_dev->tx_active = true;
216 /* Always set type as report ID */
217 usb_hid_dev->tx_packet_buffer[0] = 1;
219 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
220 PIOS_USBHOOK_EndpointTx(usb_hid_dev->cfg->data_tx_ep,
221 usb_hid_dev->tx_packet_buffer,
222 sizeof(usb_hid_dev->tx_packet_buffer));
223 #else
224 usb_hid_dev->tx_packet_buffer[1] = bytes_to_tx;
225 PIOS_USBHOOK_EndpointTx(usb_hid_dev->cfg->data_tx_ep,
226 usb_hid_dev->tx_packet_buffer,
227 sizeof(usb_hid_dev->tx_packet_buffer));
228 #endif
230 #if defined(PIOS_INCLUDE_FREERTOS)
231 if (need_yield) {
232 vPortYield();
234 #endif /* PIOS_INCLUDE_FREERTOS */
236 return true;
239 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id, uint16_t rx_bytes_avail)
241 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
243 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
245 PIOS_Assert(valid);
247 /* Make sure this USB interface has been initialized */
248 if (!usb_hid_dev->usb_if_enabled) {
249 return;
252 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
253 return;
256 // If endpoint was stalled and there is now space make it valid
257 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
258 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
259 #else
260 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
261 #endif
263 if (!usb_hid_dev->rx_active && (rx_bytes_avail >= max_payload_length)) {
264 PIOS_USBHOOK_EndpointRx(usb_hid_dev->cfg->data_rx_ep,
265 usb_hid_dev->rx_packet_buffer,
266 sizeof(usb_hid_dev->rx_packet_buffer));
267 usb_hid_dev->rx_active = true;
271 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id, __attribute__((unused)) uint16_t tx_bytes_avail)
273 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
275 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
277 PIOS_Assert(valid);
279 /* Make sure this USB interface has been initialized */
280 if (!usb_hid_dev->usb_if_enabled) {
281 return;
284 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
285 return;
288 if (!usb_hid_dev->tx_active) {
289 /* Transmitter is not currently active, send a report */
290 PIOS_USB_HID_SendReport(usb_hid_dev);
294 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id, pios_com_callback rx_in_cb, uint32_t context)
296 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
298 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
300 PIOS_Assert(valid);
303 * Order is important in these assignments since ISR uses _cb
304 * field to determine if it's ok to dereference _cb and _context
306 usb_hid_dev->rx_in_context = context;
307 WRITE_MEMORY_BARRIER();
308 usb_hid_dev->rx_in_cb = rx_in_cb;
311 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id, pios_com_callback tx_out_cb, uint32_t context)
313 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
315 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
317 PIOS_Assert(valid);
320 * Order is important in these assignments since ISR uses _cb
321 * field to determine if it's ok to dereference _cb and _context
323 usb_hid_dev->tx_out_context = context;
324 WRITE_MEMORY_BARRIER();
325 usb_hid_dev->tx_out_cb = tx_out_cb;
328 static void PIOS_USB_HID_IF_Init(uint32_t usb_hid_id)
330 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
332 if (!PIOS_USB_HID_validate(usb_hid_dev)) {
333 return;
336 /* Register endpoint specific callbacks with the USBHOOK layer */
337 PIOS_USBHOOK_RegisterEpInCallback(usb_hid_dev->cfg->data_tx_ep,
338 sizeof(usb_hid_dev->tx_packet_buffer),
339 PIOS_USB_HID_EP_IN_Callback,
340 (uint32_t)usb_hid_dev);
341 PIOS_USBHOOK_RegisterEpOutCallback(usb_hid_dev->cfg->data_rx_ep,
342 sizeof(usb_hid_dev->rx_packet_buffer),
343 PIOS_USB_HID_EP_OUT_Callback,
344 (uint32_t)usb_hid_dev);
345 usb_hid_dev->usb_if_enabled = true;
346 usb_hid_dev->tx_active = false;
347 usb_hid_dev->rx_active = false;
350 static void PIOS_USB_HID_IF_DeInit(uint32_t usb_hid_id)
352 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
354 if (!PIOS_USB_HID_validate(usb_hid_dev)) {
355 return;
358 /* DeRegister endpoint specific callbacks with the USBHOOK layer */
359 usb_hid_dev->usb_if_enabled = false;
360 usb_hid_dev->tx_active = false;
361 usb_hid_dev->rx_active = false;
362 PIOS_USBHOOK_DeRegisterEpInCallback(usb_hid_dev->cfg->data_tx_ep);
363 PIOS_USBHOOK_DeRegisterEpOutCallback(usb_hid_dev->cfg->data_rx_ep);
366 static uint8_t hid_protocol;
367 static uint8_t hid_altset;
369 struct hid_idle_msg {
370 uint8_t idle_period;
371 uint8_t report_id;
373 static struct hid_idle_msg hid_idle;
374 static uint8_t dummy_report[2];
376 static bool PIOS_USB_HID_IF_Setup(uint32_t usb_hid_id, struct usb_setup_request *req)
378 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
380 if (!PIOS_USB_HID_validate(usb_hid_dev)) {
381 return false;
384 /* Make sure this is a request for an interface we know about */
385 uint8_t ifnum = req->wIndex & 0xFF;
386 if (ifnum != usb_hid_dev->cfg->data_if) {
387 return false;
390 switch (req->bmRequestType & (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)) {
391 case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_INTERFACE):
392 switch (req->bRequest) {
393 case USB_REQ_GET_DESCRIPTOR:
394 switch (req->wValue >> 8) {
395 case USB_DESC_TYPE_REPORT:
396 PIOS_USBHOOK_CtrlTx(hid_report_desc.descriptor,
397 MIN(hid_report_desc.length, req->wLength));
398 break;
399 case USB_DESC_TYPE_HID:
400 PIOS_USBHOOK_CtrlTx(hid_desc.descriptor,
401 MIN(hid_desc.length, req->wLength));
402 break;
403 default:
404 /* Unhandled descriptor request */
405 return false;
407 break;
409 break;
410 case USB_REQ_GET_INTERFACE:
411 PIOS_USBHOOK_CtrlTx(&hid_altset, 1);
412 break;
413 case USB_REQ_SET_INTERFACE:
414 hid_altset = (uint8_t)(req->wValue);
415 break;
416 default:
417 /* Unhandled standard request */
418 return false;
420 break;
422 break;
423 case (USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE):
424 switch (req->bRequest) {
425 case USB_HID_REQ_SET_PROTOCOL:
426 hid_protocol = (uint8_t)(req->wValue);
427 break;
428 case USB_HID_REQ_SET_IDLE:
429 /* Idle rates are currently ignored but decoded for debugging */
430 hid_idle.idle_period = req->wValue & 0xFF00 >> 8;
431 hid_idle.report_id = req->wValue & 0x00FF;
432 break;
433 case USB_HID_REQ_GET_PROTOCOL:
434 PIOS_USBHOOK_CtrlTx(&hid_protocol, 1);
435 break;
436 case USB_HID_REQ_GET_REPORT:
437 /* Give back a dummy input report */
438 dummy_report[0] = req->wValue & 0xFF; /* Report ID */
439 dummy_report[1] = 0x00; /* dummy value */
440 PIOS_USBHOOK_CtrlTx(dummy_report,
441 MIN(sizeof(dummy_report), req->wLength));
442 break;
443 default:
444 /* Unhandled class request */
445 return false;
447 break;
449 break;
450 default:
451 /* Unhandled request */
452 return false;
455 return true;
458 static void PIOS_USB_HID_IF_CtrlDataOut(__attribute__((unused)) uint32_t usb_hid_id, __attribute__((unused)) const struct usb_setup_request *req)
460 /* HID devices don't have any OUT data stages on the control endpoint */
461 PIOS_Assert(0);
465 * @brief Callback used to indicate a transmission from device INto host completed
466 * Checks if any data remains, pads it into HID packet and sends.
468 static bool PIOS_USB_HID_EP_IN_Callback(uint32_t usb_hid_id, __attribute__((unused)) uint8_t epnum, __attribute__((unused)) uint16_t len)
470 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
472 if (!PIOS_USB_HID_validate(usb_hid_dev)) {
473 return false;
476 if (PIOS_USB_CheckAvailable(usb_hid_dev->lower_id) &&
477 PIOS_USB_HID_SendReport(usb_hid_dev)) {
478 /* More data has been queued, leave tx_active set to true */
479 return true;
480 } else {
481 /* Nothing new sent, transmitter is now inactive */
482 usb_hid_dev->tx_active = false;
483 return false;
488 * EP1 OUT Callback Routine
490 static bool PIOS_USB_HID_EP_OUT_Callback(uint32_t usb_hid_id, __attribute__((unused)) uint8_t epnum, uint16_t len)
492 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
494 if (!PIOS_USB_HID_validate(usb_hid_dev)) {
495 return false;
498 if (len > sizeof(usb_hid_dev->rx_packet_buffer)) {
499 len = sizeof(usb_hid_dev->rx_packet_buffer);
502 if (!usb_hid_dev->rx_in_cb) {
503 /* No Rx call back registered, disable the receiver */
504 usb_hid_dev->rx_active = false;
505 return false;
507 READ_MEMORY_BARRIER();
508 /* The first byte is report ID (not checked), the second byte is the valid data length */
509 uint16_t headroom;
510 bool need_yield = false;
511 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
512 (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
513 &usb_hid_dev->rx_packet_buffer[1],
514 len - 1,
515 &headroom,
516 &need_yield);
517 #else
518 (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
519 &usb_hid_dev->rx_packet_buffer[2],
520 usb_hid_dev->rx_packet_buffer[1],
521 &headroom,
522 &need_yield);
523 #endif
525 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
526 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
527 #else
528 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
529 #endif
531 bool rc;
532 if (headroom >= max_payload_length) {
533 /* We have room for a maximum length message */
534 PIOS_USBHOOK_EndpointRx(usb_hid_dev->cfg->data_rx_ep,
535 usb_hid_dev->rx_packet_buffer,
536 sizeof(usb_hid_dev->rx_packet_buffer));
537 rc = true;
538 } else {
539 /* Not enough room left for a message, apply backpressure */
540 usb_hid_dev->rx_active = false;
541 rc = false;
544 #if defined(PIOS_INCLUDE_FREERTOS)
545 if (need_yield) {
546 vPortYield();
548 #endif /* PIOS_INCLUDE_FREERTOS */
550 return rc;
553 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id)
555 return PIOS_USB_CheckAvailable(usbhid_id) ? COM_AVAILABLE_RXTX : COM_AVAILABLE_NONE;
558 #endif /* PIOS_INCLUDE_USB_HID */