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 */
39 /* STM32 USB Library Definitions */
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
;
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
];
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
));
93 memset(usb_hid_dev
, 0, sizeof(struct pios_usb_hid_dev
));
94 usb_hid_dev
->magic
= PIOS_USB_HID_DEV_MAGIC
;
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
) {
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
;
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
);
131 struct pios_usb_hid_dev
*usb_hid_dev
;
133 usb_hid_dev
= (struct pios_usb_hid_dev
*)PIOS_USB_HID_alloc();
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
;
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
) {
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,
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,
179 if (bytes_to_tx
== 0) {
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
),
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
),
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
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
);
215 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
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;
223 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
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
);
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
);
242 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
246 if (GetEPTxStatus(usb_hid_dev
->cfg
->data_tx_ep
) == EP_TX_VALID
) {
247 /* Endpoint is already transmitting */
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
);
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
);
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
);
298 if (!PIOS_USB_CheckAvailable(usb_hid_dev
->lower_id
)) {
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
);
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
),
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
);
336 /* The first byte is report ID (not checked), the second byte is the valid data length */
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,
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],
353 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
354 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 1;
356 uint16_t max_payload_length
= PIOS_USB_BOARD_HID_DATA_LENGTH
- 2;
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
);
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
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 */