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 #include "pios_usbhook.h" /* PIOS_USBHOOK_* */
41 
42 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
43 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
44 
45 static void PIOS_USB_HID_RegisterTxCallback(uintptr_t usbhid_id, pios_com_callback tx_out_cb, uintptr_t context);
46 static void PIOS_USB_HID_RegisterRxCallback(uintptr_t usbhid_id, pios_com_callback rx_in_cb, uintptr_t context);
47 static void PIOS_USB_HID_TxStart(uintptr_t usbhid_id, uint16_t tx_bytes_avail);
48 static void PIOS_USB_HID_RxStart(uintptr_t usbhid_id, uint16_t rx_bytes_avail);
49 
51  .tx_start = PIOS_USB_HID_TxStart,
52  .rx_start = PIOS_USB_HID_RxStart,
53  .bind_tx_cb = PIOS_USB_HID_RegisterTxCallback,
54  .bind_rx_cb = PIOS_USB_HID_RegisterRxCallback,
55  .available = PIOS_USB_CheckAvailable,
56 };
57 
58 enum pios_usb_hid_dev_magic {
59  PIOS_USB_HID_DEV_MAGIC = 0xAA00BB00,
60 };
61 
62 struct pios_usb_hid_dev {
63  enum pios_usb_hid_dev_magic magic;
64  const struct pios_usb_hid_cfg * cfg;
65 
66  uintptr_t lower_id;
67 
68  pios_com_callback rx_in_cb;
69  uintptr_t rx_in_context;
70  pios_com_callback tx_out_cb;
71  uintptr_t tx_out_context;
72 
73  bool usb_if_enabled;
74 
75  uint8_t rx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH] __attribute__ ((aligned(4)));
76  bool rx_active;
77 
78  uint8_t tx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH] __attribute__ ((aligned(4)));
79  bool tx_active;
80 
81  uint32_t rx_dropped;
82  uint32_t rx_oversize;
83 };
84 
85 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev * usb_hid_dev)
86 {
87  return (usb_hid_dev && (usb_hid_dev->magic == PIOS_USB_HID_DEV_MAGIC));
88 }
89 
90 static struct pios_usb_hid_dev * PIOS_USB_HID_alloc(void)
91 {
92  struct pios_usb_hid_dev * usb_hid_dev;
93 
94  usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_malloc(sizeof(*usb_hid_dev));
95  if (!usb_hid_dev) return(NULL);
96 
97  memset(usb_hid_dev, 0, sizeof(*usb_hid_dev));
98  usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
99  return(usb_hid_dev);
100 }
101 
102 static void PIOS_USB_HID_IF_Init(uintptr_t usb_hid_id);
103 static void PIOS_USB_HID_IF_DeInit(uintptr_t usb_hid_id);
104 static bool PIOS_USB_HID_IF_Setup(uintptr_t usb_hid_id, struct usb_setup_request *req);
105 static void PIOS_USB_HID_IF_CtrlDataOut(uintptr_t usb_hid_id, const struct usb_setup_request *req);
106 
107 static struct pios_usb_ifops usb_hid_ifops = {
108  .init = PIOS_USB_HID_IF_Init,
109  .deinit = PIOS_USB_HID_IF_DeInit,
110  .setup = PIOS_USB_HID_IF_Setup,
111  .ctrl_data_out = PIOS_USB_HID_IF_CtrlDataOut,
112 };
113 
114 static bool PIOS_USB_HID_EP_IN_Callback(uintptr_t usb_hid_id, uint8_t epnum, uint16_t len);
115 static bool PIOS_USB_HID_EP_OUT_Callback(uintptr_t usb_hid_id, uint8_t epnum, uint16_t len);
116 
117 int32_t PIOS_USB_HID_Init(uintptr_t * usbhid_id, const struct pios_usb_hid_cfg * cfg, uintptr_t lower_id)
118 {
119  PIOS_Assert(usbhid_id);
120  PIOS_Assert(cfg);
121 
122  struct pios_usb_hid_dev * usb_hid_dev;
123 
124  usb_hid_dev = (struct pios_usb_hid_dev *) PIOS_USB_HID_alloc();
125  if (!usb_hid_dev) goto out_fail;
126 
127  /* Bind the configuration to the device instance */
128  usb_hid_dev->cfg = cfg;
129  usb_hid_dev->lower_id = lower_id;
130 
131  /* Rx and Tx are not active yet */
132  usb_hid_dev->rx_active = false;
133  usb_hid_dev->tx_active = false;
134 
135  /* Register class specific interface callbacks with the USBHOOK layer */
136  usb_hid_dev->usb_if_enabled = false;
137  PIOS_USBHOOK_RegisterIfOps(cfg->data_if, &usb_hid_ifops, (uintptr_t) usb_hid_dev);
138 
139  *usbhid_id = (uintptr_t) usb_hid_dev;
140 
141  return 0;
142 
143 out_fail:
144  return -1;
145 }
146 
147 
148 static struct pios_usbhook_descriptor hid_desc;
149 
150 void PIOS_USB_HID_RegisterHidDescriptor(const uint8_t * desc, uint16_t length)
151 {
152  hid_desc.descriptor = desc;
153  hid_desc.length = length;
154 }
155 
157 
158 void PIOS_USB_HID_RegisterHidReport(const uint8_t * desc, uint16_t length)
159 {
160  hid_report_desc.descriptor = desc;
161  hid_report_desc.length = length;
162 }
163 
164 static bool PIOS_USB_HID_SendReport(struct pios_usb_hid_dev * usb_hid_dev)
165 {
166  uint16_t bytes_to_tx;
167 
168  if (!usb_hid_dev->tx_out_cb) {
169  return false;
170  }
171 
172  bool need_yield = false;
173 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
174  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
175  &usb_hid_dev->tx_packet_buffer[1],
176  sizeof(usb_hid_dev->tx_packet_buffer)-1,
177  NULL,
178  &need_yield);
179 #else
180  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
181  &usb_hid_dev->tx_packet_buffer[2],
182  sizeof(usb_hid_dev->tx_packet_buffer)-2,
183  NULL,
184  &need_yield);
185 #endif
186  if (bytes_to_tx == 0) {
187  return false;
188  }
189 
190  /*
191  * Mark this endpoint as being tx active _before_ actually transmitting
192  * to make sure we don't race with the Tx completion interrupt
193  */
194  usb_hid_dev->tx_active = true;
195 
196  /* Always set type as report ID */
197  usb_hid_dev->tx_packet_buffer[0] = 1;
198 
199 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
200  PIOS_USBHOOK_EndpointTx(usb_hid_dev->cfg->data_tx_ep,
201  usb_hid_dev->tx_packet_buffer,
202  sizeof(usb_hid_dev->tx_packet_buffer));
203 #else
204  usb_hid_dev->tx_packet_buffer[1] = bytes_to_tx;
205  PIOS_USBHOOK_EndpointTx(usb_hid_dev->cfg->data_tx_ep,
206  usb_hid_dev->tx_packet_buffer,
207  sizeof(usb_hid_dev->tx_packet_buffer));
208 #endif
209 
210  return true;
211 }
212 
213 static void PIOS_USB_HID_RxStart(uintptr_t usbhid_id, uint16_t rx_bytes_avail) {
214  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
215 
216  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
217  PIOS_Assert(valid);
218 
219  /* Make sure this USB interface has been initialized */
220  if (!usb_hid_dev->usb_if_enabled) {
221  return;
222  }
223 
224  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
225  return;
226  }
227 
228  // If endpoint was stalled and there is now space make it valid
229 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
230  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
231 #else
232  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
233 #endif
234 
235  if (!usb_hid_dev->rx_active && (rx_bytes_avail >= max_payload_length)) {
236  PIOS_USBHOOK_EndpointRx(usb_hid_dev->cfg->data_rx_ep,
237  usb_hid_dev->rx_packet_buffer,
238  sizeof(usb_hid_dev->rx_packet_buffer));
239  usb_hid_dev->rx_active = true;
240  }
241 }
242 
243 static void PIOS_USB_HID_TxStart(uintptr_t usbhid_id, uint16_t tx_bytes_avail)
244 {
245  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
246 
247  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
248  PIOS_Assert(valid);
249 
250  /* Make sure this USB interface has been initialized */
251  if (!usb_hid_dev->usb_if_enabled) {
252  return;
253  }
254 
255  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
256  return;
257  }
258 
259  if (!usb_hid_dev->tx_active) {
260  /* Transmitter is not currently active, send a report */
261  PIOS_USB_HID_SendReport(usb_hid_dev);
262  }
263 }
264 
265 static void PIOS_USB_HID_RegisterRxCallback(uintptr_t usbhid_id, pios_com_callback rx_in_cb, uintptr_t context)
266 {
267  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
268 
269  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
270  PIOS_Assert(valid);
271 
272  /*
273  * Order is important in these assignments since ISR uses _cb
274  * field to determine if it's ok to dereference _cb and _context
275  */
276  usb_hid_dev->rx_in_context = context;
277  usb_hid_dev->rx_in_cb = rx_in_cb;
278 }
279 
280 static void PIOS_USB_HID_RegisterTxCallback(uintptr_t usbhid_id, pios_com_callback tx_out_cb, uintptr_t context)
281 {
282  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
283 
284  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
285  PIOS_Assert(valid);
286 
287  /*
288  * Order is important in these assignments since ISR uses _cb
289  * field to determine if it's ok to dereference _cb and _context
290  */
291  usb_hid_dev->tx_out_context = context;
292  usb_hid_dev->tx_out_cb = tx_out_cb;
293 }
294 
295 static void PIOS_USB_HID_IF_Init(uintptr_t usb_hid_id)
296 {
297  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
298 
299  if (!PIOS_USB_HID_validate(usb_hid_dev)) {
300  return;
301  }
302 
303  /* Register endpoint specific callbacks with the USBHOOK layer */
304  PIOS_USBHOOK_RegisterEpInCallback(usb_hid_dev->cfg->data_tx_ep,
305  sizeof(usb_hid_dev->tx_packet_buffer),
306  PIOS_USB_HID_EP_IN_Callback,
307  (uintptr_t) usb_hid_dev);
308  PIOS_USBHOOK_RegisterEpOutCallback(usb_hid_dev->cfg->data_rx_ep,
309  sizeof(usb_hid_dev->rx_packet_buffer),
310  PIOS_USB_HID_EP_OUT_Callback,
311  (uintptr_t) usb_hid_dev);
312  usb_hid_dev->usb_if_enabled = true;
313 
314 }
315 
316 static void PIOS_USB_HID_IF_DeInit(uintptr_t usb_hid_id)
317 {
318  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
319 
320  if (!PIOS_USB_HID_validate(usb_hid_dev)) {
321  return;
322  }
323 
324  /* reset state of the usb hid device structure */
325  usb_hid_dev->rx_active = false;
326  usb_hid_dev->rx_dropped = 0;
327  usb_hid_dev->rx_oversize = 0;
328  usb_hid_dev->tx_active = false;
329  usb_hid_dev->usb_if_enabled = false;
330 
331  /* DeRegister endpoint specific callbacks with the USBHOOK layer */
332  PIOS_USBHOOK_DeRegisterEpInCallback(usb_hid_dev->cfg->data_tx_ep);
333  PIOS_USBHOOK_DeRegisterEpOutCallback(usb_hid_dev->cfg->data_rx_ep);
334 }
335 
336 static uint8_t hid_protocol;
337 static uint8_t hid_altset;
338 
339 struct hid_idle_msg {
340  uint8_t idle_period;
341  uint8_t report_id;
342 };
343 static struct hid_idle_msg hid_idle;
344 static uint8_t dummy_report[2];
345 
346 static bool PIOS_USB_HID_IF_Setup(uintptr_t usb_hid_id, struct usb_setup_request *req)
347 {
348  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
349 
350  if (!PIOS_USB_HID_validate(usb_hid_dev)) {
351  return false;
352  }
353 
354  /* Make sure this is a request for an interface we know about */
355  uint8_t ifnum = req->wIndex & 0xFF;
356  if (ifnum != usb_hid_dev->cfg->data_if) {
357  return (false);
358  }
359 
362  switch (req->bRequest) {
364  switch (req->wValue >> 8) {
367  MIN(hid_report_desc.length, req->wLength));
368  break;
369  case USB_DESC_TYPE_HID:
370  PIOS_USBHOOK_CtrlTx(hid_desc.descriptor,
371  MIN(hid_desc.length, req->wLength));
372  break;
373  default:
374  /* Unhandled descriptor request */
375  return false;
376  break;
377  }
378  break;
380  PIOS_USBHOOK_CtrlTx(&hid_altset, 1);
381  break;
383  hid_altset = (uint8_t)(req->wValue);
384  break;
385  default:
386  /* Unhandled standard request */
387  return false;
388  break;
389  }
390  break;
392  switch (req->bRequest) {
394  hid_protocol = (uint8_t)(req->wValue);
395  break;
397  /* Idle rates are currently ignored but decoded for debugging */
398  hid_idle.idle_period = req->wValue & 0xFF00 >> 8;
399  hid_idle.report_id = req->wValue & 0x00FF;
400  break;
402  PIOS_USBHOOK_CtrlTx(&hid_protocol, 1);
403  break;
405  /* Give back a dummy input report */
406  dummy_report[0] = req->wValue & 0xFF; /* Report ID */
407  dummy_report[1] = 0x00; /* dummy value */
408  PIOS_USBHOOK_CtrlTx(dummy_report,
409  MIN(sizeof(dummy_report), req->wLength));
410  break;
411  default:
412  /* Unhandled class request */
413  return false;
414  break;
415  }
416  break;
417  default:
418  /* Unhandled request */
419  return false;
420  }
421 
422  return true;
423 }
424 
425 static void PIOS_USB_HID_IF_CtrlDataOut(uintptr_t usb_hid_id, const struct usb_setup_request *req)
426 {
427  /* HID devices don't have any OUT data stages on the control endpoint */
428  PIOS_Assert(0);
429 }
430 
435 static bool PIOS_USB_HID_EP_IN_Callback(uintptr_t usb_hid_id, uint8_t epnum, uint16_t len)
436 {
437  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
438 
439  if (!PIOS_USB_HID_validate(usb_hid_dev)) {
440  return false;
441  }
442 
443  if (PIOS_USB_CheckAvailable(usb_hid_dev->lower_id) &&
444  PIOS_USB_HID_SendReport(usb_hid_dev)) {
445  /* More data has been queued, leave tx_active set to true */
446  return true;
447  } else {
448  /* Nothing new sent, transmitter is now inactive */
449  usb_hid_dev->tx_active = false;
450  return false;
451  }
452 }
453 
457 static bool PIOS_USB_HID_EP_OUT_Callback(uintptr_t usb_hid_id, uint8_t epnum, uint16_t len)
458 {
459  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usb_hid_id;
460 
461  if (!PIOS_USB_HID_validate(usb_hid_dev)) {
462  return false;
463  }
464 
465  if (len > sizeof(usb_hid_dev->rx_packet_buffer)) {
466  len = sizeof(usb_hid_dev->rx_packet_buffer);
467  }
468 
469  if (!usb_hid_dev->rx_in_cb) {
470  /* No Rx call back registered, disable the receiver */
471  usb_hid_dev->rx_active = false;
472  return false;
473  }
474 
475  /* The first byte is report ID (not checked), the second byte is the valid data length */
476  uint16_t headroom;
477  bool need_yield = false;
478 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
479  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
480  &usb_hid_dev->rx_packet_buffer[1],
481  len-1,
482  &headroom,
483  &need_yield);
484 #else
485  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
486  &usb_hid_dev->rx_packet_buffer[2],
487  usb_hid_dev->rx_packet_buffer[1],
488  &headroom,
489  &need_yield);
490 #endif
491 
492 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
493  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
494 #else
495  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
496 #endif
497 
498  bool rc;
499  if (headroom >= max_payload_length) {
500  /* We have room for a maximum length message */
501  PIOS_USBHOOK_EndpointRx(usb_hid_dev->cfg->data_rx_ep,
502  usb_hid_dev->rx_packet_buffer,
503  sizeof(usb_hid_dev->rx_packet_buffer));
504  rc = true;
505  } else {
506  /* Not enough room left for a message, apply backpressure */
507  usb_hid_dev->rx_active = false;
508  rc = false;
509  }
510 
511  return rc;
512 }
513 
514 #endif /* PIOS_INCLUDE_USB_HID */
void PIOS_USBHOOK_CtrlTx(const uint8_t *buf, uint16_t len)
Definition: pios_usbhook.c:197
Main PiOS header to include all the compiled in PiOS options.
#define USB_REQ_TYPE_CLASS
APIs for PIOS_USBHOOK layer.
static const uint8_t hid_report_desc[]
#define USB_REQ_TYPE_MASK
#define USB_REQ_TYPE_STANDARD
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)
tuple desc
Definition: px_mkfw.py:82
#define USB_REQ_RECIPIENT_MASK
void(* init)(uintptr_t context)
Definition: pios_usbhook.h:55
uint8_t length
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
const struct pios_com_driver pios_usb_hid_com_driver
void PIOS_USBHOOK_EndpointRx(uint8_t epnum, uint8_t *buf, uint16_t len)
Definition: pios_usbhook.c:214
#define USB_REQ_RECIPIENT_INTERFACE
USB_DESC_TYPE_REPORT
Definition: pios_usb_defs.h:48
void PIOS_USBHOOK_RegisterEpInCallback(uint8_t epnum, uint16_t max_len, pios_usbhook_epcb cb, uintptr_t context)
Definition: pios_usbhook.c:128
USB_DESC_TYPE_HID
Definition: pios_usb_defs.h:47
USB HID layer functions header.
USB COM HID private definitions.
uint32_t magic
#define PIOS_USB_BOARD_HID_DATA_LENGTH
void PIOS_USB_HID_RegisterHidReport(const uint8_t *desc, uint16_t length)
Definition: pios_usbhook.c:78
#define MIN(a, b)
Definition: misc_math.h:41
void PIOS_USBHOOK_EndpointTx(uint8_t epnum, const uint8_t *buf, uint16_t len)
Definition: pios_usbhook.c:207
void PIOS_USBHOOK_DeRegisterEpInCallback(uint8_t epnum)
Definition: pios_usbhook.c:146
void PIOS_USBHOOK_RegisterEpOutCallback(uint8_t epnum, uint16_t max_len, pios_usbhook_epcb cb, uintptr_t context)
Definition: pios_usbhook.c:156
void PIOS_USB_HID_RegisterHidDescriptor(const uint8_t *desc, uint16_t length)
Definition: pios_usbhook.c:70
void PIOS_USBHOOK_DeRegisterEpOutCallback(uint8_t epnum)
Definition: pios_usbhook.c:188
#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 PIOS_USBHOOK_RegisterIfOps(uint8_t ifnum, struct pios_usb_ifops *ifops, uintptr_t context)
Definition: pios_usbhook.c:113
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
typedef __attribute__
Definition: serial_4way.h:43