update credits
[librepilot.git] / flight / pios / stm32f10x / pios_usb_hid.c
blob6b1e9d81bc5037137fd012234ed0146113542ee6
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 */
39 /* STM32 USB Library Definitions */
40 #include "usb_lib.h"
42 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id, pios_com_callback tx_out_cb, uint32_t context);
43 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id, pios_com_callback rx_in_cb, uint32_t context);
44 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id, uint16_t tx_bytes_avail);
45 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id, uint16_t rx_bytes_avail);
46 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id);
48 const struct pios_com_driver pios_usb_hid_com_driver = {
49 .tx_start = PIOS_USB_HID_TxStart,
50 .rx_start = PIOS_USB_HID_RxStart,
51 .bind_tx_cb = PIOS_USB_HID_RegisterTxCallback,
52 .bind_rx_cb = PIOS_USB_HID_RegisterRxCallback,
53 .available = PIOS_USB_HID_Available,
56 enum pios_usb_hid_dev_magic {
57 PIOS_USB_HID_DEV_MAGIC = 0xAA00BB00,
60 struct pios_usb_hid_dev {
61 enum pios_usb_hid_dev_magic magic;
62 const struct pios_usb_hid_cfg *cfg;
64 uint32_t lower_id;
66 pios_com_callback rx_in_cb;
67 uint32_t rx_in_context;
68 pios_com_callback tx_out_cb;
69 uint32_t tx_out_context;
71 uint8_t rx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
72 uint8_t tx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
74 uint32_t rx_dropped;
75 uint32_t rx_oversize;
78 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev *usb_hid_dev)
80 return usb_hid_dev->magic == PIOS_USB_HID_DEV_MAGIC;
83 #ifdef PIOS_INCLUDE_FREERTOS
84 static struct pios_usb_hid_dev *PIOS_USB_HID_alloc(void)
86 struct pios_usb_hid_dev *usb_hid_dev;
88 usb_hid_dev = (struct pios_usb_hid_dev *)pios_malloc(sizeof(struct pios_usb_hid_dev));
89 if (!usb_hid_dev) {
90 return NULL;
93 memset(usb_hid_dev, 0, sizeof(struct pios_usb_hid_dev));
94 usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
95 return usb_hid_dev;
97 #else
98 static struct pios_usb_hid_dev pios_usb_hid_devs[PIOS_USB_HID_MAX_DEVS];
99 static uint8_t pios_usb_hid_num_devs;
100 static struct pios_usb_hid_dev *PIOS_USB_HID_alloc(void)
102 struct pios_usb_hid_dev *usb_hid_dev;
104 if (pios_usb_hid_num_devs >= PIOS_USB_HID_MAX_DEVS) {
105 return NULL;
108 usb_hid_dev = &pios_usb_hid_devs[pios_usb_hid_num_devs++];
110 memset(usb_hid_dev, 0, sizeof(struct pios_usb_hid_dev));
111 usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
113 return usb_hid_dev;
115 #endif /* ifdef PIOS_INCLUDE_FREERTOS */
117 static void PIOS_USB_HID_EP_IN_Callback(void);
118 static void PIOS_USB_HID_EP_OUT_Callback(void);
120 static uint32_t pios_usb_hid_id;
122 /* Need a better way to pull these in */
123 extern void(*pEpInt_IN[7]) (void);
124 extern void(*pEpInt_OUT[7]) (void);
126 int32_t PIOS_USB_HID_Init(uint32_t *usbhid_id, const struct pios_usb_hid_cfg *cfg, uint32_t lower_id)
128 PIOS_Assert(usbhid_id);
129 PIOS_Assert(cfg);
131 struct pios_usb_hid_dev *usb_hid_dev;
133 usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_USB_HID_alloc();
134 if (!usb_hid_dev) {
135 goto out_fail;
138 /* Bind the configuration to the device instance */
139 usb_hid_dev->cfg = cfg;
140 usb_hid_dev->lower_id = lower_id;
142 pios_usb_hid_id = (uint32_t)usb_hid_dev;
144 /* Bind lower level callbacks into the USB infrastructure */
145 pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_HID_EP_IN_Callback;
146 pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_HID_EP_OUT_Callback;
148 *usbhid_id = (uint32_t)usb_hid_dev;
150 return 0;
152 out_fail:
153 return -1;
157 static void PIOS_USB_HID_SendReport(struct pios_usb_hid_dev *usb_hid_dev)
159 uint16_t bytes_to_tx;
161 if (!usb_hid_dev->tx_out_cb) {
162 return;
165 bool need_yield = false;
166 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
167 bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
168 &usb_hid_dev->tx_packet_buffer[1],
169 sizeof(usb_hid_dev->tx_packet_buffer) - 1,
170 NULL,
171 &need_yield);
172 #else
173 bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
174 &usb_hid_dev->tx_packet_buffer[2],
175 sizeof(usb_hid_dev->tx_packet_buffer) - 2,
176 NULL,
177 &need_yield);
178 #endif
179 if (bytes_to_tx == 0) {
180 return;
183 /* Always set type as report ID */
184 usb_hid_dev->tx_packet_buffer[0] = 1;
186 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
187 UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
188 GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
189 bytes_to_tx + 1);
190 #else
191 usb_hid_dev->tx_packet_buffer[1] = bytes_to_tx;
192 UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
193 GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
194 bytes_to_tx + 2);
195 #endif
196 /* Is this correct? Why do we always send the whole buffer? */
197 SetEPTxCount(usb_hid_dev->cfg->data_tx_ep, sizeof(usb_hid_dev->tx_packet_buffer));
198 SetEPTxValid(usb_hid_dev->cfg->data_tx_ep);
200 #ifdef PIOS_INCLUDE_FREERTOS
201 if (need_yield) {
202 vPortYield();
204 #endif /* PIOS_INCLUDE_FREERTOS */
207 static void PIOS_USB_HID_RxStart(uint32_t usbhid_id, uint16_t rx_bytes_avail)
209 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
211 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
213 PIOS_Assert(valid);
215 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
216 return;
219 // If endpoint was stalled and there is now space make it valid
220 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
221 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
222 #else
223 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
224 #endif
226 PIOS_IRQ_Disable();
227 if ((GetEPRxStatus(usb_hid_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
228 (rx_bytes_avail >= max_payload_length)) {
229 SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
231 PIOS_IRQ_Enable();
234 static void PIOS_USB_HID_TxStart(uint32_t usbhid_id, __attribute__((unused)) uint16_t tx_bytes_avail)
236 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
238 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
240 PIOS_Assert(valid);
242 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
243 return;
246 if (GetEPTxStatus(usb_hid_dev->cfg->data_tx_ep) == EP_TX_VALID) {
247 /* Endpoint is already transmitting */
248 return;
251 PIOS_USB_HID_SendReport(usb_hid_dev);
254 static void PIOS_USB_HID_RegisterRxCallback(uint32_t usbhid_id, pios_com_callback rx_in_cb, uint32_t context)
256 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
258 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
260 PIOS_Assert(valid);
263 * Order is important in these assignments since ISR uses _cb
264 * field to determine if it's ok to dereference _cb and _context
266 usb_hid_dev->rx_in_context = context;
267 usb_hid_dev->rx_in_cb = rx_in_cb;
270 static void PIOS_USB_HID_RegisterTxCallback(uint32_t usbhid_id, pios_com_callback tx_out_cb, uint32_t context)
272 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
274 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
276 PIOS_Assert(valid);
279 * Order is important in these assignments since ISR uses _cb
280 * field to determine if it's ok to dereference _cb and _context
282 usb_hid_dev->tx_out_context = context;
283 usb_hid_dev->tx_out_cb = tx_out_cb;
287 * @brief Callback used to indicate a transmission from device INto host completed
288 * Checks if any data remains, pads it into HID packet and sends.
290 static void PIOS_USB_HID_EP_IN_Callback(void)
292 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
294 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
296 PIOS_Assert(valid);
298 if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
299 return;
302 PIOS_USB_HID_SendReport(usb_hid_dev);
306 * EP1 OUT Callback Routine
308 static void PIOS_USB_HID_EP_OUT_Callback(void)
310 struct pios_usb_hid_dev *usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
312 bool valid = PIOS_USB_HID_validate(usb_hid_dev);
314 PIOS_Assert(valid);
316 uint32_t DataLength;
318 /* Read received data (63 bytes) */
319 /* Get the number of received data on the selected Endpoint */
320 DataLength = GetEPRxCount(usb_hid_dev->cfg->data_rx_ep);
321 if (DataLength > sizeof(usb_hid_dev->rx_packet_buffer)) {
322 DataLength = sizeof(usb_hid_dev->rx_packet_buffer);
325 /* Use the memory interface function to read from the selected endpoint */
326 PMAToUserBufferCopy((uint8_t *)usb_hid_dev->rx_packet_buffer,
327 GetEPRxAddr(usb_hid_dev->cfg->data_rx_ep),
328 DataLength);
330 if (!usb_hid_dev->rx_in_cb) {
331 /* No Rx call back registered, disable the receiver */
332 SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
333 return;
336 /* The first byte is report ID (not checked), the second byte is the valid data length */
337 uint16_t headroom;
338 bool need_yield = false;
339 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
340 (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
341 &usb_hid_dev->rx_packet_buffer[1],
342 sizeof(usb_hid_dev->rx_packet_buffer) - 1,
343 &headroom,
344 &need_yield);
345 #else
346 (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
347 &usb_hid_dev->rx_packet_buffer[2],
348 usb_hid_dev->rx_packet_buffer[1],
349 &headroom,
350 &need_yield);
351 #endif
353 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
354 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
355 #else
356 uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
357 #endif
359 if (headroom >= max_payload_length) {
360 /* We have room for a maximum length message */
361 SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
362 } else {
363 /* Not enough room left for a message, apply backpressure */
364 SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
367 #ifdef PIOS_INCLUDE_FREERTOS
368 if (need_yield) {
369 vPortYield();
371 #endif /* PIOS_INCLUDE_FREERTOS */
374 static uint32_t PIOS_USB_HID_Available(uint32_t usbhid_id)
376 return PIOS_USB_CheckAvailable(usbhid_id) ? COM_AVAILABLE_RXTX : COM_AVAILABLE_NONE;
379 #endif /* PIOS_INCLUDE_USB_HID */