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 
18 /*
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful, but
25  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27  * for more details.
28  *
29  * You should have received a copy of the GNU General Public License along
30  * with this program; if not, see <http://www.gnu.org/licenses/>
31  */
32 
33 /* Project Includes */
34 #include "pios.h"
35 
36 #if defined(PIOS_INCLUDE_USB_HID)
37 
38 #include "pios_usb.h"
39 #include "pios_usb_hid_priv.h"
40 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
41 
42 /* STM32 USB Library Definitions */
43 #include "usb_lib.h"
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  uint8_t rx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
74  uint8_t tx_packet_buffer[PIOS_USB_BOARD_HID_DATA_LENGTH];
75 
76  uint32_t rx_dropped;
77  uint32_t rx_oversize;
78 };
79 
80 static bool PIOS_USB_HID_validate(struct pios_usb_hid_dev * usb_hid_dev)
81 {
82  return (usb_hid_dev->magic == PIOS_USB_HID_DEV_MAGIC);
83 }
84 
85 static struct pios_usb_hid_dev * PIOS_USB_HID_alloc(void)
86 {
87  struct pios_usb_hid_dev * usb_hid_dev;
88 
89  usb_hid_dev = (struct pios_usb_hid_dev *)PIOS_malloc(sizeof(*usb_hid_dev));
90  if (!usb_hid_dev) return(NULL);
91 
92  memset(usb_hid_dev, 0, sizeof(*usb_hid_dev));
93  usb_hid_dev->magic = PIOS_USB_HID_DEV_MAGIC;
94  return(usb_hid_dev);
95 }
96 
97 static void PIOS_USB_HID_EP_IN_Callback(void);
98 static void PIOS_USB_HID_EP_OUT_Callback(void);
99 
100 static uintptr_t pios_usb_hid_id;
101 
102 /* Need a better way to pull these in */
103 extern void (*pEpInt_IN[7])(void);
104 extern void (*pEpInt_OUT[7])(void);
105 
106 int32_t PIOS_USB_HID_Init(uintptr_t * usbhid_id, const struct pios_usb_hid_cfg * cfg, uintptr_t lower_id)
107 {
108  PIOS_Assert(usbhid_id);
109  PIOS_Assert(cfg);
110 
111  struct pios_usb_hid_dev * usb_hid_dev;
112 
113  usb_hid_dev = (struct pios_usb_hid_dev *) PIOS_USB_HID_alloc();
114  if (!usb_hid_dev) goto out_fail;
115 
116  /* Bind the configuration to the device instance */
117  usb_hid_dev->cfg = cfg;
118  usb_hid_dev->lower_id = lower_id;
119 
120  pios_usb_hid_id = (uintptr_t) usb_hid_dev;
121 
122  /* Bind lower level callbacks into the USB infrastructure */
123  pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_HID_EP_IN_Callback;
124  pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_HID_EP_OUT_Callback;
125 
126  *usbhid_id = (uintptr_t) usb_hid_dev;
127 
128  return 0;
129 
130 out_fail:
131  return -1;
132 }
133 
134 
135 
136 
137 static void PIOS_USB_HID_SendReport(struct pios_usb_hid_dev * usb_hid_dev)
138 {
139  uint16_t bytes_to_tx;
140 
141  if (!usb_hid_dev->tx_out_cb) {
142  return;
143  }
144 
145  bool need_yield = false;
146 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
147  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
148  &usb_hid_dev->tx_packet_buffer[1],
149  sizeof(usb_hid_dev->tx_packet_buffer)-1,
150  NULL,
151  &need_yield);
152 #else
153  bytes_to_tx = (usb_hid_dev->tx_out_cb)(usb_hid_dev->tx_out_context,
154  &usb_hid_dev->tx_packet_buffer[2],
155  sizeof(usb_hid_dev->tx_packet_buffer)-2,
156  NULL,
157  &need_yield);
158 #endif
159  if (bytes_to_tx == 0) {
160  return;
161  }
162 
163  /* Always set type as report ID */
164  usb_hid_dev->tx_packet_buffer[0] = 1;
165 
166 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
167  UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
168  GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
169  bytes_to_tx + 1);
170 #else
171  usb_hid_dev->tx_packet_buffer[1] = bytes_to_tx;
172  UserToPMABufferCopy(usb_hid_dev->tx_packet_buffer,
173  GetEPTxAddr(usb_hid_dev->cfg->data_tx_ep),
174  bytes_to_tx + 2);
175 #endif
176  /* Is this correct? Why do we always send the whole buffer? */
177  SetEPTxCount(usb_hid_dev->cfg->data_tx_ep, sizeof(usb_hid_dev->tx_packet_buffer));
178  SetEPTxValid(usb_hid_dev->cfg->data_tx_ep);
179 }
180 
181 static void PIOS_USB_HID_RxStart(uintptr_t usbhid_id, uint16_t rx_bytes_avail) {
182  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
183 
184  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
185  PIOS_Assert(valid);
186 
187  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
188  return;
189  }
190 
191  // If endpoint was stalled and there is now space make it valid
192 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
193  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
194 #else
195  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
196 #endif
197 
199  if ((GetEPRxStatus(usb_hid_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
200  (rx_bytes_avail >= max_payload_length)) {
201  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
202  }
203  PIOS_IRQ_Enable();
204 }
205 
206 static void PIOS_USB_HID_TxStart(uintptr_t usbhid_id, uint16_t tx_bytes_avail)
207 {
208  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
209 
210  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
211  PIOS_Assert(valid);
212 
213  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
214  return;
215  }
216 
217  if (GetEPTxStatus(usb_hid_dev->cfg->data_tx_ep) == EP_TX_VALID) {
218  /* Endpoint is already transmitting */
219  return;
220  }
221 
222  PIOS_USB_HID_SendReport(usb_hid_dev);
223 }
224 
225 static void PIOS_USB_HID_RegisterRxCallback(uintptr_t usbhid_id, pios_com_callback rx_in_cb, uintptr_t context)
226 {
227  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
228 
229  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
230  PIOS_Assert(valid);
231 
232  /*
233  * Order is important in these assignments since ISR uses _cb
234  * field to determine if it's ok to dereference _cb and _context
235  */
236  usb_hid_dev->rx_in_context = context;
237  usb_hid_dev->rx_in_cb = rx_in_cb;
238 }
239 
240 static void PIOS_USB_HID_RegisterTxCallback(uintptr_t usbhid_id, pios_com_callback tx_out_cb, uintptr_t context)
241 {
242  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)usbhid_id;
243 
244  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
245  PIOS_Assert(valid);
246 
247  /*
248  * Order is important in these assignments since ISR uses _cb
249  * field to determine if it's ok to dereference _cb and _context
250  */
251  usb_hid_dev->tx_out_context = context;
252  usb_hid_dev->tx_out_cb = tx_out_cb;
253 }
254 
259 static void PIOS_USB_HID_EP_IN_Callback(void)
260 {
261  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
262 
263  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
264  PIOS_Assert(valid);
265 
266  if (!PIOS_USB_CheckAvailable(usb_hid_dev->lower_id)) {
267  return;
268  }
269 
270  PIOS_USB_HID_SendReport(usb_hid_dev);
271 }
272 
276 static void PIOS_USB_HID_EP_OUT_Callback(void)
277 {
278  struct pios_usb_hid_dev * usb_hid_dev = (struct pios_usb_hid_dev *)pios_usb_hid_id;
279 
280  bool valid = PIOS_USB_HID_validate(usb_hid_dev);
281  PIOS_Assert(valid);
282 
283  uint32_t DataLength;
284 
285  /* Read received data (63 bytes) */
286  /* Get the number of received data on the selected Endpoint */
287  DataLength = GetEPRxCount(usb_hid_dev->cfg->data_rx_ep);
288  if (DataLength > sizeof(usb_hid_dev->rx_packet_buffer)) {
289  DataLength = sizeof(usb_hid_dev->rx_packet_buffer);
290  }
291 
292  /* Use the memory interface function to read from the selected endpoint */
293  PMAToUserBufferCopy((uint8_t *) usb_hid_dev->rx_packet_buffer,
294  GetEPRxAddr(usb_hid_dev->cfg->data_rx_ep),
295  DataLength);
296 
297  if (!usb_hid_dev->rx_in_cb) {
298  /* No Rx call back registered, disable the receiver */
299  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
300  return;
301  }
302 
303  /* The first byte is report ID (not checked), the second byte is the valid data length */
304  uint16_t headroom;
305  bool need_yield = false;
306 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
307  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
308  &usb_hid_dev->rx_packet_buffer[1],
309  DataLength-1,
310  &headroom,
311  &need_yield);
312 #else
313  (usb_hid_dev->rx_in_cb)(usb_hid_dev->rx_in_context,
314  &usb_hid_dev->rx_packet_buffer[2],
315  usb_hid_dev->rx_packet_buffer[1],
316  &headroom,
317  &need_yield);
318 #endif
319 
320 #ifdef PIOS_USB_BOARD_BL_HID_HAS_NO_LENGTH_BYTE
321  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 1;
322 #else
323  uint16_t max_payload_length = PIOS_USB_BOARD_HID_DATA_LENGTH - 2;
324 #endif
325 
326  if (headroom >= max_payload_length) {
327 
328  /* We have room for a maximum length message */
329  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_VALID);
330  } else {
331  /* Not enough room left for a message, apply backpressure */
332  SetEPRxStatus(usb_hid_dev->cfg->data_rx_ep, EP_RX_NAK);
333  }
334 }
335 
336 #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