dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_usb_cdc.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_CDC)
37 
38 #include "pios_usb.h"
39 #include "pios_usb_cdc_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_CDC_RegisterTxCallback(uintptr_t usbcdc_id, pios_com_callback tx_out_cb, uintptr_t context);
46 static void PIOS_USB_CDC_RegisterRxCallback(uintptr_t usbcdc_id, pios_com_callback rx_in_cb, uintptr_t context);
47 static void PIOS_USB_CDC_TxStart(uintptr_t usbcdc_id, uint16_t tx_bytes_avail);
48 static void PIOS_USB_CDC_RxStart(uintptr_t usbcdc_id, uint16_t rx_bytes_avail);
49 static bool PIOS_USB_CDC_Available (uintptr_t usbcdc_id);
50 
52  .tx_start = PIOS_USB_CDC_TxStart,
53  .rx_start = PIOS_USB_CDC_RxStart,
54  .bind_tx_cb = PIOS_USB_CDC_RegisterTxCallback,
55  .bind_rx_cb = PIOS_USB_CDC_RegisterRxCallback,
56  .available = PIOS_USB_CDC_Available,
57 };
58 
59 enum pios_usb_cdc_dev_magic {
60  PIOS_USB_CDC_DEV_MAGIC = 0xAABBCCDD,
61 };
62 
63 struct pios_usb_cdc_dev {
64  enum pios_usb_cdc_dev_magic magic;
65  const struct pios_usb_cdc_cfg * cfg;
66 
67  uintptr_t lower_id;
68 
69  pios_com_callback rx_in_cb;
70  uintptr_t rx_in_context;
71  pios_com_callback tx_out_cb;
72  uintptr_t tx_out_context;
73 
74  uint8_t rx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH];
75  /*
76  * NOTE: This is -1 as somewhat of a hack. It ensures that we always send packets
77  * that are strictly < maxPacketSize for this interface which means we never have
78  * to bother with zero length packets (ZLP).
79  */
80  uint8_t tx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH - 1];
81 
82  uint32_t rx_dropped;
83  uint32_t rx_oversize;
84 };
85 
86 static bool PIOS_USB_CDC_validate(struct pios_usb_cdc_dev * usb_cdc_dev)
87 {
88  return (usb_cdc_dev->magic == PIOS_USB_CDC_DEV_MAGIC);
89 }
90 
91 static struct pios_usb_cdc_dev * PIOS_USB_CDC_alloc(void)
92 {
93  struct pios_usb_cdc_dev * usb_cdc_dev;
94 
95  usb_cdc_dev = (struct pios_usb_cdc_dev *)PIOS_malloc(sizeof(*usb_cdc_dev));
96  if (!usb_cdc_dev) return(NULL);
97 
98  memset(usb_cdc_dev, 0, sizeof(*usb_cdc_dev));
99  usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
100  return(usb_cdc_dev);
101 }
102 
103 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void);
104 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void);
105 static void PIOS_USB_CDC_CTRL_EP_IN_Callback(void);
106 
107 static uintptr_t pios_usb_cdc_id;
108 
109 /* Need a better way to pull these in */
110 extern void (*pEpInt_IN[7])(void);
111 extern void (*pEpInt_OUT[7])(void);
112 
113 int32_t PIOS_USB_CDC_Init(uintptr_t * usbcdc_id, const struct pios_usb_cdc_cfg * cfg, uintptr_t lower_id)
114 {
115  PIOS_Assert(usbcdc_id);
116  PIOS_Assert(cfg);
117 
118  struct pios_usb_cdc_dev * usb_cdc_dev;
119 
120  usb_cdc_dev = (struct pios_usb_cdc_dev *) PIOS_USB_CDC_alloc();
121  if (!usb_cdc_dev) goto out_fail;
122 
123  /* Bind the configuration to the device instance */
124  usb_cdc_dev->cfg = cfg;
125  usb_cdc_dev->lower_id = lower_id;
126 
127  pios_usb_cdc_id = (uintptr_t) usb_cdc_dev;
128 
129  /* Bind lower level callbacks into the USB infrastructure */
130  pEpInt_OUT[cfg->ctrl_tx_ep - 1] = PIOS_USB_CDC_CTRL_EP_IN_Callback;
131  pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_CDC_DATA_EP_IN_Callback;
132  pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_CDC_DATA_EP_OUT_Callback;
133 
134  *usbcdc_id = (uintptr_t) usb_cdc_dev;
135 
136  return 0;
137 
138 out_fail:
139  return -1;
140 }
141 
142 
143 
144 static void PIOS_USB_CDC_RegisterRxCallback(uintptr_t usbcdc_id, pios_com_callback rx_in_cb, uintptr_t context)
145 {
146  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
147 
148  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
149  PIOS_Assert(valid);
150 
151  /*
152  * Order is important in these assignments since ISR uses _cb
153  * field to determine if it's ok to dereference _cb and _context
154  */
155  usb_cdc_dev->rx_in_context = context;
156  usb_cdc_dev->rx_in_cb = rx_in_cb;
157 }
158 
159 static void PIOS_USB_CDC_RegisterTxCallback(uintptr_t usbcdc_id, pios_com_callback tx_out_cb, uintptr_t context)
160 {
161  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
162 
163  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
164  PIOS_Assert(valid);
165 
166  /*
167  * Order is important in these assignments since ISR uses _cb
168  * field to determine if it's ok to dereference _cb and _context
169  */
170  usb_cdc_dev->tx_out_context = context;
171  usb_cdc_dev->tx_out_cb = tx_out_cb;
172 }
173 
174 static void PIOS_USB_CDC_RxStart(uintptr_t usbcdc_id, uint16_t rx_bytes_avail) {
175  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
176 
177  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
178  PIOS_Assert(valid);
179 
180  if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
181  return;
182  }
183 
184  // If endpoint was stalled and there is now space make it valid
186  if ((GetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
187  (rx_bytes_avail >= sizeof(usb_cdc_dev->rx_packet_buffer))) {
188  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
189  }
190  PIOS_IRQ_Enable();
191 }
192 
193 static void PIOS_USB_CDC_SendData(struct pios_usb_cdc_dev * usb_cdc_dev)
194 {
195  uint16_t bytes_to_tx;
196 
197  if (!usb_cdc_dev->tx_out_cb) {
198  return;
199  }
200 
201  bool need_yield = false;
202  bytes_to_tx = (usb_cdc_dev->tx_out_cb)(usb_cdc_dev->tx_out_context,
203  usb_cdc_dev->tx_packet_buffer,
204  sizeof(usb_cdc_dev->tx_packet_buffer),
205  NULL,
206  &need_yield);
207  if (bytes_to_tx == 0) {
208  return;
209  }
210 
211  UserToPMABufferCopy(usb_cdc_dev->tx_packet_buffer,
212  GetEPTxAddr(usb_cdc_dev->cfg->data_tx_ep),
213  bytes_to_tx);
214  SetEPTxCount(usb_cdc_dev->cfg->data_tx_ep, bytes_to_tx);
215  SetEPTxValid(usb_cdc_dev->cfg->data_tx_ep);
216 }
217 
218 static void PIOS_USB_CDC_TxStart(uintptr_t usbcdc_id, uint16_t tx_bytes_avail)
219 {
220  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
221 
222  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
223  PIOS_Assert(valid);
224 
225  if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
226  return;
227  }
228 
229  if (GetEPTxStatus(usb_cdc_dev->cfg->data_tx_ep) == EP_TX_VALID) {
230  /* Endpoint is already transmitting */
231  return;
232  }
233 
234  PIOS_USB_CDC_SendData(usb_cdc_dev);
235 }
236 
237 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void)
238 {
239  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
240 
241  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
242  PIOS_Assert(valid);
243 
244  PIOS_USB_CDC_SendData(usb_cdc_dev);
245 }
246 
247 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void)
248 {
249  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
250 
251  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
252  PIOS_Assert(valid);
253 
254  uint32_t DataLength;
255 
256  /* Get the number of received data on the selected Endpoint */
257  DataLength = GetEPRxCount(usb_cdc_dev->cfg->data_rx_ep);
258  if (DataLength > sizeof(usb_cdc_dev->rx_packet_buffer)) {
259  usb_cdc_dev->rx_oversize++;
260  DataLength = sizeof(usb_cdc_dev->rx_packet_buffer);
261  }
262 
263  /* Use the memory interface function to read from the selected endpoint */
264  PMAToUserBufferCopy((uint8_t *) usb_cdc_dev->rx_packet_buffer,
265  GetEPRxAddr(usb_cdc_dev->cfg->data_rx_ep),
266  DataLength);
267 
268  if (!usb_cdc_dev->rx_in_cb) {
269  /* No Rx call back registered, disable the receiver */
270  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
271  return;
272  }
273 
274  uint16_t headroom;
275  bool need_yield = false;
276  uint16_t rc;
277  rc = (usb_cdc_dev->rx_in_cb)(usb_cdc_dev->rx_in_context,
278  usb_cdc_dev->rx_packet_buffer,
279  DataLength,
280  &headroom,
281  &need_yield);
282 
283  if (rc < DataLength) {
284  /* Lost bytes on rx */
285  usb_cdc_dev->rx_dropped += (DataLength - rc);
286  }
287 
288  if (headroom >= sizeof(usb_cdc_dev->rx_packet_buffer)) {
289  /* We have room for a maximum length message */
290  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
291  } else {
292  /* Not enough room left for a message, apply backpressure */
293  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
294  }
295 }
296 
297 static uint16_t control_line_state;
299 {
300  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
301 
302  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
303  if (!valid) {
304  /* No CDC interface is configured */
305  return USB_UNSUPPORT;
306  }
307 
308  uint8_t wValue0 = pInformation->USBwValue0;
309  uint8_t wValue1 = pInformation->USBwValue1;
310 
311  control_line_state = wValue1 << 8 | wValue0;
312 
313  return USB_SUCCESS;
314 }
315 
316 static bool PIOS_USB_CDC_Available (uintptr_t usbcdc_id)
317 {
318  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
319 
320  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
321  PIOS_Assert(valid);
322 
323  return PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id);
324 }
325 
326 static struct usb_cdc_line_coding line_coding = {
327  .dwDTERate = htousbl(57600),
328  .bCharFormat = USB_CDC_LINE_CODING_STOP_1,
329  .bParityType = USB_CDC_LINE_CODING_PARITY_NONE,
330  .bDataBits = 8,
331 };
332 
333 uint8_t *PIOS_USB_CDC_SetLineCoding(uint16_t Length)
334 {
335  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
336 
337  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
338  if (!valid) {
339  /* No CDC interface is configured */
340  return NULL;
341  }
342 
343  if (Length == 0) {
344  /* Report the number of bytes we're prepared to consume */
345  pInformation->Ctrl_Info.Usb_wLength = sizeof(line_coding);
346  pInformation->Ctrl_Info.Usb_rLength = sizeof(line_coding);
347  return NULL;
348  } else {
349  /* Give out a pointer to the data struct */
350  return ((uint8_t *) &line_coding);
351  }
352 }
353 
354 const uint8_t *PIOS_USB_CDC_GetLineCoding(uint16_t Length)
355 {
356  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
357 
358  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
359  if (!valid) {
360  /* No CDC interface is configured */
361  return NULL;
362  }
363 
364  if (Length == 0) {
365  pInformation->Ctrl_Info.Usb_wLength = sizeof(line_coding);
366  return NULL;
367  } else {
368  return ((uint8_t *) &line_coding);
369  }
370 }
371 
372 const struct usb_cdc_serial_state_report uart_state = {
373  .bmRequestType = 0xA1,
374  .bNotification = USB_CDC_NOTIFICATION_SERIAL_STATE,
375  .wValue = 0,
376  .wIndex = htousbs(1),
377  .wLength = htousbs(2),
378  .bmUartState = htousbs(2 /* DSR */ | 1 /* DCD */),
379 };
380 
381 static void PIOS_USB_CDC_CTRL_EP_IN_Callback(void)
382 {
383  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
384 
385  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
386  PIOS_Assert(valid);
387 
388  UserToPMABufferCopy((const uint8_t *) &uart_state,
389  GetEPTxAddr(usb_cdc_dev->cfg->ctrl_tx_ep),
390  sizeof(uart_state));
391 
392  SetEPTxCount(usb_cdc_dev->cfg->ctrl_tx_ep, PIOS_USB_BOARD_CDC_MGMT_LENGTH);
393  SetEPTxValid(usb_cdc_dev->cfg->ctrl_tx_ep);
394 }
395 
396 #endif /* PIOS_INCLUDE_USB_CDC */
void(* pEpInt_IN[7])(void)
USB_CDC_LINE_CODING_STOP_1
Definition: pios_usb_defs.h:41
int32_t PIOS_IRQ_Enable(void)
Definition: pios_irq.c:53
Main PiOS header to include all the compiled in PiOS options.
const struct pios_com_driver pios_usb_cdc_com_driver
USB_CDC_NOTIFICATION_SERIAL_STATE
Definition: pios_usb_defs.h:41
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
bool PIOS_USB_CheckAvailable(uintptr_t id)
uint8_t * PIOS_USB_CDC_SetLineCoding(uint16_t Length)
#define PIOS_USB_BOARD_CDC_MGMT_LENGTH
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
#define htousbs(v)
Definition: pios_usb_defs.h:77
const uint8_t * PIOS_USB_CDC_GetLineCoding(uint16_t Length)
USB HID layer functions header.
uint32_t magic
int32_t PIOS_IRQ_Disable(void)
Definition: pios_irq.c:40
#define htousbl(v)
Definition: pios_usb_defs.h:78
#define PIOS_USB_BOARD_CDC_DATA_LENGTH
RESULT PIOS_USB_CDC_SetControlLineState(void)
int32_t PIOS_USB_CDC_Init(uintptr_t *usbcdc_id, const struct pios_usb_cdc_cfg *cfg, uintptr_t lower_id)
USB COM CDC private definitions.
#define PIOS_Assert(test)
Definition: pios_debug.h:52
USB_CDC_LINE_CODING_PARITY_NONE
Definition: pios_usb_defs.h:41
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