update credits
[librepilot.git] / flight / pios / stm32f30x / pios_usb_hid.c
blob4b819cb15ab9a1cffb25a84dfb739376da13117c
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 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
26 * for more details.
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
33 #include "pios.h"
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 */
41 #include "usb_lib.h"
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;
65 uint32_t lower_id;
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];
75 uint32_t rx_dropped;
76 uint32_t rx_oversize;
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));
90 if (!usb_hid_dev) {
91 return NULL;
94 memset(usb_hid_dev, 0, sizeof(struct pios_usb_hid_dev));
95 usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
96 return usb_hid_dev;
98 #else
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) {
106 return NULL;
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;
114 return usb_hid_dev;
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);
130 PIOS_Assert(cfg);
132 struct pios_usb_hid_dev *usb_hid_dev;
134 usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_USB_HID_alloc();
135 if (!usb_hid_dev) {
136 goto out_fail;
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;
151 return 0;
153 out_fail:
154 return -1;
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) {
163 return;
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,
171 NULL,
172 &need_yield);
173 #else
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,
177 NULL,
178 &need_yield);
179 #endif
180 if (bytes_to_tx == 0) {
181 return;
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),
190 bytes_to_tx + 1);
191 #else
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),
195 bytes_to_tx + 2);
196 #endif
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
202 if (need_yield) {
203 vPortYield();
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);
214 PIOS_Assert(valid);
216 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
217 return;
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;
223 #else
224 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
225 #endif
227 PIOS_IRQ_Disable();
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);
232 PIOS_IRQ_Enable();
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);
241 PIOS_Assert(valid);
243 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
244 return;
247 if (GetEPTxStatus(usb_hid_dev->cfg->data_tx_ep) == EP_TX_VALID) {
248 /* Endpoint is already transmitting */
249 return;
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);
261 PIOS_Assert(valid);
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);
277 PIOS_Assert(valid);
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);
297 PIOS_Assert(valid);
299 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
300 return;
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);
315 PIOS_Assert(valid);
317 uint32_t DataLength;
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),
329 DataLength);
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);
334 return;
337 /* The first byte is report ID (not checked), the second byte is the valid data length */
338 uint16_t headroom;
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,
344 &headroom,
345 &need_yield);
346 #else
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],
350 &headroom,
351 &need_yield);
352 #endif
354 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
355 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
356 #else
357 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
358 #endif
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);
363 } else {
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
369 if (need_yield) {
370 vPortYield();
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 */