2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
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
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
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
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
;
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
;
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
;
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
));
96 memset(usb_hid_dev
, 0, sizeof(struct pios_usb_hid_dev
));
97 usb_hid_dev
->magic
= PIOS_USB_HID_DEV_MAGIC
;
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
) {
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
;
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
);
140 struct pios_usb_hid_dev
*usb_hid_dev
;
142 usb_hid_dev
= (struct pios_usb_hid_dev
*)PIOS_USB_HID_alloc();
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
;
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
) {
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,
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,
206 if (bytes_to_tx
== 0) {
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
));
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
));
230 #if defined(PIOS_INCLUDE_FREERTOS)
234 #endif /* PIOS_INCLUDE_FREERTOS */
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
);
247 /* Make sure this USB interface has been initialized */
248 if (!usb_hid_dev
->usb_if_enabled
) {
252 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
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;
260 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
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
);
279 /* Make sure this USB interface has been initialized */
280 if (!usb_hid_dev
->usb_if_enabled
) {
284 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
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
);
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
);
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
)) {
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
)) {
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
{
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
)) {
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
) {
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
));
399 case USB_DESC_TYPE_HID
:
400 PIOS_USBHOOK_CtrlTx(hid_desc
.descriptor
,
401 MIN(hid_desc
.length
, req
->wLength
));
404 /* Unhandled descriptor request */
410 case USB_REQ_GET_INTERFACE
:
411 PIOS_USBHOOK_CtrlTx(&hid_altset
, 1);
413 case USB_REQ_SET_INTERFACE
:
414 hid_altset
= (uint8_t)(req
->wValue
);
417 /* Unhandled standard request */
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
);
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;
433 case USB_HID_REQ_GET_PROTOCOL
:
434 PIOS_USBHOOK_CtrlTx(&hid_protocol
, 1);
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
));
444 /* Unhandled class request */
451 /* Unhandled request */
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 */
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
)) {
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 */
481 /* Nothing new sent, transmitter is now inactive */
482 usb_hid_dev
->tx_active
= 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
)) {
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;
507 READ_MEMORY_BARRIER();
508 /* The first byte is report ID (not checked), the second byte is the valid data length */
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],
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],
525 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
526 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 1;
528 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
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
));
539 /* Not enough room left for a message, apply backpressure */
540 usb_hid_dev
->rx_active
= false;
544 #if defined(PIOS_INCLUDE_FREERTOS)
548 #endif /* PIOS_INCLUDE_FREERTOS */
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 */