dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_usb_hid.c
Go to the documentation of this file.
1 
17 /*
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.
22  *
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.
27  *
28  * You should have received a copy of the GNU General Public License along
29  * with this program; if not, see <http://www.gnu.org/licenses/>
30  */
31 
32 /* Project Includes */
33 #include "pios.h"
34 
35 #if defined(PIOS_INCLUDE_USB_HID)
36 
37 #include "pios_usb.h"
38 #include "pios_usb_hid_priv.h"
39 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
40 
41 /* STM32 USB Library Definitions */
42 #include "usb_lib.h"
43 
44 static void PIOS_USB_HID_RegisterTxCallback(uintptr_t usbhid_id, pios_com_callback tx_out_cb, uintptr_t context);
45 static void PIOS_USB_HID_RegisterRxCallback(uintptr_t usbhid_id, pios_com_callback rx_in_cb, uintptr_t context);
46 static void PIOS_USB_HID_TxStart(uintptr_t usbhid_id, uint16_t tx_bytes_avail);
47 static void PIOS_USB_HID_RxStart(uintptr_t usbhid_id, uint16_t rx_bytes_avail);
48 
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_CheckAvailable,
55 };
56 
57 enum pios_usb_hid_dev_magic {
58  PIOS_USB_HID_DEV_MAGIC = 0xAA00BB00,
59 };
60 
61 struct pios_usb_hid_dev {
62  enum pios_usb_hid_dev_magic magic;
63  const struct pios_usb_hid_cfg * cfg;
64 
65  uintptr_t lower_id;
66 
67  pios_com_callback rx_in_cb;
68  uintptr_t rx_in_context;
69  pios_com_callback tx_out_cb;
70  uintptr_t tx_out_context;
71 
72  uint8_t rx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
73  uint8_t tx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
74 
75  uint32_t rx_dropped;
76  uint32_t rx_oversize;
77 };
78 
79 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev * usb_hid_dev)
80 {
81  return (usb_hid_dev->magic == PIOS_USB_HID_DEV_MAGIC);
82 }
83 
84 static struct pios_usb_hid_dev * PIOS_USB_HID_alloc(void)
85 {
86  struct pios_usb_hid_dev * usb_hid_dev;
87 
88  usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_malloc(sizeof(*usb_hid_dev));
89  if (!usb_hid_dev) return(NULL);
90 
91  memset(usb_hid_dev, 0, sizeof(*usb_hid_dev));
92  usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
93  return(usb_hid_dev);
94 }
95 
96 static void PIOS_USB_HID_EP_IN_Callback(void);
97 static void PIOS_USB_HID_EP_OUT_Callback(void);
98 
99 static uintptr_t pios_usb_hid_id;
100 
101 /* Need a better way to pull these in */
102 extern void (*pEpInt_IN[7])(void);
103 extern void (*pEpInt_OUT[7])(void);
104 
105 int32_t PIOS_USB_HID_Init(uintptr_t * usbhid_id, const struct pios_usb_hid_cfg * cfg, uintptr_t lower_id)
106 {
107  PIOS_Assert(usbhid_id);
108  PIOS_Assert(cfg);
109 
110  struct pios_usb_hid_dev * usb_hid_dev;
111 
112  usb_hid_dev = (struct pios_usb_hid_dev *) PIOS_USB_HID_alloc();
113  if (!usb_hid_dev) goto out_fail;
114 
115  /* Bind the configuration to the device instance */
116  usb_hid_dev->cfg = cfg;
117  usb_hid_dev->lower_id = lower_id;
118 
119  pios_usb_hid_id = (uintptr_t) usb_hid_dev;
120 
121  /* Bind lower level callbacks into the USB infrastructure */
122  pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_HID_EP_IN_Callback;
123  pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_HID_EP_OUT_Callback;
124 
125  *usbhid_id = (uintptr_t) usb_hid_dev;
126 
127  return 0;
128 
129 out_fail:
130  return -1;
131 }
132 
133 static void PIOS_USB_HID_SendReport(struct pios_usb_hid_dev * usb_hid_dev)
134 {
135  uint16_t bytes_to_tx;
136 
137  if (!usb_hid_dev->tx_out_cb) {
138  return;
139  }
140 
141  bool need_yield = false;
142 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
143  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
144  &usb_hid_dev->tx_packet_buffer[1],
145  sizeof(usb_hid_dev->tx_packet_buffer)-1,
146  NULL,
147  &need_yield);
148 #else
149  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
150  &usb_hid_dev->tx_packet_buffer[2],
151  sizeof(usb_hid_dev->tx_packet_buffer)-2,
152  NULL,
153  &need_yield);
154 #endif
155  if (bytes_to_tx == 0) {
156  return;
157  }
158 
159  /* Always set type as report ID */
160  usb_hid_dev->tx_packet_buffer[0] = 1;
161 
162 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
163  UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
164  GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
165  bytes_to_tx + 1);
166 #else
167  usb_hid_dev->tx_packet_buffer[1] = bytes_to_tx;
168  UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
169  GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
170  bytes_to_tx + 2);
171 #endif
172  /* Is this correct? Why do we always send the whole buffer? */
173  SetEPTxCount(usb_hid_dev->cfg->data_tx_ep, sizeof(usb_hid_dev->tx_packet_buffer));
174  SetEPTxValid(usb_hid_dev->cfg->data_tx_ep);
175 }
176 
177 static void PIOS_USB_HID_RxStart(uintptr_t usbhid_id, uint16_t rx_bytes_avail) {
178  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
179 
180  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
181  PIOS_Assert(valid);
182 
183  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
184  return;
185  }
186 
187  // If endpoint was stalled and there is now space make it valid
188 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
189  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
190 #else
191  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
192 #endif
193 
195  if ((GetEPRxStatus(usb_hid_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
196  (rx_bytes_avail >= max_payload_length)) {
197  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
198  }
199  PIOS_IRQ_Enable();
200 }
201 
202 static void PIOS_USB_HID_TxStart(uintptr_t usbhid_id, uint16_t tx_bytes_avail)
203 {
204  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
205 
206  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
207  PIOS_Assert(valid);
208 
209  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
210  return;
211  }
212 
213  if (GetEPTxStatus(usb_hid_dev->cfg->data_tx_ep) == EP_TX_VALID) {
214  /* Endpoint is already transmitting */
215  return;
216  }
217 
218  PIOS_USB_HID_SendReport(usb_hid_dev);
219 }
220 
221 static void PIOS_USB_HID_RegisterRxCallback(uintptr_t usbhid_id, pios_com_callback rx_in_cb, uintptr_t context)
222 {
223  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
224 
225  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
226  PIOS_Assert(valid);
227 
228  /*
229  * Order is important in these assignments since ISR uses _cb
230  * field to determine if it's ok to dereference _cb and _context
231  */
232  usb_hid_dev->rx_in_context = context;
233  usb_hid_dev->rx_in_cb = rx_in_cb;
234 }
235 
236 static void PIOS_USB_HID_RegisterTxCallback(uintptr_t usbhid_id, pios_com_callback tx_out_cb, uintptr_t context)
237 {
238  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
239 
240  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
241  PIOS_Assert(valid);
242 
243  /*
244  * Order is important in these assignments since ISR uses _cb
245  * field to determine if it's ok to dereference _cb and _context
246  */
247  usb_hid_dev->tx_out_context = context;
248  usb_hid_dev->tx_out_cb = tx_out_cb;
249 }
250 
255 static void PIOS_USB_HID_EP_IN_Callback(void)
256 {
257  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
258 
259  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
260  PIOS_Assert(valid);
261 
262  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
263  return;
264  }
265 
266  PIOS_USB_HID_SendReport(usb_hid_dev);
267 }
268 
272 static void PIOS_USB_HID_EP_OUT_Callback(void)
273 {
274  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
275 
276  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
277  PIOS_Assert(valid);
278 
279  uint32_t DataLength;
280 
281  /* Read received data (63 bytes) */
282  /* Get the number of received data on the selected Endpoint */
283  DataLength = GetEPRxCount(usb_hid_dev->cfg->data_rx_ep);
284  if (DataLength > sizeof(usb_hid_dev->rx_packet_buffer)) {
285  DataLength = sizeof(usb_hid_dev->rx_packet_buffer);
286  }
287 
288  /* Use the memory interface function to read from the selected endpoint */
289  PMAToUserBufferCopy((uint8_t *) usb_hid_dev->rx_packet_buffer,
290  GetEPRxAddr(usb_hid_dev->cfg->data_rx_ep),
291  DataLength);
292 
293  if (!usb_hid_dev->rx_in_cb) {
294  /* No Rx call back registered, disable the receiver */
295  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
296  return;
297  }
298 
299  /* The first byte is report ID (not checked), the second byte is the valid data length */
300  uint16_t headroom;
301  bool need_yield = false;
302 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
303  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
304  &usb_hid_dev->rx_packet_buffer[1],
305  sizeof(usb_hid_dev->rx_packet_buffer)-1,
306  &headroom,
307  &need_yield);
308 #else
309  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
310  &usb_hid_dev->rx_packet_buffer[2],
311  usb_hid_dev->rx_packet_buffer[1],
312  &headroom,
313  &need_yield);
314 #endif
315 
316 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
317  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
318 #else
319  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
320 #endif
321 
322  if (headroom >= max_payload_length) {
323 
324  /* We have room for a maximum length message */
325  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
326  } else {
327  /* Not enough room left for a message, apply backpressure */
328  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
329  }
330 }
331 
332 #endif /* PIOS_INCLUDE_USB_HID */
void(* pEpInt_IN[7])(void)
int32_t PIOS_IRQ_Enable(void)
Definition: pios_irq.c:53
Main PiOS header to include all the compiled in PiOS options.
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
bool PIOS_USB_CheckAvailable(uintptr_t id)
int32_t PIOS_USB_HID_Init(uintptr_t *usbhid_id, const struct pios_usb_hid_cfg *cfg, uintptr_t lower_id)
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
const struct pios_com_driver pios_usb_hid_com_driver
USB HID layer functions header.
USB COM HID private definitions.
uint32_t magic
int32_t PIOS_IRQ_Disable(void)
Definition: pios_irq.c:40
#define PIOS_USB_BOARD_HID_DATA_LENGTH
#define PIOS_Assert(test)
Definition: pios_debug.h:52
void(* tx_start)(uintptr_t id, uint16_t tx_bytes_avail)
Definition: pios_com.h:45
void(* pEpInt_OUT[7])(void)
uint16_t(* pios_com_callback)(uintptr_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *task_woken)
Definition: pios_com.h:41