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 LibrePilot Project, htpp://www.librepilot.org Copyright (C) 2017.
12 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
13 * @brief USB COM functions (STM32 dependent code)
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #ifdef PIOS_INCLUDE_USB_HID
37 #include "pios_usb_hid_priv.h"
38 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
40 /* STM32 USB Library Definitions */
43 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id
, pios_com_callback tx_out_cb
, uint32_t context
);
44 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id
, pios_com_callback rx_in_cb
, uint32_t context
);
45 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id
, uint16_t tx_bytes_avail
);
46 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id
, uint16_t rx_bytes_avail
);
47 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id
);
49 const struct pios_com_driver pios_usb_hid_com_driver
= {
50 .tx_start
= PIOS_USB_HID_TxStart
,
51 .rx_start
= PIOS_USB_HID_RxStart
,
52 .bind_tx_cb
= PIOS_USB_HID_RegisterTxCallback
,
53 .bind_rx_cb
= PIOS_USB_HID_RegisterRxCallback
,
54 .available
= PIOS_USB_HID_Available
,
57 enum pios_usb_hid_dev_magic
{
58 PIOS_USB_HID_DEV_MAGIC
= 0xAA00BB00,
61 struct pios_usb_hid_dev
{
62 enum pios_usb_hid_dev_magic magic
;
63 const struct pios_usb_hid_cfg
*cfg
;
67 pios_com_callback rx_in_cb
;
68 uint32_t rx_in_context
;
69 pios_com_callback tx_out_cb
;
70 uint32_t tx_out_context
;
72 uint8_t rx_packet_buffer
[PIOS_USB_BOARD_HID_DATA_LENGTH
];
73 uint8_t tx_packet_buffer
[PIOS_USB_BOARD_HID_DATA_LENGTH
];
79 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev
*usb_hid_dev
)
81 return usb_hid_dev
->magic
== PIOS_USB_HID_DEV_MAGIC
;
84 #ifdef PIOS_INCLUDE_FREERTOS
85 static struct pios_usb_hid_dev
*PIOS_USB_HID_alloc(void)
87 struct pios_usb_hid_dev
*usb_hid_dev
;
89 usb_hid_dev
= (struct pios_usb_hid_dev
*)pios_fastheapmalloc(sizeof(struct pios_usb_hid_dev
));
94 memset(usb_hid_dev
, 0, sizeof(struct pios_usb_hid_dev
));
95 usb_hid_dev
->magic
= PIOS_USB_HID_DEV_MAGIC
;
99 static struct pios_usb_hid_dev pios_usb_hid_devs
[PIOS_USB_HID_MAX_DEVS
];
100 static uint8_t pios_usb_hid_num_devs
;
101 static struct pios_usb_hid_dev
*PIOS_USB_HID_alloc(void)
103 struct pios_usb_hid_dev
*usb_hid_dev
;
105 if (pios_usb_hid_num_devs
>= PIOS_USB_HID_MAX_DEVS
) {
109 usb_hid_dev
= &pios_usb_hid_devs
[pios_usb_hid_num_devs
++];
111 memset(usb_hid_dev
, 0, sizeof(struct pios_usb_hid_dev
));
112 usb_hid_dev
->magic
= PIOS_USB_HID_DEV_MAGIC
;
116 #endif /* ifdef PIOS_INCLUDE_FREERTOS */
118 static void PIOS_USB_HID_EP_IN_Callback(void);
119 static void PIOS_USB_HID_EP_OUT_Callback(void);
121 static uint32_t pios_usb_hid_id
;
123 /* Need a better way to pull these in */
124 extern void(*pEpInt_IN
[7]) (void);
125 extern void(*pEpInt_OUT
[7]) (void);
127 int32_t PIOS_USB_HID_Init(uint32_t *usbhid_id
, const struct pios_usb_hid_cfg
*cfg
, uint32_t lower_id
)
129 PIOS_Assert(usbhid_id
);
132 struct pios_usb_hid_dev
*usb_hid_dev
;
134 usb_hid_dev
= (struct pios_usb_hid_dev
*)PIOS_USB_HID_alloc();
139 /* Bind the configuration to the device instance */
140 usb_hid_dev
->cfg
= cfg
;
141 usb_hid_dev
->lower_id
= lower_id
;
143 pios_usb_hid_id
= (uint32_t)usb_hid_dev
;
145 /* Bind lower level callbacks into the USB infrastructure */
146 pEpInt_IN
[cfg
->data_tx_ep
- 1] = PIOS_USB_HID_EP_IN_Callback
;
147 pEpInt_OUT
[cfg
->data_rx_ep
- 1] = PIOS_USB_HID_EP_OUT_Callback
;
149 *usbhid_id
= (uint32_t)usb_hid_dev
;
158 static void PIOS_USB_HID_SendReport(struct pios_usb_hid_dev
*usb_hid_dev
)
160 uint16_t bytes_to_tx
;
162 if (!usb_hid_dev
->tx_out_cb
) {
166 bool need_yield
= false;
167 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
168 bytes_to_tx
= (usb_hid_dev
->tx_out_cb
)(usb_hid_dev
->tx_out_context
,
169 &usb_hid_dev
->tx_packet_buffer
[1],
170 sizeof(usb_hid_dev
->tx_packet_buffer
) - 1,
174 bytes_to_tx
= (usb_hid_dev
->tx_out_cb
)(usb_hid_dev
->tx_out_context
,
175 &usb_hid_dev
->tx_packet_buffer
[2],
176 sizeof(usb_hid_dev
->tx_packet_buffer
) - 2,
180 if (bytes_to_tx
== 0) {
184 /* Always set type as report ID */
185 usb_hid_dev
->tx_packet_buffer
[0] = 1;
187 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
188 UserToPMABufferCopy(usb_hid_dev
->tx_packet_buffer
,
189 GetEPTxAddr(usb_hid_dev
->cfg
->data_tx_ep
),
192 usb_hid_dev
->tx_packet_buffer
[1] = bytes_to_tx
;
193 UserToPMABufferCopy(usb_hid_dev
->tx_packet_buffer
,
194 GetEPTxAddr(usb_hid_dev
->cfg
->data_tx_ep
),
197 /* Is this correct? Why do we always send the whole buffer? */
198 SetEPTxCount(usb_hid_dev
->cfg
->data_tx_ep
, sizeof(usb_hid_dev
->tx_packet_buffer
));
199 SetEPTxValid(usb_hid_dev
->cfg
->data_tx_ep
);
201 #ifdef PIOS_INCLUDE_FREERTOS
205 #endif /* PIOS_INCLUDE_FREERTOS */
208 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id
, uint16_t rx_bytes_avail
)
210 struct pios_usb_hid_dev
*usb_hid_dev
= (struct pios_usb_hid_dev
*)usbhid_id
;
212 bool valid
= PIOS_USB_HID_validate(usb_hid_dev
);
216 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
220 // If endpoint was stalled and there is now space make it valid
221 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
222 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 1;
224 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
228 if ((GetEPRxStatus(usb_hid_dev
->cfg
->data_rx_ep
) != EP_RX_VALID
) &&
229 (rx_bytes_avail
>= max_payload_length
)) {
230 SetEPRxStatus(usb_hid_dev
->cfg
->data_rx_ep
, EP_RX_VALID
);
235 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id
, __attribute__((unused
)) uint16_t tx_bytes_avail
)
237 struct pios_usb_hid_dev
*usb_hid_dev
= (struct pios_usb_hid_dev
*)usbhid_id
;
239 bool valid
= PIOS_USB_HID_validate(usb_hid_dev
);
243 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
247 if (GetEPTxStatus(usb_hid_dev
->cfg
->data_tx_ep
) == EP_TX_VALID
) {
248 /* Endpoint is already transmitting */
252 PIOS_USB_HID_SendReport(usb_hid_dev
);
255 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id
, pios_com_callback rx_in_cb
, uint32_t context
)
257 struct pios_usb_hid_dev
*usb_hid_dev
= (struct pios_usb_hid_dev
*)usbhid_id
;
259 bool valid
= PIOS_USB_HID_validate(usb_hid_dev
);
264 * Order is important in these assignments since ISR uses _cb
265 * field to determine if it's ok to dereference _cb and _context
267 usb_hid_dev
->rx_in_context
= context
;
268 usb_hid_dev
->rx_in_cb
= rx_in_cb
;
271 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id
, pios_com_callback tx_out_cb
, uint32_t context
)
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
);
280 * Order is important in these assignments since ISR uses _cb
281 * field to determine if it's ok to dereference _cb and _context
283 usb_hid_dev
->tx_out_context
= context
;
284 usb_hid_dev
->tx_out_cb
= tx_out_cb
;
288 * @brief Callback used to indicate a transmission from device INto host completed
289 * Checks if any data remains, pads it into HID packet and sends.
291 static void PIOS_USB_HID_EP_IN_Callback(void)
293 struct pios_usb_hid_dev
*usb_hid_dev
= (struct pios_usb_hid_dev
*)pios_usb_hid_id
;
295 bool valid
= PIOS_USB_HID_validate(usb_hid_dev
);
299 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
303 PIOS_USB_HID_SendReport(usb_hid_dev
);
307 * EP1 OUT Callback Routine
309 static void PIOS_USB_HID_EP_OUT_Callback(void)
311 struct pios_usb_hid_dev
*usb_hid_dev
= (struct pios_usb_hid_dev
*)pios_usb_hid_id
;
313 bool valid
= PIOS_USB_HID_validate(usb_hid_dev
);
319 /* Read received data (63 bytes) */
320 /* Get the number of received data on the selected Endpoint */
321 DataLength
= GetEPRxCount(usb_hid_dev
->cfg
->data_rx_ep
);
322 if (DataLength
> sizeof(usb_hid_dev
->rx_packet_buffer
)) {
323 DataLength
= sizeof(usb_hid_dev
->rx_packet_buffer
);
326 /* Use the memory interface function to read from the selected endpoint */
327 PMAToUserBufferCopy((uint8_t *)usb_hid_dev
->rx_packet_buffer
,
328 GetEPRxAddr(usb_hid_dev
->cfg
->data_rx_ep
),
331 if (!usb_hid_dev
->rx_in_cb
) {
332 /* No Rx call back registered, disable the receiver */
333 SetEPRxStatus(usb_hid_dev
->cfg
->data_rx_ep
, EP_RX_NAK
);
337 /* The first byte is report ID (not checked), the second byte is the valid data length */
339 bool need_yield
= false;
340 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
341 (usb_hid_dev
->rx_in_cb
)(usb_hid_dev
->rx_in_context
,
342 &usb_hid_dev
->rx_packet_buffer
[1],
343 sizeof(usb_hid_dev
->rx_packet_buffer
) - 1,
347 (usb_hid_dev
->rx_in_cb
)(usb_hid_dev
->rx_in_context
,
348 &usb_hid_dev
->rx_packet_buffer
[2],
349 usb_hid_dev
->rx_packet_buffer
[1],
354 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
355 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 1;
357 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
360 if (headroom
>= max_payload_length
) {
361 /* We have room for a maximum length message */
362 SetEPRxStatus(usb_hid_dev
->cfg
->data_rx_ep
, EP_RX_VALID
);
364 /* Not enough room left for a message, apply backpressure */
365 SetEPRxStatus(usb_hid_dev
->cfg
->data_rx_ep
, EP_RX_NAK
);
368 #ifdef PIOS_INCLUDE_FREERTOS
372 #endif /* PIOS_INCLUDE_FREERTOS */
375 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id
)
377 return PIOS_USB_CheckAvailable(usbhid_id
) ? COM_AVAILABLE_RXTX
: COM_AVAILABLE_NONE
;
380 #endif /* PIOS_INCLUDE_USB_HID */