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 
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_CDC)
36 
37 #include "pios_usb.h"
38 #include "pios_usb_cdc_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_CDC_RegisterTxCallback(uintptr_t usbcdc_id, pios_com_callback tx_out_cb, uintptr_t context);
45 static void PIOS_USB_CDC_RegisterRxCallback(uintptr_t usbcdc_id, pios_com_callback rx_in_cb, uintptr_t context);
46 static void PIOS_USB_CDC_TxStart(uintptr_t usbcdc_id, uint16_t tx_bytes_avail);
47 static void PIOS_USB_CDC_RxStart(uintptr_t usbcdc_id, uint16_t rx_bytes_avail);
48 static bool PIOS_USB_CDC_Available (uintptr_t usbcdc_id);
49 
51  .tx_start = PIOS_USB_CDC_TxStart,
52  .rx_start = PIOS_USB_CDC_RxStart,
53  .bind_tx_cb = PIOS_USB_CDC_RegisterTxCallback,
54  .bind_rx_cb = PIOS_USB_CDC_RegisterRxCallback,
55  .available = PIOS_USB_CDC_Available,
56 };
57 
58 enum pios_usb_cdc_dev_magic {
59  PIOS_USB_CDC_DEV_MAGIC = 0xAABBCCDD,
60 };
61 
62 struct pios_usb_cdc_dev {
63  enum pios_usb_cdc_dev_magic magic;
64  const struct pios_usb_cdc_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_CDC_DATA_LENGTH];
74  /*
75  * NOTE: This is -1 as somewhat of a hack. It ensures that we always send packets
76  * that are strictly < maxPacketSize for this interface which means we never have
77  * to bother with zero length packets (ZLP).
78  */
79  uint8_t tx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH - 1];
80 
81  uint32_t rx_dropped;
82  uint32_t rx_oversize;
83 };
84 
85 static bool PIOS_USB_CDC_validate(struct pios_usb_cdc_dev * usb_cdc_dev)
86 {
87  return (usb_cdc_dev->magic == PIOS_USB_CDC_DEV_MAGIC);
88 }
89 
90 static struct pios_usb_cdc_dev * PIOS_USB_CDC_alloc(void)
91 {
92  struct pios_usb_cdc_dev * usb_cdc_dev;
93 
94  usb_cdc_dev = (struct pios_usb_cdc_dev *)PIOS_malloc(sizeof(*usb_cdc_dev));
95  if (!usb_cdc_dev) return(NULL);
96 
97  memset(usb_cdc_dev, 0, sizeof(*usb_cdc_dev));
98  usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
99  return(usb_cdc_dev);
100 }
101 
102 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void);
103 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void);
104 static void PIOS_USB_CDC_CTRL_EP_IN_Callback(void);
105 
106 static uintptr_t pios_usb_cdc_id;
107 
108 /* Need a better way to pull these in */
109 extern void (*pEpInt_IN[7])(void);
110 extern void (*pEpInt_OUT[7])(void);
111 
112 int32_t PIOS_USB_CDC_Init(uintptr_t * usbcdc_id, const struct pios_usb_cdc_cfg * cfg, uintptr_t lower_id)
113 {
114  PIOS_Assert(usbcdc_id);
115  PIOS_Assert(cfg);
116 
117  struct pios_usb_cdc_dev * usb_cdc_dev;
118 
119  usb_cdc_dev = (struct pios_usb_cdc_dev *) PIOS_USB_CDC_alloc();
120  if (!usb_cdc_dev) goto out_fail;
121 
122  /* Bind the configuration to the device instance */
123  usb_cdc_dev->cfg = cfg;
124  usb_cdc_dev->lower_id = lower_id;
125 
126  pios_usb_cdc_id = (uintptr_t) usb_cdc_dev;
127 
128  /* Bind lower level callbacks into the USB infrastructure */
129  pEpInt_OUT[cfg->ctrl_tx_ep - 1] = PIOS_USB_CDC_CTRL_EP_IN_Callback;
130  pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_CDC_DATA_EP_IN_Callback;
131  pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_CDC_DATA_EP_OUT_Callback;
132 
133  *usbcdc_id = (uintptr_t) usb_cdc_dev;
134 
135  return 0;
136 
137 out_fail:
138  return -1;
139 }
140 
141 
142 
143 static void PIOS_USB_CDC_RegisterRxCallback(uintptr_t usbcdc_id, pios_com_callback rx_in_cb, uintptr_t context)
144 {
145  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
146 
147  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
148  PIOS_Assert(valid);
149 
150  /*
151  * Order is important in these assignments since ISR uses _cb
152  * field to determine if it's ok to dereference _cb and _context
153  */
154  usb_cdc_dev->rx_in_context = context;
155  usb_cdc_dev->rx_in_cb = rx_in_cb;
156 }
157 
158 static void PIOS_USB_CDC_RegisterTxCallback(uintptr_t usbcdc_id, pios_com_callback tx_out_cb, uintptr_t context)
159 {
160  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
161 
162  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
163  PIOS_Assert(valid);
164 
165  /*
166  * Order is important in these assignments since ISR uses _cb
167  * field to determine if it's ok to dereference _cb and _context
168  */
169  usb_cdc_dev->tx_out_context = context;
170  usb_cdc_dev->tx_out_cb = tx_out_cb;
171 }
172 
173 static void PIOS_USB_CDC_RxStart(uintptr_t usbcdc_id, uint16_t rx_bytes_avail) {
174  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
175 
176  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
177  PIOS_Assert(valid);
178 
179  if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
180  return;
181  }
182 
183  // If endpoint was stalled and there is now space make it valid
185  if ((GetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
186  (rx_bytes_avail >= sizeof(usb_cdc_dev->rx_packet_buffer))) {
187  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
188  }
189  PIOS_IRQ_Enable();
190 }
191 
192 static void PIOS_USB_CDC_SendData(struct pios_usb_cdc_dev * usb_cdc_dev)
193 {
194  uint16_t bytes_to_tx;
195 
196  if (!usb_cdc_dev->tx_out_cb) {
197  return;
198  }
199 
200  bool need_yield = false;
201  bytes_to_tx = (usb_cdc_dev->tx_out_cb)(usb_cdc_dev->tx_out_context,
202  usb_cdc_dev->tx_packet_buffer,
203  sizeof(usb_cdc_dev->tx_packet_buffer),
204  NULL,
205  &need_yield);
206  if (bytes_to_tx == 0) {
207  return;
208  }
209 
210  UserToPMABufferCopy(usb_cdc_dev->tx_packet_buffer,
211  GetEPTxAddr(usb_cdc_dev->cfg->data_tx_ep),
212  bytes_to_tx);
213  SetEPTxCount(usb_cdc_dev->cfg->data_tx_ep, bytes_to_tx);
214  SetEPTxValid(usb_cdc_dev->cfg->data_tx_ep);
215 }
216 
217 static void PIOS_USB_CDC_TxStart(uintptr_t usbcdc_id, uint16_t tx_bytes_avail)
218 {
219  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
220 
221  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
222  PIOS_Assert(valid);
223 
224  if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
225  return;
226  }
227 
228  if (GetEPTxStatus(usb_cdc_dev->cfg->data_tx_ep) == EP_TX_VALID) {
229  /* Endpoint is already transmitting */
230  return;
231  }
232 
233  PIOS_USB_CDC_SendData(usb_cdc_dev);
234 }
235 
236 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void)
237 {
238  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
239 
240  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
241  PIOS_Assert(valid);
242 
243  PIOS_USB_CDC_SendData(usb_cdc_dev);
244 }
245 
246 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void)
247 {
248  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
249 
250  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
251  PIOS_Assert(valid);
252 
253  uint32_t DataLength;
254 
255  /* Get the number of received data on the selected Endpoint */
256  DataLength = GetEPRxCount(usb_cdc_dev->cfg->data_rx_ep);
257  if (DataLength > sizeof(usb_cdc_dev->rx_packet_buffer)) {
258  usb_cdc_dev->rx_oversize++;
259  DataLength = sizeof(usb_cdc_dev->rx_packet_buffer);
260  }
261 
262  /* Use the memory interface function to read from the selected endpoint */
263  PMAToUserBufferCopy((uint8_t *) usb_cdc_dev->rx_packet_buffer,
264  GetEPRxAddr(usb_cdc_dev->cfg->data_rx_ep),
265  DataLength);
266 
267  if (!usb_cdc_dev->rx_in_cb) {
268  /* No Rx call back registered, disable the receiver */
269  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
270  return;
271  }
272 
273  uint16_t headroom;
274  bool need_yield = false;
275  uint16_t rc;
276  rc = (usb_cdc_dev->rx_in_cb)(usb_cdc_dev->rx_in_context,
277  usb_cdc_dev->rx_packet_buffer,
278  DataLength,
279  &headroom,
280  &need_yield);
281 
282  if (rc < DataLength) {
283  /* Lost bytes on rx */
284  usb_cdc_dev->rx_dropped += (DataLength - rc);
285  }
286 
287  if (headroom >= sizeof(usb_cdc_dev->rx_packet_buffer)) {
288  /* We have room for a maximum length message */
289  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
290  } else {
291  /* Not enough room left for a message, apply backpressure */
292  SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
293  }
294 }
295 
296 static uint16_t control_line_state;
298 {
299  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
300 
301  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
302  if (!valid) {
303  /* No CDC interface is configured */
304  return USB_UNSUPPORT;
305  }
306 
307  uint8_t wValue0 = pInformation->USBwValue0;
308  uint8_t wValue1 = pInformation->USBwValue1;
309 
310  control_line_state = wValue1 << 8 | wValue0;
311 
312  return USB_SUCCESS;
313 }
314 
315 static bool PIOS_USB_CDC_Available (uintptr_t usbcdc_id)
316 {
317  struct pios_usb_cdc_dev * usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
318 
319  bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
320  PIOS_Assert(valid);
321 
322  return (PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id) &&
323  (control_line_state & USB_CDC_CONTROL_LINE_STATE_DTE_PRESENT));
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 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(0),
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  /* Give back UART State Bitmap */
389  /* UART State Bitmap
390  * 15-7: reserved
391  * 6: bOverRun overrun error
392  * 5: bParity parity error
393  * 4: bFraming framing error
394  * 3: bRingSignal RI
395  * 2: bBreak break reception
396  * 1: bTxCarrier DSR
397  * 0: bRxCarrier DCD
398  */
399  uart_state.bmUartState = htousbs(0x0003);
400 
401  UserToPMABufferCopy((uint8_t *) &uart_state,
402  GetEPTxAddr(usb_cdc_dev->cfg->ctrl_tx_ep),
403  sizeof(uart_state));
404  SetEPTxCount(usb_cdc_dev->cfg->ctrl_tx_ep, PIOS_USB_BOARD_CDC_MGMT_LENGTH);
405  SetEPTxValid(usb_cdc_dev->cfg->ctrl_tx_ep);
406 }
407 
408 #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
#define USB_CDC_CONTROL_LINE_STATE_DTE_PRESENT
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