瀏覽代碼

CMSIS-DAP: Add UART communication support

Robert Rostohar 4 年之前
父節點
當前提交
756a4fcdbb

+ 17 - 3
CMSIS/DAP/Firmware/Config/DAP_config.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
+ * Copyright (c) 2013-2021 ARM Limited. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -17,8 +17,8 @@
  *
  * ----------------------------------------------------------------------
  *
- * $Date:        1. December 2017
- * $Revision:    V2.0.0
+ * $Date:        29. March 2021
+ * $Revision:    V2.1.0
  *
  * Project:      CMSIS-DAP Configuration
  * Title:        DAP_config.h CMSIS-DAP Configuration File (Template)
@@ -100,6 +100,9 @@ This information includes:
 /// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
 #define SWO_UART                1               ///< SWO UART:  1 = available, 0 = not available.
 
+/// USART Driver instance number for the UART SWO.
+#define SWO_UART_DRIVER         0               ///< USART Driver instance number (Driver_USART#).
+
 /// Maximum SWO UART Baudrate.
 #define SWO_UART_MAX_BAUDRATE   10000000U       ///< SWO UART Maximum Baudrate in Hz.
 
@@ -116,6 +119,17 @@ This information includes:
 /// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET.
 #define TIMESTAMP_CLOCK         100000000U      ///< Timestamp clock in Hz (0 = timestamps not supported).
 
+/// Indicate that UART Communication Port is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_UART                1               ///< DAP UART:  1 = available, 0 = not available.
+
+/// USART Driver instance number for the UART Communication Port.
+#define DAP_UART_DRIVER         1               ///< USART Driver instance number (Driver_USART#).
+
+/// Indicate that UART Communication via USB COM Port is available.
+/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
+#define DAP_UART_USB_COM_PORT   1               ///< USB COM Port:  1 = available, 0 = not available.
+
 /// Debug Unit is connected to fixed Target Device.
 /// The Debug Unit may be part of an evaluation board and always connected to a fixed
 /// known device.  In this case a Device Vendor and Device Name string is stored which

+ 23 - 4
CMSIS/DAP/Firmware/Include/DAP.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2019 ARM Limited. All rights reserved.
+ * Copyright (c) 2013-2021 ARM Limited. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -17,8 +17,8 @@
  *
  * ----------------------------------------------------------------------
  *
- * $Date:        26. November 2019
- * $Revision:    V2.0.0
+ * $Date:        1. March 2021
+ * $Revision:    V2.1.0
  *
  * Project:      CMSIS-DAP Include
  * Title:        DAP.h Definitions
@@ -33,7 +33,7 @@
 #ifdef  DAP_FW_V1
 #define DAP_FW_VER                      "1.2.0"
 #else
-#define DAP_FW_VER                      "2.0.0"
+#define DAP_FW_VER                      "2.1.0"
 #endif
 
 // DAP Command IDs
@@ -63,6 +63,9 @@
 #define ID_DAP_SWO_Status               0x1BU
 #define ID_DAP_SWO_ExtendedStatus       0x1EU
 #define ID_DAP_SWO_Data                 0x1CU
+#define ID_DAP_UART_Transport           0x1FU
+#define ID_DAP_UART_Configure           0x20U
+#define ID_DAP_UART_Transfer            0x21U
 
 #define ID_DAP_QueueCommands            0x7EU
 #define ID_DAP_ExecuteCommands          0x7FU
@@ -165,6 +168,16 @@
 #define DAP_SWO_STREAM_ERROR            (1U<<6)
 #define DAP_SWO_BUFFER_OVERRUN          (1U<<7)
 
+// DAP UART Transport
+#define DAP_UART_TRANSPORT_USB_COM_PORT 0U
+#define DAP_UART_TRANSPORT_DAP_COMMAND  1U
+
+// DAP UART Transfer Response Status
+#define DAP_UART_TRANSFER_TX_BUSY       (1U<<11)
+#define DAP_UART_TRANSFER_TX_DATA_LOST  (1U<<12)
+#define DAP_UART_TRANSFER_RX_DATA_LOST  (1U<<13)
+#define DAP_UART_TRANSFER_FRAMING_ERROR (1U<<14)
+#define DAP_UART_TRANSFER_PARITY_ERROR  (1U<<15)
 
 // Debug Port Register Addresses
 #define DP_IDCODE                       0x00U   // IDCODE Register (SW Read only)
@@ -274,6 +287,12 @@ extern uint32_t Manchester_SWO_Control  (uint32_t active);
 extern void     Manchester_SWO_Capture  (uint8_t *buf, uint32_t num);
 extern uint32_t Manchester_SWO_GetCount (void);
 
+extern uint32_t UART_Transport (const uint8_t *request, uint8_t *response);
+extern uint32_t UART_Configure (const uint8_t *request, uint8_t *response);
+extern uint32_t UART_Transfer  (const uint8_t *request, uint8_t *response);
+
+extern void     USB_COM_PORT_Activate (uint32_t cmd);
+
 extern uint32_t DAP_ProcessVendorCommand (const uint8_t *request, uint8_t *response);
 extern uint32_t DAP_ProcessCommand       (const uint8_t *request, uint8_t *response);
 extern uint32_t DAP_ExecuteCommand       (const uint8_t *request, uint8_t *response);

+ 20 - 5
CMSIS/DAP/Firmware/Source/DAP.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
+ * Copyright (c) 2013-2021 ARM Limited. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -17,8 +17,8 @@
  *
  * ----------------------------------------------------------------------
  *
- * $Date:        1. December 2017
- * $Revision:    V2.0.0
+ * $Date:        29. March 2021
+ * $Revision:    V2.1.0
  *
  * Project:      CMSIS-DAP Source
  * Title:        DAP.c CMSIS-DAP Commands
@@ -105,8 +105,11 @@ static uint8_t DAP_Info(uint8_t id, uint8_t *info) {
                 ((SWO_MANCHESTER != 0)   ? (1U << 3) : 0U) |
                 /* Atomic Commands  */     (1U << 4)       |
                 ((TIMESTAMP_CLOCK != 0U) ? (1U << 5) : 0U) |
-                ((SWO_STREAM != 0U)      ? (1U << 6) : 0U);
-      length = 1U;
+                ((SWO_STREAM != 0U)      ? (1U << 6) : 0U) |
+                ((DAP_UART != 0U)        ? (1U << 7) : 0U);
+
+      info[1] = ((DAP_UART_USB_COM_PORT != 0) ? (1U << 0) : 0U);
+      length = 2U;
       break;
     case DAP_ID_TIMESTAMP_CLOCK:
 #if (TIMESTAMP_CLOCK != 0U) 
@@ -1711,6 +1714,18 @@ uint32_t DAP_ProcessCommand(const uint8_t *request, uint8_t *response) {
       break;
 #endif
 
+#if (DAP_UART != 0)
+    case ID_DAP_UART_Transport:
+      num = UART_Transport(request, response);
+      break;
+    case ID_DAP_UART_Configure:
+      num = UART_Configure(request, response);
+      break;
+    case ID_DAP_UART_Transfer:
+      num = UART_Transfer(request, response);
+      break;
+#endif
+
     default:
       *(response-1) = ID_DAP_Invalid;
       return ((1U << 16) | 1U);

+ 5 - 9
CMSIS/DAP/Firmware/Source/SWO.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
+ * Copyright (c) 2013-2021 ARM Limited. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -17,8 +17,8 @@
  *
  * ----------------------------------------------------------------------
  *
- * $Date:        1. December 2017
- * $Revision:    V2.0.0
+ * $Date:        29. March 2021
+ * $Revision:    V2.0.1
  *
  * Project:      CMSIS-DAP Source
  * Title:        SWO.c CMSIS-DAP SWO I/O
@@ -42,15 +42,11 @@
 
 #if (SWO_UART != 0)
 
-#ifndef  USART_PORT
-#define  USART_PORT 0           /* USART Port Number */
-#endif
-
 // USART Driver
 #define _USART_Driver_(n)  Driver_USART##n
 #define  USART_Driver_(n) _USART_Driver_(n)
-extern ARM_DRIVER_USART    USART_Driver_(USART_PORT);
-#define pUSART           (&USART_Driver_(USART_PORT))
+extern ARM_DRIVER_USART    USART_Driver_(SWO_UART_DRIVER);
+#define pUSART           (&USART_Driver_(SWO_UART_DRIVER))
 
 static uint8_t USART_Ready = 0U;
 

+ 392 - 0
CMSIS/DAP/Firmware/Source/UART.c

@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2021 ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the License); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----------------------------------------------------------------------
+ *
+ * $Date:        1. March 2021
+ * $Revision:    V1.0.0
+ *
+ * Project:      CMSIS-DAP Source
+ * Title:        UART.c CMSIS-DAP UART
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "DAP_config.h"
+#include "DAP.h"
+
+#if (DAP_UART != 0)
+
+#ifdef DAP_FW_V1
+#error "UART Communication Port not supported in DAP V1!"
+#endif
+
+#include "Driver_USART.h"
+
+#include "cmsis_os2.h"
+#include <string.h>
+
+#define UART_TX_BUF_SIZE    1024U   /* Uart Tx Buffer (must be 2^n) */
+#define UART_RX_BUF_SIZE    1024U   /* Uart Rx Buffer (must be 2^n) */
+#define UART_RX_BLOCK_SIZE    32U   /* Uart Rx Block Size (must be 2^n) */
+#define UART_MAX_XFER_NUM    509U
+
+// USART Driver
+#define _USART_Driver_(n)  Driver_USART##n
+#define  USART_Driver_(n) _USART_Driver_(n)
+extern ARM_DRIVER_USART    USART_Driver_(DAP_UART_DRIVER);
+#define pUSART           (&USART_Driver_(DAP_UART_DRIVER))
+
+// UART Configuration
+static uint8_t  UartTransport = DAP_UART_TRANSPORT_USB_COM_PORT;
+static uint8_t  UartControl   = 0U;
+static uint32_t UartBaudrate  = 115200U;
+
+// UART TX Buffer
+static uint8_t UartTxBuf[UART_TX_BUF_SIZE];
+static volatile uint32_t UartTxIndexI = 0U;
+static volatile uint32_t UartTxIndexO = 0U;
+
+// UART RX Buffer
+static uint8_t UartRxBuf[UART_RX_BUF_SIZE];
+static volatile uint32_t UartRxIndexI = 0U;
+static volatile uint32_t UartRxIndexO = 0U;
+
+// Uart Errors
+static volatile uint8_t UartRxDataLost   = 0U;
+static volatile uint8_t UartFramingError = 0U;
+static volatile uint8_t UartParityError  = 0U;
+
+// UART Transmit
+static uint32_t UartTxNum = 0U;
+
+// Function prototypes
+static uint32_t UART_Start   (void);
+static uint32_t UART_Stop    (void);
+static void     UART_Send    (void);
+static void     UART_Receive (void);
+
+
+// USART Driver Callback function
+//   event: event mask
+static void USART_Callback (uint32_t event) {
+  if (event &  ARM_USART_EVENT_SEND_COMPLETE) {
+    UartTxIndexO += UartTxNum;
+    UART_Send();
+  }
+  if (event &  ARM_USART_EVENT_RECEIVE_COMPLETE) {
+    UartRxIndexI += UART_RX_BLOCK_SIZE;
+    UART_Receive();
+  }
+  if (event &  ARM_USART_EVENT_RX_OVERFLOW) {
+    UartRxDataLost = 1U;
+  }
+  if (event &  ARM_USART_EVENT_RX_FRAMING_ERROR) {
+    UartFramingError = 1U;
+  }
+  if (event &  ARM_USART_EVENT_RX_PARITY_ERROR) {
+    UartParityError = 1U;
+  }
+}
+
+// Start UART
+//   return: 1 - Success, 0 - Error
+static uint32_t UART_Start (void) {
+  int32_t status;
+  uint32_t ret;
+
+  UartTxNum = 0U;
+
+  UartTxIndexI = 0U;
+  UartTxIndexO = 0U;
+  UartRxIndexI = 0U;
+  UartRxIndexO = 0U;
+
+  status = pUSART->Initialize(USART_Callback);
+
+  if (status == ARM_DRIVER_OK) {
+    status = pUSART->PowerControl(ARM_POWER_FULL);
+  }
+
+  if (status == ARM_DRIVER_OK) {
+    status = pUSART->Control (UartControl |
+                              ARM_USART_MODE_ASYNCHRONOUS |
+                              ARM_USART_FLOW_CONTROL_NONE,
+                              UartBaudrate);
+  }
+
+  if (status == ARM_DRIVER_OK) {
+    UART_Receive();
+    pUSART->Control (ARM_USART_CONTROL_TX, 1);
+    pUSART->Control (ARM_USART_CONTROL_RX, 1);
+  }
+
+  if (status != ARM_DRIVER_OK) {
+    pUSART->PowerControl(ARM_POWER_OFF);
+    pUSART->Uninitialize();
+  }
+
+  if (status == ARM_DRIVER_OK) {
+    ret = 1U;
+  } else {
+    ret = 0U;
+  }
+
+  return (ret);
+}
+
+// Stop UART
+//   return: 1 - Success, 0 - Error
+static uint32_t UART_Stop (void) {
+  UartTxIndexI = 0U;
+  UartTxIndexO = 0U;
+  UartRxIndexI = 0U;
+  UartRxIndexO = 0U;
+
+  pUSART->Control(ARM_USART_ABORT_RECEIVE, 0U);
+  pUSART->Control(ARM_USART_ABORT_SEND, 0U);
+  pUSART->PowerControl(ARM_POWER_OFF);
+  pUSART->Uninitialize();
+
+  return (1U);
+}
+
+// Send available data to target via UART
+static void UART_Send (void) {
+  uint32_t count;
+  uint32_t index;
+
+  count = UartTxIndexI - UartTxIndexO;
+  index = UartTxIndexO & (UART_TX_BUF_SIZE - 1);
+
+  if (count != 0U) {
+    if ((index + count) < UART_TX_BUF_SIZE) {
+      UartTxNum = count;
+    } else {
+      UartTxNum = UART_TX_BUF_SIZE - index;
+    }
+    pUSART->Send(&UartTxBuf[index], UartTxNum);
+  }
+}
+
+// Receive data from target via UART
+static void UART_Receive (void) {
+  uint16_t num;
+  uint32_t count;
+  uint32_t index;
+
+  count = UartRxIndexI - UartRxIndexO;
+  index = UartRxIndexI & (UART_RX_BUF_SIZE - 1);
+  num   =  UART_RX_BLOCK_SIZE;
+
+  if ((UART_RX_BUF_SIZE - count) >= num) {
+    pUSART->Receive(&UartRxBuf[index], num);
+  }
+}
+
+// Process UART Transport command and prepare response
+//   request:  pointer to request data
+//   response: pointer to response data
+//   return:   number of bytes in response (lower 16 bits)
+//             number of bytes in request (upper 16 bits)
+uint32_t UART_Transport (const uint8_t *request, uint8_t *response) {
+  uint8_t  transport;
+  uint32_t result = 0U;
+
+  transport = *request;
+  switch (transport) {
+    case DAP_UART_TRANSPORT_USB_COM_PORT:
+#if (DAP_UART_USB_COM_PORT != 0U)
+      if (UartTransport == DAP_UART_TRANSPORT_DAP_COMMAND) {
+        result = UART_Stop();
+        USB_COM_PORT_Activate(1U);
+      } else {
+        result = 1U;
+      }
+      UartTransport = transport;
+#endif
+      break;
+    case DAP_UART_TRANSPORT_DAP_COMMAND:
+      if (UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) {
+#if (DAP_UART_USB_COM_PORT != 0U)
+        USB_COM_PORT_Activate(0U);
+#endif
+        result = UART_Start();
+      } else {
+        result = 1U;
+      }
+      UartTransport = transport;
+      break;
+    default:
+      result = 0U;
+      break;
+  }
+
+  if (result != 0U) {
+    *response = DAP_OK;
+  } else {
+    *response = DAP_ERROR;
+  }
+
+  return ((1U << 16) | 1U);
+}
+
+// Process UART Configure command and prepare response
+//   request:  pointer to request data
+//   response: pointer to response data
+//   return:   number of bytes in response (lower 16 bits)
+//             number of bytes in request (upper 16 bits)
+uint32_t UART_Configure (const uint8_t *request, uint8_t *response) {
+  uint8_t  control, status;
+  uint32_t baudrate;
+  int32_t  result;
+
+  status   = 0U;
+  control  = *request;
+  baudrate = (uint32_t)(*(request+1) <<  0) |
+             (uint32_t)(*(request+2) <<  8) |
+             (uint32_t)(*(request+3) << 16) |
+             (uint32_t)(*(request+4) << 24);
+
+  if (UartTransport == DAP_UART_TRANSPORT_DAP_COMMAND) {
+    result = pUSART->Control (control |
+                              ARM_USART_MODE_ASYNCHRONOUS |
+                              ARM_USART_FLOW_CONTROL_NONE,
+                              baudrate);
+    switch (result) {
+      case ARM_USART_ERROR_BAUDRATE:
+        status = 0U;
+        baudrate = 0U;
+        break;
+      case ARM_USART_ERROR_DATA_BITS:
+        status = (1U << 0);
+        break;
+      case ARM_USART_ERROR_STOP_BITS:
+        status = (1U << 1);
+        break;
+      case ARM_USART_ERROR_PARITY:
+        status = (1U << 2);
+        break;
+    }
+  }
+
+  if ((status == 0U) && (baudrate != 0U)) {
+    UartControl = control;
+    UartBaudrate = baudrate;
+  }
+
+  *response++ = status;
+  *response++ = (uint8_t)(baudrate >>  0);
+  *response++ = (uint8_t)(baudrate >>  8);
+  *response++ = (uint8_t)(baudrate >> 16);
+  *response   = (uint8_t)(baudrate >> 24);
+
+  return ((5U << 16) | 5U);
+}
+
+// Process UART Transfer command and prepare response
+//   request:  pointer to request data
+//   response: pointer to response data
+//   return:   number of bytes in response (lower 16 bits)
+//             number of bytes in request (upper 16 bits)
+uint32_t UART_Transfer (const uint8_t *request, uint8_t *response) {
+  uint16_t status = 0U;
+  uint32_t count, index;
+  uint32_t tx_num, rx_num, num;
+  uint8_t * data;
+
+  if (UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) {
+    return (0U);
+  }
+
+  // TX Data
+  tx_num = ((uint16_t)(*(request+0) <<  0)  |
+            (uint16_t)(*(request+1) <<  8));
+  data = (uint8_t *)((uint32_t)request) + 2;
+
+  if (tx_num > UART_MAX_XFER_NUM) {
+    tx_num = UART_MAX_XFER_NUM;
+  }
+
+  count = UartTxIndexI - UartTxIndexO;
+  index = UartTxIndexI & (UART_TX_BUF_SIZE - 1);
+
+  if ((UART_TX_BUF_SIZE - count) >= tx_num) {
+    if ((index + tx_num) < UART_TX_BUF_SIZE) {
+      memcpy(&UartTxBuf[index], data, tx_num);
+    } else {
+      num = UART_TX_BUF_SIZE - index;
+      memcpy(&UartTxBuf[index], data, num);
+      memcpy(&UartTxBuf[0], data + num, tx_num - num);
+    }
+    UartTxIndexI += tx_num;
+
+    if (pUSART->GetStatus().tx_busy == 0U) {
+      UART_Send();
+    }
+  } else {
+    // Tx Data lost
+    status |= DAP_UART_TRANSFER_TX_DATA_LOST;
+  }
+  if ((UART_TX_BUF_SIZE - count) < UART_MAX_XFER_NUM) {
+    // Can't accept next full TX packet
+    status |= DAP_UART_TRANSFER_TX_BUSY;
+  }
+
+  // RX Data
+  rx_num  = UartRxIndexI - UartRxIndexO;
+  rx_num += pUSART->GetRxCount();
+  data = response + 2;
+  index = UartRxIndexO & (UART_RX_BUF_SIZE - 1);
+
+  if (rx_num > UART_MAX_XFER_NUM) {
+    rx_num = UART_MAX_XFER_NUM;
+  }
+  if ((index + rx_num) < UART_RX_BUF_SIZE) {
+    memcpy(data, &UartRxBuf[index], rx_num);
+  } else {
+    num = UART_RX_BUF_SIZE - index;
+    memcpy(data, &UartRxBuf[index], num);
+    memcpy(data + num, &UartRxBuf[0], rx_num - num);
+  }
+  UartRxIndexO += rx_num;
+
+  if (pUSART->GetStatus().rx_busy == 0U) {
+    UART_Receive();
+  }
+
+  if (UartRxDataLost == 1U) {
+    UartRxDataLost = 0U;
+    status |= DAP_UART_TRANSFER_RX_DATA_LOST;
+  }
+  if (UartFramingError == 1U) {
+    UartFramingError = 0U;
+    status |= DAP_UART_TRANSFER_FRAMING_ERROR;
+  }
+  if (UartParityError == 1U) {
+    UartParityError = 0U;
+    status |= DAP_UART_TRANSFER_PARITY_ERROR;
+  }
+
+  status |= rx_num;
+
+  *response++ = (uint8_t)(status >> 0);
+  *response++ = (uint8_t)(status >> 8);
+
+  return (((2U + tx_num) << 16) | (2U + rx_num));
+}
+
+#endif /* DAP_UART */

+ 378 - 0
CMSIS/DAP/Firmware/Template/MDK5/USBD_User_CDC_ACM_UART_0.c

@@ -0,0 +1,378 @@
+/*------------------------------------------------------------------------------
+ * MDK Middleware - Component ::USB:Device:CDC
+ * Copyright (c) 2004-2021 Arm Limited (or its affiliates). All rights reserved.
+ *------------------------------------------------------------------------------
+ * Name:    USBD_User_CDC_ACM_UART_0.c
+ * Purpose: USB Device Communication Device Class (CDC)
+ *          Abstract Control Model (ACM) USB <-> UART Bridge User module
+ * Rev.:    V1.0.8
+ *----------------------------------------------------------------------------*/
+/**
+ * \addtogroup usbd_cdcFunctions
+ *
+ * USBD_User_CDC_ACM_UART_0.c implements the application specific
+ * functionality of the CDC ACM class and is used to demonstrate a USB <-> UART
+ * bridge. All data received on USB is transmitted on UART and all data
+ * received on UART is transmitted on USB.
+ *
+ * Details of operation:
+ *   UART -> USB:
+ *     Initial reception on UART is started after the USB Host sets line coding
+ *     with SetLineCoding command. Having received a full UART buffer, any
+ *     new reception is restarted on the same buffer. Any data received on
+ *     the UART is sent over USB using the CDC0_ACM_UART_to_USB_Thread thread.
+ *   USB -> UART:
+ *     While the UART transmit is not busy, data transmission on the UART is
+ *     started in the USBD_CDC0_ACM_DataReceived callback as soon as data is
+ *     received on the USB. Further data received on USB is transmitted on
+ *     UART in the UART callback routine until there is no more data available.
+ *     In this case, the next UART transmit is restarted from the
+ *     USBD_CDC0_ACM_DataReceived callback as soon as new data is received
+ *     on the USB.
+ *
+ * The following constants in this module affect the module functionality:
+ *
+ *  - UART_PORT:        specifies UART Port
+ *      default value:  0 (=UART0)
+ *  - UART_BUFFER_SIZE: specifies UART data Buffer Size
+ *      default value:  512
+ *
+ * Notes:
+ *   If the USB is slower than the UART, data can get lost. This may happen
+ *   when USB is pausing during data reception because of the USB Host being
+ *   too loaded with other tasks and not polling the Bulk IN Endpoint often
+ *   enough (up to 2 seconds of gap in polling Bulk IN Endpoint may occur).
+ *   This problem can be solved by using a large enough UART buffer to
+ *   compensate up to a few seconds of received UART data or by using UART
+ *   flow control.
+ *   If the device that receives the UART data (usually a PC) is too loaded
+ *   with other tasks it can also loose UART data. This problem can only be
+ *   solved by using UART flow control.
+ *
+ *   This file has to be adapted in case of UART flow control usage.
+ */
+ 
+ 
+//! [code_USBD_User_CDC_ACM]
+#include <stdio.h>
+#include <string.h>
+ 
+#include "rl_usb.h"
+ 
+#include "Driver_USART.h"
+ 
+#include "DAP_config.h"
+#include "DAP.h"
+ 
+// UART Configuration ----------------------------------------------------------
+ 
+#define  UART_BUFFER_SIZE      (512)       // UART Buffer Size
+ 
+//------------------------------------------------------------------------------
+ 
+#define _UART_Driver_(n)        Driver_USART##n
+#define  UART_Driver_(n)       _UART_Driver_(n)
+extern   ARM_DRIVER_USART       UART_Driver_(DAP_UART_DRIVER);
+#define  ptrUART              (&UART_Driver_(DAP_UART_DRIVER))
+ 
+// Local Variables
+static            uint8_t       uart_rx_buf[UART_BUFFER_SIZE];
+static            uint8_t       uart_tx_buf[UART_BUFFER_SIZE];
+ 
+static   volatile int32_t       uart_rx_cnt         =   0;
+static   volatile int32_t       usb_tx_cnt          =   0;
+ 
+static   void                  *cdc_acm_bridge_tid  =   0U;
+static   CDC_LINE_CODING        cdc_acm_line_coding = { 0U, 0U, 0U, 0U };
+ 
+static            uint8_t       cdc_acm_active      =   1U;
+static        osMutexId_t       cdc_acm_mutex_id    =   NULL;
+ 
+// Acquire mutex
+__STATIC_INLINE void CDC_ACM_Lock (void) {
+  if (cdc_acm_mutex_id == NULL) {
+    cdc_acm_mutex_id = osMutexNew(NULL);
+  }
+  osMutexAcquire(cdc_acm_mutex_id, osWaitForever);
+}
+ 
+// Release mutex
+__STATIC_INLINE void CDC_ACM_Unlock (void) {
+  osMutexRelease(cdc_acm_mutex_id);
+}
+ 
+// Change communication settings.
+// \param[in]   line_coding   pointer to CDC_LINE_CODING structure.
+// \return      true          set line coding request processed.
+// \return      false         set line coding request not supported or not processed.
+static bool CDC_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
+  uint32_t data_bits = 0U, parity = 0U, stop_bits = 0U;
+  int32_t  status;
+ 
+  (void)ptrUART->Control (ARM_USART_ABORT_SEND,    0U);
+  (void)ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
+  (void)ptrUART->Control (ARM_USART_CONTROL_TX,    0U);
+  (void)ptrUART->Control (ARM_USART_CONTROL_RX,    0U);
+ 
+  switch (line_coding->bCharFormat) {
+    case 0:                             // 1 Stop bit
+      stop_bits = ARM_USART_STOP_BITS_1;
+      break;
+    case 1:                             // 1.5 Stop bits
+      stop_bits = ARM_USART_STOP_BITS_1_5;
+      break;
+    case 2:                             // 2 Stop bits
+      stop_bits = ARM_USART_STOP_BITS_2;
+      break;
+    default:
+      return false;
+  }
+ 
+  switch (line_coding->bParityType) {
+    case 0:                             // None
+      parity = ARM_USART_PARITY_NONE;
+      break;
+    case 1:                             // Odd
+      parity = ARM_USART_PARITY_ODD;
+      break;
+    case 2:                             // Even
+      parity = ARM_USART_PARITY_EVEN;
+      break;
+    default:
+      return false;
+  }
+ 
+  switch (line_coding->bDataBits) {
+    case 5:
+      data_bits = ARM_USART_DATA_BITS_5;
+      break;
+    case 6:
+      data_bits = ARM_USART_DATA_BITS_6;
+      break;
+    case 7:
+      data_bits = ARM_USART_DATA_BITS_7;
+      break;
+    case 8:
+      data_bits = ARM_USART_DATA_BITS_8;
+      break;
+    default:
+      return false;
+  }
+ 
+  status = ptrUART->Control(ARM_USART_MODE_ASYNCHRONOUS  |
+                            data_bits                    |
+                            parity                       |
+                            stop_bits                    ,
+                            line_coding->dwDTERate       );
+ 
+  if (status != ARM_DRIVER_OK) {
+    return false;
+  }
+ 
+  // Store requested settings to local variable
+  memcpy(&cdc_acm_line_coding, line_coding, sizeof(cdc_acm_line_coding));
+ 
+  uart_rx_cnt = 0;
+  usb_tx_cnt  = 0;
+ 
+  (void)ptrUART->Control (ARM_USART_CONTROL_TX, 1U);
+  (void)ptrUART->Control (ARM_USART_CONTROL_RX, 1U);
+ 
+  (void)ptrUART->Receive (uart_rx_buf, UART_BUFFER_SIZE);
+ 
+  return true;
+}
+ 
+// Activate or Deactivate USBD COM PORT
+// \param[in]   cmd  0=deactivate, 1=activate
+void USB_COM_PORT_Activate (uint32_t cmd) {
+  switch (cmd) {
+    case 0:
+      cdc_acm_active = 0U;
+      USBD_CDC0_ACM_Uninitialize();
+      break;
+    case 1:
+      USBD_CDC0_ACM_Initialize();
+      CDC_ACM_Lock();
+      CDC_ACM_SetLineCoding(&cdc_acm_line_coding);
+      cdc_acm_active = 1U;
+      CDC_ACM_Unlock();
+      break;
+  }
+}
+ 
+// Called when UART has transmitted or received requested number of bytes.
+// \param[in]   event         UART event
+//               - ARM_USART_EVENT_SEND_COMPLETE:    all requested data was sent
+//               - ARM_USART_EVENT_RECEIVE_COMPLETE: all requested data was received
+static void UART_Callback (uint32_t event) {
+  int32_t cnt;
+ 
+  if (cdc_acm_active == 0U) {
+    return;
+  }
+ 
+  if (event & ARM_USART_EVENT_SEND_COMPLETE) {
+    // USB -> UART
+    cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE);
+    if (cnt > 0) {
+      (void)ptrUART->Send(uart_tx_buf, (uint32_t)(cnt));
+    }
+  }
+ 
+  if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) {
+    // UART data received, restart new reception
+    uart_rx_cnt += UART_BUFFER_SIZE;
+    (void)ptrUART->Receive(uart_rx_buf, UART_BUFFER_SIZE);
+  }
+}
+ 
+// Thread: Sends data received on UART to USB
+// \param[in]     arg           not used.
+__NO_RETURN static void CDC0_ACM_UART_to_USB_Thread (void *arg) {
+  int32_t cnt, cnt_to_wrap;
+ 
+  (void)(arg);
+ 
+  for (;;) {
+    // UART - > USB
+    if (ptrUART->GetStatus().rx_busy != 0U) {
+      cnt  = uart_rx_cnt;
+      cnt += (int32_t)ptrUART->GetRxCount();
+      cnt -= usb_tx_cnt;
+      if (cnt >= UART_BUFFER_SIZE) {
+        // Dump data received on UART if USB is not consuming fast enough
+        usb_tx_cnt += cnt;
+        cnt = 0U;
+      }
+      if (cnt > 0) {
+        cnt_to_wrap = (int32_t)(UART_BUFFER_SIZE - ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1)));
+        if (cnt > cnt_to_wrap) {
+          cnt = cnt_to_wrap;
+        }
+        cnt = USBD_CDC_ACM_WriteData(0U, (uart_rx_buf + ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1))), cnt);
+        if (cnt > 0) {
+          usb_tx_cnt += cnt;
+        }
+      }
+    }
+    (void)osDelay(10U);
+  }
+}
+ 
+static osRtxThread_t        cdc0_acm_uart_to_usb_thread_cb_mem               __SECTION(.bss.os.thread.cb);
+static uint64_t             cdc0_acm_uart_to_usb_thread_stack_mem[512U / 8U] __SECTION(.bss.os.thread.cdc.stack);
+static const osThreadAttr_t cdc0_acm_uart_to_usb_thread_attr = {
+  "CDC0_ACM_UART_to_USB_Thread",
+  0U,
+ &cdc0_acm_uart_to_usb_thread_cb_mem,
+  sizeof(osRtxThread_t),
+ &cdc0_acm_uart_to_usb_thread_stack_mem[0],
+  sizeof(cdc0_acm_uart_to_usb_thread_stack_mem),
+  osPriorityNormal,
+  0U,
+  0U
+};
+ 
+ 
+// CDC ACM Callbacks -----------------------------------------------------------
+ 
+// Called when new data was received from the USB Host.
+// \param[in]   len           number of bytes available to read.
+void USBD_CDC0_ACM_DataReceived (uint32_t len) {
+  int32_t cnt;
+ 
+  (void)(len);
+ 
+  if (cdc_acm_active == 0U) {
+    return;
+  }
+ 
+  if (ptrUART->GetStatus().tx_busy == 0U) {
+    // Start USB -> UART
+    cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE);
+    if (cnt > 0) {
+      (void)ptrUART->Send(uart_tx_buf, (uint32_t)(cnt));
+    }
+  }
+}
+ 
+// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
+void USBD_CDC0_ACM_Initialize (void) {
+  (void)ptrUART->Initialize   (UART_Callback);
+  (void)ptrUART->PowerControl (ARM_POWER_FULL);
+ 
+  cdc_acm_bridge_tid = osThreadNew (CDC0_ACM_UART_to_USB_Thread, NULL, &cdc0_acm_uart_to_usb_thread_attr);
+}
+ 
+ 
+// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
+void USBD_CDC0_ACM_Uninitialize (void) {
+  if (osThreadTerminate (cdc_acm_bridge_tid) == osOK) {
+    cdc_acm_bridge_tid = NULL;
+  }
+  (void)ptrUART->Control      (ARM_USART_ABORT_RECEIVE, 0U);
+  (void)ptrUART->PowerControl (ARM_POWER_OFF);
+  (void)ptrUART->Uninitialize ();
+}
+ 
+ 
+// Called upon USB Bus Reset Event.
+void USBD_CDC0_ACM_Reset (void) {
+  if (cdc_acm_active == 0U ) {
+    return;
+  }
+  (void)ptrUART->Control      (ARM_USART_ABORT_SEND,    0U);
+  (void)ptrUART->Control      (ARM_USART_ABORT_RECEIVE, 0U);
+}
+ 
+ 
+// Called upon USB Host request to change communication settings.
+// \param[in]   line_coding   pointer to CDC_LINE_CODING structure.
+// \return      true          set line coding request processed.
+// \return      false         set line coding request not supported or not processed.
+bool USBD_CDC0_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
+  bool ret = false;
+ 
+  CDC_ACM_Lock();
+  if (cdc_acm_active == 0U) {
+    // Store requested settings to local variable
+    memcpy(&cdc_acm_line_coding, line_coding, sizeof(cdc_acm_line_coding));
+    ret = true;
+  } else {
+    ret = CDC_ACM_SetLineCoding(line_coding);
+  }
+  CDC_ACM_Unlock();
+ 
+  return ret;
+}
+ 
+ 
+// Called upon USB Host request to retrieve communication settings.
+// \param[out]  line_coding   pointer to CDC_LINE_CODING structure.
+// \return      true          get line coding request processed.
+// \return      false         get line coding request not supported or not processed.
+bool USBD_CDC0_ACM_GetLineCoding (CDC_LINE_CODING *line_coding) {
+ 
+  // Load settings from ones stored on USBD_CDC0_ACM_SetLineCoding callback
+  *line_coding = cdc_acm_line_coding;
+ 
+  return true;
+}
+ 
+ 
+// Called upon USB Host request to set control line states.
+// \param [in]  state         control line settings bitmap.
+//                - bit 0: DTR state
+//                - bit 1: RTS state
+// \return      true          set control line state request processed.
+// \return      false         set control line state request not supported or not processed.
+bool USBD_CDC0_ACM_SetControlLineState (uint16_t state) {
+  // Add code for set control line state
+ 
+  (void)(state);
+ 
+  return true;
+}
+ 
+//! [code_USBD_User_CDC_ACM]