USB Component  Version 6.13.0
MDK Middleware for USB Device and Host Communication
 All Data Structures Functions Variables Enumerations Enumerator Groups Pages
CDC: Communication Device Class (ACM)

Implement application specific behavior of a Communication Device Class (CDC) USB Device using the sub-class Abstract Control Model (ACM). More...

Content

 User API
 User API reference of the Communication Device Class (ACM).
 
 Configuration
 Configuration of the USB Device CDC (ACM) Class in ┬ÁVision.
 

Description

Implement application specific behavior of a Communication Device Class (CDC) USB Device using the sub-class Abstract Control Model (ACM).

The CDC (ACM) class in the USB Component is used for data communication. You can typically use it in applications that previously used a serial COM or UART communication.

Refer to:

The USB Component allows multiple instances of the CDC class. This feature is used to create USB Composite Devices. Each CDC class instance has a separate files and interface functions:

This documentation uses n as a placeholder for the instance number 0 - 7. Most applications only require one instance of a CDC ACM class. For the first CDC ACM class instance the instance number is 0:

Descriptor Requirements

The following descriptors are required in an USB CDC ACM Device:

The necessary descriptors are automatically generated by the USB Middleware Component. The page USB Descriptors provides more information on the topic.

Software Structure

The handling for the CDC class endpoint events is implemented in USBD_CDCn_Int_Thread and USBD_CDCn_Bulk_Thread which are started by USBD_Initialize. Each instance of a CDC class runs an instance of USBD_CDCn_Int_Thread and USBD_CDCn_Bulk_Thread.

The thread USBD_CDCn_Int_Thread handles Interrupt IN Endpoint whereas the USBD_CDCn_Bulk_Thread handles the Bulk IN and Bulk OUT Endpoints.

msc_inline_mscgraph_3

Implementation

To create an USB Device with a CDC ACM class:

User Code Template USBD_User_CDC_ACM_n.c

The following source code contains all the required callback functions and can be used to implement the application specific behavior of a USB CDC (ACM) Device.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device
* Copyright (c) 2004-2018 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_CDC_ACM_n.c
* Purpose: USB Device Communication Device Class (CDC)
* Abstract Control Model (ACM) User module
* Rev.: V6.4.0
*----------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include "rl_usb.h"
// Local Variables
static CDC_LINE_CODING cdc_acm_line_coding = { 0U, 0U, 0U, 0U };
// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
// Add code for initialization
}
// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
// Add code for de-initialization
}
// Called upon USB Bus Reset Event.
void USBD_CDCn_ACM_Reset (void) {
// Add code for reset
}
// Callback function called upon reception of request send encapsulated command sent by the USB Host.
// \param[in] buf buffer that contains send encapsulated command request.
// \param[in] len length of send encapsulated command request.
// \return true send encapsulated command request processed.
// \return false send encapsulated command request not supported or not processed.
bool USBD_CDCn_ACM_SendEncapsulatedCommand (const uint8_t *buf, uint16_t len) {
return true;
}
// Callback function called upon reception of request to get encapsulated response sent by the USB Host.
// \param[in] max_len maximum number of data bytes that USB Host expects to receive
// \param[out] buf pointer to buffer containing get encapsulated response to be returned to USB Host.
// \param[out] len pointer to number of data bytes to be returned to USB Host.
// \return true get encapsulated response request processed.
// \return false get encapsulated response request not supported or not processed.
bool USBD_CDCn_ACM_GetEncapsulatedResponse (uint16_t max_len, uint8_t **buf, uint16_t *len) {
return true;
}
// 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_CDCn_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
// Add code for set line coding
// Store requested settings to local variable
cdc_acm_line_coding = *line_coding;
return true;
}
// 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.
// Load settings from ones stored on USBD_CDCn_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_CDCn_ACM_SetControlLineState (uint16_t state) {
// Add code for set control line state
(void)(state);
return true;
}
// Called when new data was received.
// \param [in] len number of bytes available for reading.
void USBD_CDCn_ACM_DataReceived (uint32_t len) {
// Add code for handling new data reception
}
// Called when when all data was sent.
// Add code for handling new data send
}

User Code Template USBD_User_CDC_ACM_UART_n.c

The following source code contains all the required callback functions and can be used to implement the application specific behavior of a USB CDC (ACM) Device that can be used as a USB <-> UART bridge.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device
* Copyright (c) 2004-2017 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_CDC_ACM_UART_n.c
* Purpose: USB Device Communication Device Class (CDC)
* Abstract Control Model (ACM) USB <-> UART Bridge User module
* Rev.: V1.0.7
*----------------------------------------------------------------------------*/
#include <stdio.h>
#include "rl_usb.h"
#if defined(RTE_CMSIS_RTOS2)
#include "cmsis_os2.h"
#if defined(RTE_CMSIS_RTOS2_RTX5)
#include "rtx_os.h"
#endif
#endif
#if defined(RTE_CMSIS_RTOS)
#include "cmsis_os.h"
#endif
#include "Driver_USART.h"
// UART Configuration ----------------------------------------------------------
#define UART_PORT n // UART Port number
#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_(UART_PORT);
#define ptrUART (&UART_Driver_(UART_PORT))
// External functions
#ifdef USB_CMSIS_RTOS
extern void CDCn_ACM_UART_to_USB_Thread (void const *arg);
#endif
// 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 };
// 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 (event & ARM_USART_EVENT_SEND_COMPLETE) {
// USB -> UART
cnt = USBD_CDC_ACM_ReadData(nU, uart_tx_buf, UART_BUFFER_SIZE);
if (cnt > 0) {
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;
ptrUART->Receive(uart_rx_buf, UART_BUFFER_SIZE);
}
}
// Thread: Sends data received on UART to USB
// \param[in] arg not used.
#ifdef USB_CMSIS_RTOS2
__NO_RETURN static void CDCn_ACM_UART_to_USB_Thread (void *arg) {
#else
__NO_RETURN void CDCn_ACM_UART_to_USB_Thread (void const *arg) {
#endif
int32_t cnt, cnt_to_wrap;
(void)(arg);
while (1) {
// UART - > USB
if (ptrUART->GetStatus().rx_busy != 0U) {
cnt = uart_rx_cnt;
cnt += 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(nU, (uart_rx_buf + ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1))), cnt);
if (cnt > 0) {
usb_tx_cnt += cnt;
}
}
}
osDelay(10U);
}
}
#ifdef USB_CMSIS_RTOS2
#ifdef USB_CMSIS_RTOS2_RTX5
static osRtxThread_t cdcn_acm_uart_to_usb_thread_cb_mem __SECTION(.bss.os.thread.cb);
static uint64_t cdcn_acm_uart_to_usb_thread_stack_mem[512U / 8U] __SECTION(.bss.os.thread.stack);
#endif
static const osThreadAttr_t cdcn_acm_uart_to_usb_thread_attr = {
"CDCn_ACM_UART_to_USB_Thread",
0U,
#ifdef USB_CMSIS_RTOS2_RTX5
&cdcn_acm_uart_to_usb_thread_cb_mem,
sizeof(osRtxThread_t),
&cdcn_acm_uart_to_usb_thread_stack_mem[0],
#else
NULL,
0U,
NULL,
#endif
512U,
osPriorityNormal,
0U,
0U
};
#else
extern const osThreadDef_t os_thread_def_CDCn_ACM_UART_to_USB_Thread;
osThreadDef (CDCn_ACM_UART_to_USB_Thread, osPriorityNormal, 1U, 0U);
#endif
// CDC ACM Callbacks -----------------------------------------------------------
// Called when new data was received from the USB Host.
// \param[in] len number of bytes available to read.
void USBD_CDCn_ACM_DataReceived (uint32_t len) {
int32_t cnt;
(void)(len);
if (ptrUART->GetStatus().tx_busy == 0U) {
// Start USB -> UART
cnt = USBD_CDC_ACM_ReadData(nU, uart_tx_buf, UART_BUFFER_SIZE);
if (cnt > 0) {
ptrUART->Send(uart_tx_buf, (uint32_t)(cnt));
}
}
}
// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
ptrUART->Initialize (UART_Callback);
ptrUART->PowerControl (ARM_POWER_FULL);
#ifdef USB_CMSIS_RTOS2
cdc_acm_bridge_tid = osThreadNew (CDCn_ACM_UART_to_USB_Thread, NULL, &cdcn_acm_uart_to_usb_thread_attr);
#else
cdc_acm_bridge_tid = osThreadCreate (osThread (CDCn_ACM_UART_to_USB_Thread), NULL);
#endif
}
// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
if (osThreadTerminate (cdc_acm_bridge_tid) == osOK) {
cdc_acm_bridge_tid = NULL;
}
ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
ptrUART->PowerControl (ARM_POWER_OFF);
ptrUART->Uninitialize ();
}
// Called upon USB Bus Reset Event.
void USBD_CDCn_ACM_Reset (void) {
ptrUART->Control (ARM_USART_ABORT_SEND, 0U);
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_CDCn_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
uint32_t data_bits = 0U, parity = 0U, stop_bits = 0U;
int32_t status;
ptrUART->Control (ARM_USART_ABORT_SEND, 0U);
ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
ptrUART->Control (ARM_USART_CONTROL_TX, 0U);
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;
}
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 |
ARM_USART_FLOW_CONTROL_NONE ,
line_coding->dwDTERate );
if (status != ARM_DRIVER_OK) {
return false;
}
// Store requested settings to local variable
cdc_acm_line_coding = *line_coding;
uart_rx_cnt = 0;
usb_tx_cnt = 0;
ptrUART->Control (ARM_USART_CONTROL_TX, 1U);
ptrUART->Control (ARM_USART_CONTROL_RX, 1U);
ptrUART->Receive (uart_rx_buf, UART_BUFFER_SIZE);
return true;
}
// 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.
// Load settings from ones stored on USBD_CDCn_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_CDCn_ACM_SetControlLineState (uint16_t state) {
// Add code for set control line state
(void)(state);
return true;
}

User Code Template USBD_User_CDC_ACM_RNDIS_VETH_n.c

The following source code contains all the required callback functions and can be used to implement the application specific behavior of a USB CDC (ACM) Device that can be used for virtual Ethernet using the RNDIS protocol.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device
* Copyright (c) 2018 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_CDC_ACM_RNDIS_VETH_n.c
* Purpose: USB Device Communication Device Class (CDC)
* Abstract Control Model (ACM)
* Remote Network Driver Interface Specification (RNDIS)
* User Module for a Virtual Ethernet
* Rev.: V1.0.0
*----------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "rl_usb.h"
#include "Driver_ETH.h"
#include "Driver_ETH_MAC.h"
#include "Driver_ETH_PHY.h"
#include "RTE/USB/USBD_Config_CDC_n.h"
//-------- <<< Use Configuration Wizard in Context Menu >>> --------------------
// Configuration defines
// <s.17>MAC Address
// <i>Ethernet MAC Address in text representation
// <i>Value FF-FF-FF-FF-FF-FF is not allowed,
// <i>LSB of first byte must be 0 (an ethernet Multicast bit).
// <i>Default: "1E-30-6C-A2-45-5E"
#define RNDIS_MAC_ADDR "1E-30-6C-A2-45-5E" // RNDIS MAC Address
// <o.0..5>Maximum number of multicast addresses <1-32>
#define RNDIS_MCAST_NUM 16 // RNDIS Number of Multicast Addresses supported
// <s.32>RNDIS Vendor Description
#define RNDIS_VENDOR_DESC "Keil NIC (USB <-> ETH)" // RNDIS Vendor Description
// <o.0..23>RNDIS Vendor Id Code <0x000000-0xFFFFFF>
#define RNDIS_VENDOR_ID 0xFFFFFF // RNDIS three-byte IEEE-registered Vendor Code
//------------- <<< end of configuration section >>> ---------------------------
// Global functions exported by this module
ARM_ETH_LINK_STATE RNDISn_GetLinkState (void);
int32_t RNDISn_SendFrame (const uint8_t *frame, uint32_t len);
int32_t RNDISn_ReadFrame ( uint8_t *frame, uint32_t len);
uint32_t RNDISn_GetRxFrameSize(void);
// Local functions
static void MAC_str_to_addr (const char *mac_str, uint8_t *mac_addr);
static void InitVars (void);
static void ResetVars (void);
// Local variables
static uint32_t rndis_state;
static ARM_ETH_LINK_STATE link_state;
static bool link_state_up;
static bool link_state_down;
static uint32_t packet_filter;
static ARM_ETH_MAC_ADDR mac_address;
static ARM_ETH_MAC_ADDR mcast_address[RNDIS_MCAST_NUM];
static uint16_t get_encapsulated_response_len;
static uint32_t get_encapsulated_response_buf[sizeof(REMOTE_NDIS_INITIALIZE_CMPLT_t)];
static uint32_t xmit_ok;
static uint32_t rcv_ok;
static uint32_t xmit_error;
static uint32_t rcv_error;
static uint32_t rcv_no_buffer;
static uint32_t packet_in [(USBD_CDCn_ACM_SEND_BUF_SIZE +3)/4];
static uint32_t packet_out[(USBD_CDCn_ACM_RECEIVE_BUF_SIZE+3)/4];
// Local functions
// MAC Address conversion from string
// \param[in] mac_str Pointer to wide string.
// \param[out] mac_addr Pointer to address.
static void MAC_str_to_addr (const char *mac_str, uint8_t *mac_addr) {
uint8_t c;
uint8_t n;
uint8_t i;
uint8_t str_len;
str_len = strlen(mac_str);
for (i = 0U; i < str_len; i++) {
c = mac_str[i];
if (c == '-') {
continue;
} else if ((c >= '0') && (c <= '9')) {
n = c - '0';
} else if ((c >= 'A') && (c <= 'F')) {
n = c - 'A' + 10U;
} else if ((c >= 'a') && (c <= 'f')) {
n = c - 'a' + 10U;
} else {
n = 0U;
}
if ((i & 1U) != 0U) {
mac_addr[i>>1] |= n;
} else {
mac_addr[i>>1] = n << 4;
}
}
}
// Initialize variables
void InitVars (void) {
rndis_state = RNDIS_UNINITIALIZED;
link_state = ARM_ETH_LINK_DOWN;
packet_filter = 0U;
MAC_str_to_addr(RNDIS_MAC_ADDR, (uint8_t *)&mac_address);
memset((void *)mcast_address, 0, sizeof(mcast_address));
ResetVars();
}
// Reset variables
static void ResetVars (void) {
link_state_up = false;
link_state_down = false;
get_encapsulated_response_len = 0U;
xmit_ok = 0U;
rcv_ok = 0U;
xmit_error = 0U;
rcv_error = 0U;
rcv_no_buffer = 0U;
}
// USB CDC ACM callback global functions
// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
InitVars();
}
// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
InitVars();
}
// Called upon USB Bus Reset Event.
void USBD_CDCn_ACM_Reset (void) {
InitVars();
}
// Callback function called upon reception of request send encapsulated command sent by the USB Host.
// \param[in] buf buffer that contains send encapsulated command request.
// \param[in] len length of send encapsulated command request.
// \return true send encapsulated command request processed.
// \return false send encapsulated command request not supported or not processed.
bool USBD_CDCn_ACM_SendEncapsulatedCommand (const uint8_t *buf, uint16_t len) {
REMOTE_NDIS_INITIALIZE_MSG_t *ptr_init_msg;
REMOTE_NDIS_INITIALIZE_CMPLT_t *ptr_init_cmplt;
REMOTE_NDIS_HALT_MSG_t *ptr_halt_msg;
REMOTE_NDIS_QUERY_MSG_t *ptr_query_msg;
REMOTE_NDIS_QUERY_CMPLT_t *ptr_query_cmplt;
REMOTE_NDIS_SET_MSG_t *ptr_set_msg;
REMOTE_NDIS_SET_CMPLT_t *ptr_set_cmplt;
REMOTE_NDIS_RESET_MSG_t *ptr_reset_msg;
REMOTE_NDIS_RESET_CMPLT_t *ptr_reset_cmplt;
REMOTE_NDIS_KEEPALIVE_MSG_t *ptr_keepalive_msg;
REMOTE_NDIS_KEEPALIVE_CMPLT_t *ptr_keepalive_cmplt;
uint32_t status, val;
int32_t i;
uint32_t num, by;
uint16_t msg_type;
msg_type = __UNALIGNED_UINT16_READ(buf); // Extract message type of received message
// In uninitialized state only allowed messages are INITALIZE and HALT
if ((rndis_state == RNDIS_UNINITIALIZED) &&
(msg_type != REMOTE_NDIS_INITIALIZE_MSG) &&
(msg_type != REMOTE_NDIS_HALT_MSG)) {
return false;
}
status = RNDIS_STATUS_SUCCESS; // Default message processing status
get_encapsulated_response_len = 0U; // Prepare default no response size
switch (msg_type) { // MessageType
case REMOTE_NDIS_INITIALIZE_MSG:
// Check message is valid
ptr_init_msg = (REMOTE_NDIS_INITIALIZE_MSG_t *)buf;
if (ptr_init_msg->MessageLength != sizeof(REMOTE_NDIS_INITIALIZE_MSG_t)) { return false; }
if (ptr_init_msg->MajorVersion != RNDIS_MAJOR_VERSION) { return false; }
if (ptr_init_msg->MinorVersion != RNDIS_MINOR_VERSION) { return false; }
if (ptr_init_msg->MaxTransferSize != 16384U) { return false; }
rndis_state = RNDIS_INITIALIZED;
// Prepare response
ptr_init_cmplt = (REMOTE_NDIS_INITIALIZE_CMPLT_t *)get_encapsulated_response_buf;
ptr_init_cmplt->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
ptr_init_cmplt->MessageLength = sizeof(REMOTE_NDIS_INITIALIZE_CMPLT_t);
ptr_init_cmplt->RequestID = ptr_init_msg->RequestID;
ptr_init_cmplt->Status = status;
ptr_init_cmplt->MajorVersion = RNDIS_MAJOR_VERSION;
ptr_init_cmplt->MinorVersion = RNDIS_MINOR_VERSION;
ptr_init_cmplt->DeviceFlags = RNDIS_DF_CONNECTIONLESS;
ptr_init_cmplt->Medium = NdisMedium802_3;
ptr_init_cmplt->MaxPacketsPerTransfer = 1U;
ptr_init_cmplt->MaxTransferSize = USBD_CDCn_ACM_RECEIVE_BUF_SIZE;
ptr_init_cmplt->PacketAlignmentFactor = 2U;
ptr_init_cmplt->Reserved[0] = 0U;
ptr_init_cmplt->Reserved[1] = 0U;
get_encapsulated_response_len = ptr_init_cmplt->MessageLength;
break;
case REMOTE_NDIS_HALT_MSG:
// Check message is valid
ptr_halt_msg = (REMOTE_NDIS_HALT_MSG_t *)buf;
if (ptr_halt_msg->MessageLength != sizeof(REMOTE_NDIS_HALT_MSG_t)) { return false; }
rndis_state = RNDIS_UNINITIALIZED;
// This message does not have a response
return true;
case REMOTE_NDIS_QUERY_MSG:
// Check message is valid
ptr_query_msg = (REMOTE_NDIS_QUERY_MSG_t *)buf;
if (ptr_query_msg->MessageLength < 28U) { return false; }
// Prepare response
ptr_query_cmplt = (REMOTE_NDIS_QUERY_CMPLT_t *)get_encapsulated_response_buf;
ptr_query_cmplt->MessageType = REMOTE_NDIS_QUERY_CMPLT;
ptr_query_cmplt->RequestID = ptr_query_msg->RequestID;
ptr_query_cmplt->InformationBufferOffset = 16U;
switch (ptr_query_msg->Oid) { // Handle OID
case OID_GEN_SUPPORTED_LIST:
ptr_query_cmplt->InformationBufferLength = 23U * 4U;
ptr_query_cmplt->OIDInputBuffer[0] = OID_GEN_SUPPORTED_LIST;
ptr_query_cmplt->OIDInputBuffer[1] = OID_GEN_HARDWARE_STATUS;
ptr_query_cmplt->OIDInputBuffer[2] = OID_GEN_MEDIA_SUPPORTED;
ptr_query_cmplt->OIDInputBuffer[3] = OID_GEN_MEDIA_IN_USE;
ptr_query_cmplt->OIDInputBuffer[4] = OID_GEN_MAXIMUM_FRAME_SIZE;
ptr_query_cmplt->OIDInputBuffer[5] = OID_GEN_LINK_SPEED;
ptr_query_cmplt->OIDInputBuffer[6] = OID_GEN_TRANSMIT_BLOCK_SIZE;
ptr_query_cmplt->OIDInputBuffer[7] = OID_GEN_RECEIVE_BLOCK_SIZE;
ptr_query_cmplt->OIDInputBuffer[8] = OID_GEN_VENDOR_ID;
ptr_query_cmplt->OIDInputBuffer[9] = OID_GEN_VENDOR_DESCRIPTION;
ptr_query_cmplt->OIDInputBuffer[10] = OID_GEN_CURRENT_PACKET_FILTER;
ptr_query_cmplt->OIDInputBuffer[11] = OID_GEN_MAXIMUM_TOTAL_SIZE;
ptr_query_cmplt->OIDInputBuffer[12] = OID_GEN_MEDIA_CONNECT_STATUS;
ptr_query_cmplt->OIDInputBuffer[13] = OID_GEN_PHYSICAL_MEDIUM;
ptr_query_cmplt->OIDInputBuffer[14] = OID_GEN_XMIT_OK;
ptr_query_cmplt->OIDInputBuffer[15] = OID_GEN_RCV_OK;
ptr_query_cmplt->OIDInputBuffer[16] = OID_GEN_XMIT_ERROR;
ptr_query_cmplt->OIDInputBuffer[17] = OID_GEN_RCV_ERROR;
ptr_query_cmplt->OIDInputBuffer[18] = OID_GEN_RCV_NO_BUFFER;
ptr_query_cmplt->OIDInputBuffer[19] = OID_802_3_PERMANENT_ADDRESS;
ptr_query_cmplt->OIDInputBuffer[20] = OID_802_3_CURRENT_ADDRESS;
ptr_query_cmplt->OIDInputBuffer[21] = OID_802_3_MULTICAST_LIST;
ptr_query_cmplt->OIDInputBuffer[22] = OID_802_3_MAXIMUM_LIST_SIZE;
break;
case OID_GEN_HARDWARE_STATUS:
ptr_query_cmplt->InformationBufferLength = 4U;
if (link_state == ARM_ETH_LINK_UP) {
ptr_query_cmplt->OIDInputBuffer[0] = NdisHardwareStatusReady;
} else {
ptr_query_cmplt->OIDInputBuffer[0] = NdisHardwareStatusNotReady;
}
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = NdisMedium802_3;
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = ETH_MTU_SIZE;
break;
case OID_GEN_LINK_SPEED:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = 100000000U / 100U; // 100 MBit/s
break;
case OID_GEN_TRANSMIT_BLOCK_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = USBD_CDCn_ACM_SEND_BUF_SIZE;
break;
case OID_GEN_RECEIVE_BLOCK_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = USBD_CDCn_ACM_RECEIVE_BUF_SIZE;
break;
case OID_GEN_VENDOR_ID:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = RNDIS_VENDOR_ID;
break;
case OID_GEN_VENDOR_DESCRIPTION:
ptr_query_cmplt->InformationBufferLength = strlen(RNDIS_VENDOR_DESC) + 1;
memset((void *)&ptr_query_cmplt->OIDInputBuffer[0], 0, ptr_query_cmplt->InformationBufferLength + 1U);
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], RNDIS_VENDOR_DESC, strlen(RNDIS_VENDOR_DESC));
break;
case OID_GEN_CURRENT_PACKET_FILTER:
ptr_query_cmplt->InformationBufferLength = 4U;
val = 0U;
if ((packet_filter & ARM_ETH_MAC_ADDRESS_MULTICAST) != 0U) { val |= RNDIS_FILTER_ALL_MULTICAST; }
if ((packet_filter & ARM_ETH_MAC_ADDRESS_BROADCAST) != 0U) { val |= RNDIS_FILTER_BROADCAST; }
if ((packet_filter & ARM_ETH_MAC_ADDRESS_ALL) != 0U) { val |= RNDIS_FILTER_PROMISCUOUS; }
ptr_query_cmplt->OIDInputBuffer[0] = val;
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U + ETH_MAX_SIZE;
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
ptr_query_cmplt->InformationBufferLength = 4U;
if (link_state == ARM_ETH_LINK_UP) {
ptr_query_cmplt->OIDInputBuffer[0] = NdisMediaStateConnected;
} else {
ptr_query_cmplt->OIDInputBuffer[0] = NdisMediaStateDisconnected;
}
break;
case OID_GEN_PHYSICAL_MEDIUM:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = NdisPhysicalMediumUnspecified;
break;
case OID_GEN_XMIT_OK:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = xmit_ok;
break;
case OID_GEN_RCV_OK:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_ok;
break;
case OID_GEN_XMIT_ERROR:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = xmit_error;
break;
case OID_GEN_RCV_ERROR:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_error;
break;
case OID_GEN_RCV_NO_BUFFER:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_no_buffer;
break;
case OID_802_3_PERMANENT_ADDRESS:
case OID_802_3_CURRENT_ADDRESS:
ptr_query_cmplt->InformationBufferLength = 6U;
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], &mac_address, sizeof(ARM_ETH_MAC_ADDR));
break;
case OID_802_3_MULTICAST_LIST:
for (i = 0U; i < RNDIS_MCAST_NUM; i++) {
if (memcmp(&mcast_address[i], "\0\0\0\0\0\0", 6) == 0) {
break;
}
}
if (i == 0U) {
num = 0U;
ptr_query_cmplt->InformationBufferOffset = 0U;
} else {
num = i + 1U;
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], mcast_address, num * sizeof(ARM_ETH_MAC_ADDR));
}
ptr_query_cmplt->InformationBufferLength = num * sizeof(ARM_ETH_MAC_ADDR);
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = RNDIS_MCAST_NUM;
break;
default:
ptr_query_cmplt->InformationBufferOffset = 0U;
ptr_query_cmplt->InformationBufferLength = 0U;
status = RNDIS_STATUS_NOT_SUPPORTED;
break;
}
ptr_query_cmplt->Status = status;
ptr_query_cmplt->MessageLength = ptr_query_cmplt->InformationBufferLength + 24U;
get_encapsulated_response_len = ptr_query_cmplt->MessageLength;
break;
case REMOTE_NDIS_SET_MSG:
// Check message is valid
ptr_set_msg = (REMOTE_NDIS_SET_MSG_t *)buf;
if (ptr_set_msg->MessageLength < 28U) { return false; }
// Prepare response
ptr_set_cmplt = (REMOTE_NDIS_SET_CMPLT_t *)get_encapsulated_response_buf;
ptr_set_cmplt->MessageType = REMOTE_NDIS_SET_CMPLT;
ptr_set_cmplt->MessageLength = sizeof(REMOTE_NDIS_SET_CMPLT_t);
ptr_set_cmplt->RequestID = ptr_set_msg->RequestID;
switch (ptr_set_msg->Oid) { // Handle OID
case OID_802_3_MULTICAST_LIST:
by = ptr_set_msg->InformationBufferLength;
if (by > (sizeof(ARM_ETH_MAC_ADDR) * RNDIS_MCAST_NUM)) {
by = sizeof(ARM_ETH_MAC_ADDR) * RNDIS_MCAST_NUM;
}
if (by > 0U) {
memcpy(mcast_address, (void *)&ptr_set_msg->OIDInputBuffer[0], by);
num = by / sizeof(ARM_ETH_MAC_ADDR);
}
break;
case OID_GEN_CURRENT_PACKET_FILTER:
if ((ptr_set_msg->InformationBufferLength == 4U) &&
(ptr_set_msg->InformationBufferOffset != 0U)) {
val = *(uint32_t *)(((uint8_t *)&ptr_set_msg->RequestID) + ptr_set_msg->InformationBufferOffset);
if (val != 0U) {
if ((val & RNDIS_FILTER_ALL_MULTICAST) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_MULTICAST; }
if ((val & RNDIS_FILTER_BROADCAST) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_BROADCAST; }
if ((val & RNDIS_FILTER_PROMISCUOUS) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_ALL; }
if (link_state == ARM_ETH_LINK_DOWN) {
link_state = ARM_ETH_LINK_UP;
link_state_up = true;
}
rndis_state = RNDIS_DATA_INITIALIZED;
} else {
if (rndis_state == RNDIS_DATA_INITIALIZED) {
rndis_state = RNDIS_INITIALIZED;
}
}
} else {
status = RNDIS_STATUS_FAILURE;
}
break;
default:
status = RNDIS_STATUS_NOT_SUPPORTED;
break;
}
ptr_set_cmplt->Status = status;
get_encapsulated_response_len = ptr_set_cmplt->MessageLength;
break;
case REMOTE_NDIS_RESET_MSG:
// Check message is valid
ptr_reset_msg = (REMOTE_NDIS_RESET_MSG_t *)buf;
if (ptr_reset_msg->MessageLength != sizeof(REMOTE_NDIS_RESET_MSG_t)) { return false; }
ResetVars();
// Prepare response
ptr_reset_cmplt = (REMOTE_NDIS_RESET_CMPLT_t *)get_encapsulated_response_buf;
ptr_reset_cmplt->MessageType = REMOTE_NDIS_RESET_CMPLT;
ptr_reset_cmplt->MessageLength = sizeof(REMOTE_NDIS_RESET_CMPLT_t);
ptr_reset_cmplt->Status = status;
ptr_reset_cmplt->AddressingReset = 0U;
get_encapsulated_response_len = ptr_reset_cmplt->MessageLength;
break;
case REMOTE_NDIS_KEEPALIVE_MSG:
// Check message is valid
ptr_keepalive_msg = (REMOTE_NDIS_KEEPALIVE_MSG_t *)buf;
if (ptr_keepalive_msg->MessageLength != sizeof(REMOTE_NDIS_KEEPALIVE_MSG_t)) { return false; }
// Prepare response
ptr_keepalive_cmplt = (REMOTE_NDIS_KEEPALIVE_CMPLT_t *)get_encapsulated_response_buf;
ptr_keepalive_cmplt->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
ptr_keepalive_cmplt->MessageLength = sizeof(REMOTE_NDIS_KEEPALIVE_CMPLT_t);
ptr_keepalive_cmplt->RequestID = ptr_keepalive_msg->RequestID;
ptr_keepalive_cmplt->Status = status;
get_encapsulated_response_len = ptr_keepalive_cmplt->MessageLength;
break;
default:
return false;
}
if (get_encapsulated_response_len != 0U) {
// If response is prepared send notification over Interrupt Endpoint
}
return true;
}
// Callback function called upon reception of request to get encapsulated response sent by the USB Host.
// \param[in] max_len maximum number of data bytes that USB Host expects to receive
// \param[out] buf pointer to buffer containing get encapsulated response to be returned to USB Host.
// \param[out] len pointer to number of data bytes to be returned to USB Host.
// \return true get encapsulated response request processed.
// \return false get encapsulated response request not supported or not processed.
bool USBD_CDCn_ACM_GetEncapsulatedResponse (uint16_t max_len, uint8_t **buf, uint16_t *len) {
REMOTE_NDIS_INDICATE_STATUS_MSG_t *ptr_indicate_status_msg;
uint32_t status;
if (link_state_up || link_state_down) { // Generate unsolicited INDICATE STATUS message if link status has changed
if (link_state_up) {
status = RNDIS_STATUS_MEDIA_CONNECT;
} else {
status = RNDIS_STATUS_MEDIA_DISCONNECT;
}
// Prepare INDICATE STATUS message
ptr_indicate_status_msg = (REMOTE_NDIS_INDICATE_STATUS_MSG_t *)get_encapsulated_response_buf;
ptr_indicate_status_msg->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG;
ptr_indicate_status_msg->MessageLength = 20U;
ptr_indicate_status_msg->Status = status;
ptr_indicate_status_msg->StatusBufferLength = 0U;
ptr_indicate_status_msg->StatusBufferOffset = 0U;
get_encapsulated_response_len = 20U;
link_state_up = false;
link_state_down = false;
}
if (get_encapsulated_response_len != 0U) { // If response is available return it
*buf = (uint8_t *)get_encapsulated_response_buf;
*len = get_encapsulated_response_len;
get_encapsulated_response_len = 0U;
}
return true;
}
// Callback function called when all data was sent
// \return none.
}
// Callback function called when new data was received
// \param[in] len number of bytes available to read.
// \return none.
void USBD_CDCn_ACM_DataReceived (uint32_t len) {
}
// Global functions exported for Virtual Ethernet driver
ARM_ETH_LINK_STATE RNDISn_GetLinkState (void) {
if (rndis_state == RNDIS_DATA_INITIALIZED) {
return ARM_ETH_LINK_UP;
}
return ARM_ETH_LINK_DOWN;
}
int32_t RNDISn_SendFrame (const uint8_t *frame, uint32_t len) {
REMOTE_NDIS_PACKET_MSG_t *ptr_packet_msg;
int32_t usb_cdc_acm_status;
ptr_packet_msg = (REMOTE_NDIS_PACKET_MSG_t *)packet_in;
if ((rndis_state == RNDIS_DATA_INITIALIZED) &&
(len >= ETH_MIN_SIZE) &&
(len <= ETH_MAX_SIZE)) {
memcpy((void *)&ptr_packet_msg->PayLoad[0], (void *)frame, len);
ptr_packet_msg->MessageType = REMOTE_NDIS_PACKET_MSG;
ptr_packet_msg->MessageLength = len + sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U;
ptr_packet_msg->DataOffset = sizeof(REMOTE_NDIS_PACKET_MSG_t) - 12U;
ptr_packet_msg->DataLength = len;
ptr_packet_msg->OutOfBandDataOffset = 0U;
ptr_packet_msg->OutOfBandDataLength = 0U;
ptr_packet_msg->NumOutOfBandDataElements = 0U;
ptr_packet_msg->PerPacketInfoOffset = 0U;
ptr_packet_msg->PerPacketInfoLength = 0U;
ptr_packet_msg->Reserved[0] = 0U;
ptr_packet_msg->Reserved[1] = 0U;
usb_cdc_acm_status = USBD_CDC_ACM_WriteData (n, (const uint8_t *)ptr_packet_msg, ptr_packet_msg->MessageLength);
if (usb_cdc_acm_status == ptr_packet_msg->MessageLength) {
rcv_ok++;
return ARM_DRIVER_OK;
}
if (usb_cdc_acm_status < 0) {
rcv_error++;
return ARM_DRIVER_ERROR;
}
if (usb_cdc_acm_status == 0) {
return ARM_DRIVER_ERROR_BUSY;
}
}
return ARM_DRIVER_ERROR;
}
int32_t RNDISn_ReadFrame (uint8_t *frame, uint32_t len) {
REMOTE_NDIS_PACKET_MSG_t *ptr_packet_msg;
int32_t usb_cdc_acm_status, data_len;
ptr_packet_msg = (REMOTE_NDIS_PACKET_MSG_t *)packet_out;
if ((rndis_state == RNDIS_DATA_INITIALIZED) &&
(len >= ETH_MIN_SIZE) &&
(len <= ETH_MAX_SIZE)) {
usb_cdc_acm_status = USBD_CDC_ACM_ReadData (n, (uint8_t *)ptr_packet_msg, USBD_CDCn_ACM_RECEIVE_BUF_SIZE);
if ((usb_cdc_acm_status != 0) && (usb_cdc_acm_status == ptr_packet_msg->MessageLength)) {
data_len = len;
if (data_len > ptr_packet_msg->DataLength) {
data_len = ptr_packet_msg->DataLength;
}
memcpy((void *)frame, (void *)&ptr_packet_msg->PayLoad[0], data_len);
xmit_ok++;
return data_len;
}
if (usb_cdc_acm_status < 0) {
xmit_error++;
return ARM_DRIVER_ERROR;
}
if (usb_cdc_acm_status == 0) {
return ARM_DRIVER_ERROR_BUSY;
}
}
return 0;
}
uint32_t RNDISn_GetRxFrameSize (void) {
uint32_t avail_data_len;
avail_data_len = USBD_CDC_ACM_DataAvailable (n);
if (avail_data_len > (sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U)) {
avail_data_len -= (sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U);
}
return avail_data_len;
}

User Code Template USBD_User_CDC_ACM_RNDIS_ETH_n.c

The following source code contains all the required callback functions and can be used to implement the application specific behavior of a USB CDC (ACM) Device that can be used as a USB <-> Ethernet bridge using the RNDIS protocol.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device
* Copyright (c) 2018 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_CDC_ACM_RNDIS_ETH_n.c
* Purpose: USB Device Communication Device Class (CDC)
* Abstract Control Model (ACM)
* Remote Network Driver Interface Specification (RNDIS)
* User Module for an USB CDC ACM RNDIS Ethernet Bridge
* Rev.: V1.0.0
*----------------------------------------------------------------------------*/
#include "RTE_Components.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#if defined(RTE_CMSIS_RTOS2)
#include "cmsis_os2.h"
#if defined(RTE_CMSIS_RTOS2_RTX5)
#include "rtx_os.h"
#endif
#endif
#if defined(RTE_CMSIS_RTOS)
#include "cmsis_os.h"
#endif
#include "rl_usb.h"
#include "Driver_ETH.h"
#include "Driver_ETH_MAC.h"
#include "Driver_ETH_PHY.h"
#include "RTE/USB/USBD_Config_CDC_n.h"
//-------- <<< Use Configuration Wizard in Context Menu >>> --------------------
// Configuration defines
// <o>Ethernet MAC Driver Index <0-255>
// <i>Select Ethernet MAC driver index
#define ETH_MAC_NUM 0
// <o>Ethernet PHY Driver Index <0-255>
// <i>Select Ethernet PHY driver index
#define ETH_PHY_NUM 0
// <s.17>MAC Address
// <i>Ethernet MAC Address in text representation
// <i>Value FF-FF-FF-FF-FF-FF is not allowed,
// <i>LSB of first byte must be 0 (an ethernet Multicast bit).
// <i>Default: "1E-30-6C-A2-45-5E"
#define RNDIS_MAC_ADDR "1E-30-6C-A2-45-5E" // RNDIS MAC Address
// <o.0..5>Maximum number of multicast addresses <1-32>
#define RNDIS_MCAST_NUM 16 // RNDIS Number of Multicast Addresses supported
// <s.32>RNDIS Vendor Description
#define RNDIS_VENDOR_DESC "Keil NIC (USB <-> ETH)" // RNDIS Vendor Description
// <o.0..23>RNDIS Vendor Id Code <0x000000-0xFFFFFF>
#define RNDIS_VENDOR_ID 0xFFFFFF // RNDIS three-byte IEEE-registered Vendor Code
//------------- <<< end of configuration section >>> ---------------------------
extern ARM_DRIVER_ETH_MAC ARM_Driver_ETH_MAC_(ETH_MAC_NUM);
extern ARM_DRIVER_ETH_PHY ARM_Driver_ETH_PHY_(ETH_PHY_NUM);
static ARM_DRIVER_ETH_MAC *EthMac = &ARM_Driver_ETH_MAC_(ETH_MAC_NUM);
static ARM_DRIVER_ETH_PHY *EthPhy = &ARM_Driver_ETH_PHY_(ETH_PHY_NUM);
// Local functions
static void MAC_str_to_addr (const char *mac_str, uint8_t *mac_addr);
static void InitVars (void);
static void ResetVars (void);
static void EthMac_Notify (uint32_t event);
// Local variables
static uint32_t rndis_state;
static ARM_ETH_LINK_STATE link_state;
static ARM_ETH_LINK_INFO link_info;
static uint32_t link_speed;
static bool link_state_up;
static bool link_state_down;
static uint32_t packet_filter;
static ARM_ETH_MAC_ADDR mac_address;
static ARM_ETH_MAC_ADDR mcast_address[RNDIS_MCAST_NUM];
static uint16_t get_encapsulated_response_len;
static uint32_t get_encapsulated_response_buf[sizeof(REMOTE_NDIS_INITIALIZE_CMPLT_t)];
static uint32_t xmit_ok;
static uint32_t rcv_ok;
static uint32_t xmit_error;
static uint32_t rcv_error;
static uint32_t rcv_no_buffer;
static uint32_t packet_in [(USBD_CDCn_ACM_SEND_BUF_SIZE +3)/4];
static uint32_t packet_out[(USBD_CDCn_ACM_RECEIVE_BUF_SIZE+3)/4];
// Threads
#ifdef USB_CMSIS_RTOS2
static void ThreadConnection (void *arg);
static void ThreadDataIN (void *arg);
static void ThreadDataOUT (void *arg);
#ifdef USB_CMSIS_RTOS2_RTX5
static osRtxThread_t thread_cb_mem_connection __SECTION(.bss.os.thread.cb);
static uint64_t thread_stack_mem_connection[512/8] __SECTION(.bss.os.thread.stack);
static osRtxThread_t thread_cb_mem_data_in __SECTION(.bss.os.thread.cb);
static uint64_t thread_stack_mem_data_in [512/8] __SECTION(.bss.os.thread.stack);
static osRtxThread_t thread_cb_mem_data_out __SECTION(.bss.os.thread.cb);
static uint64_t thread_stack_mem_data_out [512/8] __SECTION(.bss.os.thread.stack);
#endif
static const osThreadAttr_t thread_attr_connection = {
"ThreadConnection",
0U,
#ifdef USB_CMSIS_RTOS2_RTX5
&thread_cb_mem_connection,
sizeof(osRtxThread_t),
&thread_stack_mem_connection[0],
#else
NULL,
0U,
NULL,
#endif
512U,
osPriorityNormal,
0U,
0U
};
static const osThreadAttr_t thread_attr_data_in = {
"ThreadDataIN",
0U,
#ifdef USB_CMSIS_RTOS2_RTX5
&thread_cb_mem_data_in,
sizeof(osRtxThread_t),
&thread_stack_mem_data_in[0],
#else
NULL,
0U,
NULL,
#endif
512U,
osPriorityNormal,
0U,
0U
};
static const osThreadAttr_t thread_attr_data_out = {
"ThreadDataOUT",
0U,
#ifdef USB_CMSIS_RTOS2_RTX5
&thread_cb_mem_data_out,
sizeof(osRtxThread_t),
&thread_stack_mem_data_out[0],
#else
NULL,
0U,
NULL,
#endif
512U,
osPriorityNormal,
0U,
0U
};
#else
static void ThreadConnection (void const *arg);
static void ThreadDataIN (void const *arg);
static void ThreadDataOUT (void const *arg);
extern const osThreadDef_t os_thread_def_ThreadConnection;
osThreadDef(ThreadConnection, osPriorityNormal, 1, NULL);
extern const osThreadDef_t os_thread_def_ThreadDataIN;
osThreadDef(ThreadDataIN, osPriorityNormal, 1, NULL);
extern const osThreadDef_t os_thread_def_ThreadDataOUT;
osThreadDef(ThreadDataOUT, osPriorityNormal, 1, NULL);
#endif
static void *thread_id_Connection;
static void *thread_id_DataIN;
static void *thread_id_DataOUT;
// MAC Address conversion from string
// \param[in] mac_str Pointer to wide string.
// \param[out] mac_addr Pointer to address.
static void MAC_str_to_addr (const char *mac_str, uint8_t *mac_addr) {
uint8_t c;
uint8_t n;
uint8_t i;
uint8_t str_len;
str_len = strlen(mac_str);
for (i = 0U; i < str_len; i++) {
c = mac_str[i];
if (c == '-') {
continue;
} else if ((c >= '0') && (c <= '9')) {
n = c - '0';
} else if ((c >= 'A') && (c <= 'F')) {
n = c - 'A' + 10U;
} else if ((c >= 'a') && (c <= 'f')) {
n = c - 'a' + 10U;
} else {
n = 0U;
}
if ((i & 1U) != 0U) {
mac_addr[i>>1] |= n;
} else {
mac_addr[i>>1] = n << 4;
}
}
}
// Initialize variables
void InitVars (void) {
rndis_state = RNDIS_UNINITIALIZED;
link_state = ARM_ETH_LINK_DOWN;
link_info.speed = 0U;
link_info.duplex = 0U;
link_speed = 0U;
packet_filter = 0U;
memset((void *)mcast_address, 0, sizeof(mcast_address));
ResetVars();
}
// Reset variables
static void ResetVars (void) {
link_state_up = false;
link_state_down = false;
get_encapsulated_response_len = 0U;
xmit_ok = 0U;
rcv_ok = 0U;
xmit_error = 0U;
rcv_error = 0U;
rcv_no_buffer = 0U;
}
// Initialize Ethernet (MAC and PHY) Interface
// \return true initialization succeeded.
// \return false initialization failed.
static bool ENET_Initialize (void) {
ARM_ETH_MAC_CAPABILITIES eth_capabilities;
packet_filter = ARM_ETH_MAC_ADDRESS_BROADCAST;
// Initialize Media Access Controller
eth_capabilities = EthMac->GetCapabilities();
if (EthMac->Initialize(EthMac_Notify) != ARM_DRIVER_OK) { return false; }
if (EthMac->PowerControl(ARM_POWER_FULL) != ARM_DRIVER_OK) { return false; }
if (eth_capabilities.mac_address != 0U) {
// Hardware provided MAC address
if (EthMac->GetMacAddress(&mac_address) != ARM_DRIVER_OK) { return false; }
} else {
// MAC address defined as ETH_MAC_ADDR string
MAC_str_to_addr(RNDIS_MAC_ADDR, (uint8_t *)&mac_address);
if (EthMac->SetMacAddress(&mac_address) != ARM_DRIVER_OK) { return false; }
}
// Initialize Physical Media Interface
if (EthPhy->Initialize(EthMac->PHY_Read, EthMac->PHY_Write) != ARM_DRIVER_OK) { return false; }
if (EthPhy->PowerControl(ARM_POWER_FULL) != ARM_DRIVER_OK) { return false; }
if (EthPhy->SetInterface(eth_capabilities.media_interface) != ARM_DRIVER_OK) { return false; }
if (EthPhy->SetMode(ARM_ETH_PHY_AUTO_NEGOTIATE) != ARM_DRIVER_OK) { return false; }
// Create Threads
#ifdef USB_CMSIS_RTOS2
thread_id_Connection = osThreadNew(ThreadConnection, NULL, &thread_attr_connection);
thread_id_DataIN = osThreadNew(ThreadDataIN, NULL, &thread_attr_data_in);
thread_id_DataOUT = osThreadNew(ThreadDataOUT, NULL, &thread_attr_data_out);
#else
thread_id_Connection = osThreadCreate(osThread(ThreadConnection), NULL);
thread_id_DataIN = osThreadCreate(osThread(ThreadDataIN), NULL);
thread_id_DataOUT = osThreadCreate(osThread(ThreadDataOUT), NULL);
#endif
// Set initial signal to Connection thread to check for current connection
#ifdef USB_CMSIS_RTOS2
osThreadFlagsSet(thread_id_Connection, 1U);
#else
osSignalSet(thread_id_Connection, 1U);
#endif
return true;
}
// Uninitialize Ethernet (MAC and PHY) Interface
static void ENET_Uninitialize (void) {
packet_filter = ARM_ETH_MAC_ADDRESS_BROADCAST;
EthMac->SetMacAddress(&mac_address);
EthMac->SetAddressFilter(NULL, 0);
EthMac->Control(ARM_ETH_MAC_CONFIGURE,
(uint32_t)(link_info.speed << ARM_ETH_MAC_SPEED_Pos ) |
(uint32_t)(link_info.duplex << ARM_ETH_MAC_DUPLEX_Pos) |
packet_filter);
// Terminate threads
if (thread_id_Connection != NULL) { osThreadTerminate(thread_id_Connection); thread_id_Connection = NULL; }
if (thread_id_DataIN != NULL) { osThreadTerminate(thread_id_DataIN); thread_id_DataIN = NULL; }
if (thread_id_DataOUT != NULL) { osThreadTerminate(thread_id_DataOUT); thread_id_DataOUT = NULL; }
EthPhy->PowerControl(ARM_POWER_OFF);
EthPhy->Uninitialize();
EthMac->PowerControl(ARM_POWER_OFF);
EthMac->Uninitialize();
}
// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
InitVars();
}
// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
InitVars();
ENET_Uninitialize();
}
// Called upon USB Bus Reset Event.
void USBD_CDCn_ACM_Reset (void) {
InitVars();
}
// Callback function called upon reception of request send encapsulated command sent by the USB Host.
// \param[in] buf buffer that contains send encapsulated command request.
// \param[in] len length of send encapsulated command request.
// \return true send encapsulated command request processed.
// \return false send encapsulated command request not supported or not processed.
bool USBD_CDCn_ACM_SendEncapsulatedCommand (const uint8_t *buf, uint16_t len) {
REMOTE_NDIS_INITIALIZE_MSG_t *ptr_init_msg;
REMOTE_NDIS_INITIALIZE_CMPLT_t *ptr_init_cmplt;
REMOTE_NDIS_HALT_MSG_t *ptr_halt_msg;
REMOTE_NDIS_QUERY_MSG_t *ptr_query_msg;
REMOTE_NDIS_QUERY_CMPLT_t *ptr_query_cmplt;
REMOTE_NDIS_SET_MSG_t *ptr_set_msg;
REMOTE_NDIS_SET_CMPLT_t *ptr_set_cmplt;
REMOTE_NDIS_RESET_MSG_t *ptr_reset_msg;
REMOTE_NDIS_RESET_CMPLT_t *ptr_reset_cmplt;
REMOTE_NDIS_KEEPALIVE_MSG_t *ptr_keepalive_msg;
REMOTE_NDIS_KEEPALIVE_CMPLT_t *ptr_keepalive_cmplt;
uint32_t status, val;
int32_t i;
uint32_t num, by;
uint16_t msg_type;
msg_type = __UNALIGNED_UINT16_READ(buf); // Extract message type of received message
// In uninitialized state only allowed messages are INITALIZE and HALT
if ((rndis_state == RNDIS_UNINITIALIZED) &&
(msg_type != REMOTE_NDIS_INITIALIZE_MSG) &&
(msg_type != REMOTE_NDIS_HALT_MSG)) {
return false;
}
status = RNDIS_STATUS_SUCCESS; // Default message processing status
get_encapsulated_response_len = 0U; // Prepare default no response size
switch (msg_type) { // MessageType
case REMOTE_NDIS_INITIALIZE_MSG:
// Check message is valid
ptr_init_msg = (REMOTE_NDIS_INITIALIZE_MSG_t *)buf;
if (ptr_init_msg->MessageLength != sizeof(REMOTE_NDIS_INITIALIZE_MSG_t)) { return false; }
if (ptr_init_msg->MajorVersion != RNDIS_MAJOR_VERSION) { return false; }
if (ptr_init_msg->MinorVersion != RNDIS_MINOR_VERSION) { return false; }
if (ptr_init_msg->MaxTransferSize != 16384U) { return false; }
ENET_Initialize ();
rndis_state = RNDIS_INITIALIZED;
// Prepare response
ptr_init_cmplt = (REMOTE_NDIS_INITIALIZE_CMPLT_t *)get_encapsulated_response_buf;
ptr_init_cmplt->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
ptr_init_cmplt->MessageLength = sizeof(REMOTE_NDIS_INITIALIZE_CMPLT_t);
ptr_init_cmplt->RequestID = ptr_init_msg->RequestID;
ptr_init_cmplt->Status = status;
ptr_init_cmplt->MajorVersion = RNDIS_MAJOR_VERSION;
ptr_init_cmplt->MinorVersion = RNDIS_MINOR_VERSION;
ptr_init_cmplt->DeviceFlags = RNDIS_DF_CONNECTIONLESS;
ptr_init_cmplt->Medium = NdisMedium802_3;
ptr_init_cmplt->MaxPacketsPerTransfer = 1U;
ptr_init_cmplt->MaxTransferSize = USBD_CDCn_ACM_RECEIVE_BUF_SIZE;
ptr_init_cmplt->PacketAlignmentFactor = 2U;
ptr_init_cmplt->Reserved[0] = 0U;
ptr_init_cmplt->Reserved[1] = 0U;
get_encapsulated_response_len = ptr_init_cmplt->MessageLength;
break;
case REMOTE_NDIS_HALT_MSG:
// Check message is valid
ptr_halt_msg = (REMOTE_NDIS_HALT_MSG_t *)buf;
if (ptr_halt_msg->MessageLength != sizeof(REMOTE_NDIS_HALT_MSG_t)) { return false; }
ENET_Uninitialize ();
rndis_state = RNDIS_UNINITIALIZED;
// This message does not have a response
return true;
case REMOTE_NDIS_QUERY_MSG:
// Check message is valid
ptr_query_msg = (REMOTE_NDIS_QUERY_MSG_t *)buf;
if (ptr_query_msg->MessageLength < 28U) { return false; }
// Prepare response
ptr_query_cmplt = (REMOTE_NDIS_QUERY_CMPLT_t *)get_encapsulated_response_buf;
ptr_query_cmplt->MessageType = REMOTE_NDIS_QUERY_CMPLT;
ptr_query_cmplt->RequestID = ptr_query_msg->RequestID;
ptr_query_cmplt->InformationBufferOffset = 16U;
switch (ptr_query_msg->Oid) { // Handle OID
case OID_GEN_SUPPORTED_LIST:
ptr_query_cmplt->InformationBufferLength = 23U * 4U;
ptr_query_cmplt->OIDInputBuffer[0] = OID_GEN_SUPPORTED_LIST;
ptr_query_cmplt->OIDInputBuffer[1] = OID_GEN_HARDWARE_STATUS;
ptr_query_cmplt->OIDInputBuffer[2] = OID_GEN_MEDIA_SUPPORTED;
ptr_query_cmplt->OIDInputBuffer[3] = OID_GEN_MEDIA_IN_USE;
ptr_query_cmplt->OIDInputBuffer[4] = OID_GEN_MAXIMUM_FRAME_SIZE;
ptr_query_cmplt->OIDInputBuffer[5] = OID_GEN_LINK_SPEED;
ptr_query_cmplt->OIDInputBuffer[6] = OID_GEN_TRANSMIT_BLOCK_SIZE;
ptr_query_cmplt->OIDInputBuffer[7] = OID_GEN_RECEIVE_BLOCK_SIZE;
ptr_query_cmplt->OIDInputBuffer[8] = OID_GEN_VENDOR_ID;
ptr_query_cmplt->OIDInputBuffer[9] = OID_GEN_VENDOR_DESCRIPTION;
ptr_query_cmplt->OIDInputBuffer[10] = OID_GEN_CURRENT_PACKET_FILTER;
ptr_query_cmplt->OIDInputBuffer[11] = OID_GEN_MAXIMUM_TOTAL_SIZE;
ptr_query_cmplt->OIDInputBuffer[12] = OID_GEN_MEDIA_CONNECT_STATUS;
ptr_query_cmplt->OIDInputBuffer[13] = OID_GEN_PHYSICAL_MEDIUM;
ptr_query_cmplt->OIDInputBuffer[14] = OID_GEN_XMIT_OK;
ptr_query_cmplt->OIDInputBuffer[15] = OID_GEN_RCV_OK;
ptr_query_cmplt->OIDInputBuffer[16] = OID_GEN_XMIT_ERROR;
ptr_query_cmplt->OIDInputBuffer[17] = OID_GEN_RCV_ERROR;
ptr_query_cmplt->OIDInputBuffer[18] = OID_GEN_RCV_NO_BUFFER;
ptr_query_cmplt->OIDInputBuffer[19] = OID_802_3_PERMANENT_ADDRESS;
ptr_query_cmplt->OIDInputBuffer[20] = OID_802_3_CURRENT_ADDRESS;
ptr_query_cmplt->OIDInputBuffer[21] = OID_802_3_MULTICAST_LIST;
ptr_query_cmplt->OIDInputBuffer[22] = OID_802_3_MAXIMUM_LIST_SIZE;
break;
case OID_GEN_HARDWARE_STATUS:
ptr_query_cmplt->InformationBufferLength = 4U;
if (link_state == ARM_ETH_LINK_UP) {
ptr_query_cmplt->OIDInputBuffer[0] = NdisHardwareStatusReady;
} else {
ptr_query_cmplt->OIDInputBuffer[0] = NdisHardwareStatusNotReady;
}
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = NdisMedium802_3;
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = ETH_MTU_SIZE;
break;
case OID_GEN_LINK_SPEED:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = link_speed / 100U;
break;
case OID_GEN_TRANSMIT_BLOCK_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = USBD_CDCn_ACM_SEND_BUF_SIZE;
break;
case OID_GEN_RECEIVE_BLOCK_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = USBD_CDCn_ACM_RECEIVE_BUF_SIZE;
break;
case OID_GEN_VENDOR_ID:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = RNDIS_VENDOR_ID;
break;
case OID_GEN_VENDOR_DESCRIPTION:
ptr_query_cmplt->InformationBufferLength = strlen(RNDIS_VENDOR_DESC) + 1;
memset((void *)&ptr_query_cmplt->OIDInputBuffer[0], 0, ptr_query_cmplt->InformationBufferLength + 1U);
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], RNDIS_VENDOR_DESC, strlen(RNDIS_VENDOR_DESC));
break;
case OID_GEN_CURRENT_PACKET_FILTER:
ptr_query_cmplt->InformationBufferLength = 4U;
val = 0U;
if ((packet_filter & ARM_ETH_MAC_ADDRESS_MULTICAST) != 0U) { val |= RNDIS_FILTER_ALL_MULTICAST; }
if ((packet_filter & ARM_ETH_MAC_ADDRESS_BROADCAST) != 0U) { val |= RNDIS_FILTER_BROADCAST; }
if ((packet_filter & ARM_ETH_MAC_ADDRESS_ALL) != 0U) { val |= RNDIS_FILTER_PROMISCUOUS; }
ptr_query_cmplt->OIDInputBuffer[0] = val;
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U + ETH_MAX_SIZE;
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
ptr_query_cmplt->InformationBufferLength = 4U;
if (link_state == ARM_ETH_LINK_UP) {
ptr_query_cmplt->OIDInputBuffer[0] = NdisMediaStateConnected;
} else {
ptr_query_cmplt->OIDInputBuffer[0] = NdisMediaStateDisconnected;
}
break;
case OID_GEN_PHYSICAL_MEDIUM:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = NdisPhysicalMediumUnspecified;
break;
case OID_GEN_XMIT_OK:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = xmit_ok;
break;
case OID_GEN_RCV_OK:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_ok;
break;
case OID_GEN_XMIT_ERROR:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = xmit_error;
break;
case OID_GEN_RCV_ERROR:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_error;
break;
case OID_GEN_RCV_NO_BUFFER:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = rcv_no_buffer;
break;
case OID_802_3_PERMANENT_ADDRESS:
case OID_802_3_CURRENT_ADDRESS:
ptr_query_cmplt->InformationBufferLength = 6U;
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], &mac_address, sizeof(ARM_ETH_MAC_ADDR));
break;
case OID_802_3_MULTICAST_LIST:
for (i = 0U; i < RNDIS_MCAST_NUM; i++) {
if (memcmp(&mcast_address[i], "\0\0\0\0\0\0", 6) == 0) {
break;
}
}
if (i == 0U) {
num = 0U;
ptr_query_cmplt->InformationBufferOffset = 0U;
} else {
num = i + 1U;
memcpy((void *)&ptr_query_cmplt->OIDInputBuffer[0], mcast_address, num * sizeof(ARM_ETH_MAC_ADDR));
}
ptr_query_cmplt->InformationBufferLength = num * sizeof(ARM_ETH_MAC_ADDR);
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
ptr_query_cmplt->InformationBufferLength = 4U;
ptr_query_cmplt->OIDInputBuffer[0] = RNDIS_MCAST_NUM;
break;
default:
ptr_query_cmplt->InformationBufferOffset = 0U;
ptr_query_cmplt->InformationBufferLength = 0U;
status = RNDIS_STATUS_NOT_SUPPORTED;
break;
}
ptr_query_cmplt->Status = status;
ptr_query_cmplt->MessageLength = ptr_query_cmplt->InformationBufferLength + 24U;
get_encapsulated_response_len = ptr_query_cmplt->MessageLength;
break;
case REMOTE_NDIS_SET_MSG:
// Check message is valid
ptr_set_msg = (REMOTE_NDIS_SET_MSG_t *)buf;
if (ptr_set_msg->MessageLength < 28U) { return false; }
// Prepare response
ptr_set_cmplt = (REMOTE_NDIS_SET_CMPLT_t *)get_encapsulated_response_buf;
ptr_set_cmplt->MessageType = REMOTE_NDIS_SET_CMPLT;
ptr_set_cmplt->MessageLength = sizeof(REMOTE_NDIS_SET_CMPLT_t);
ptr_set_cmplt->RequestID = ptr_set_msg->RequestID;
switch (ptr_set_msg->Oid) { // Handle OID
case OID_802_3_MULTICAST_LIST:
by = ptr_set_msg->InformationBufferLength;
if (by > (sizeof(ARM_ETH_MAC_ADDR) * RNDIS_MCAST_NUM)) {
by = sizeof(ARM_ETH_MAC_ADDR) * RNDIS_MCAST_NUM;
}
if (by > 0U) {
memcpy(mcast_address, (void *)&ptr_set_msg->OIDInputBuffer[0], by);
num = by / sizeof(ARM_ETH_MAC_ADDR);
if (EthMac->SetAddressFilter(mcast_address, num) != ARM_DRIVER_OK) {
status = RNDIS_STATUS_FAILURE;
}
}
break;
case OID_GEN_CURRENT_PACKET_FILTER:
if ((ptr_set_msg->InformationBufferLength == 4U) &&
(ptr_set_msg->InformationBufferOffset != 0U)) {
val = *(uint32_t *)(((uint8_t *)&ptr_set_msg->RequestID) + ptr_set_msg->InformationBufferOffset);
if (val != 0U) {
if ((val & RNDIS_FILTER_ALL_MULTICAST) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_MULTICAST; }
if ((val & RNDIS_FILTER_BROADCAST) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_BROADCAST; }
if ((val & RNDIS_FILTER_PROMISCUOUS) != 0U) { packet_filter |= ARM_ETH_MAC_ADDRESS_ALL; }
EthMac->Control(ARM_ETH_MAC_CONFIGURE,
(uint32_t)(link_info.speed << ARM_ETH_MAC_SPEED_Pos ) |
(uint32_t)(link_info.duplex << ARM_ETH_MAC_DUPLEX_Pos) |
packet_filter);
rndis_state = RNDIS_DATA_INITIALIZED;
} else {
if (rndis_state == RNDIS_DATA_INITIALIZED) {
rndis_state = RNDIS_INITIALIZED;
}
}
} else {
status = RNDIS_STATUS_FAILURE;
}
break;
default:
status = RNDIS_STATUS_NOT_SUPPORTED;
break;
}
ptr_set_cmplt->Status = status;
get_encapsulated_response_len = ptr_set_cmplt->MessageLength;
break;
case REMOTE_NDIS_RESET_MSG:
// Check message is valid
ptr_reset_msg = (REMOTE_NDIS_RESET_MSG_t *)buf;
if (ptr_reset_msg->MessageLength != sizeof(REMOTE_NDIS_RESET_MSG_t)) { return false; }
ResetVars();
// Set flags to message processing threads
#ifdef USB_CMSIS_RTOS2
osThreadFlagsSet(thread_id_DataIN, 1U);
osThreadFlagsSet(thread_id_DataOUT, 1U);
#else
osSignalSet(thread_id_DataIN, 1U);
osSignalSet(thread_id_DataOUT, 1U);
#endif
// Prepare response
ptr_reset_cmplt = (REMOTE_NDIS_RESET_CMPLT_t *)get_encapsulated_response_buf;
ptr_reset_cmplt->MessageType = REMOTE_NDIS_RESET_CMPLT;
ptr_reset_cmplt->MessageLength = sizeof(REMOTE_NDIS_RESET_CMPLT_t);
ptr_reset_cmplt->Status = status;
ptr_reset_cmplt->AddressingReset = 0U;
get_encapsulated_response_len = ptr_reset_cmplt->MessageLength;
break;
case REMOTE_NDIS_KEEPALIVE_MSG:
// Check message is valid
ptr_keepalive_msg = (REMOTE_NDIS_KEEPALIVE_MSG_t *)buf;
if (ptr_keepalive_msg->MessageLength != sizeof(REMOTE_NDIS_KEEPALIVE_MSG_t)) { return false; }
// Prepare response
ptr_keepalive_cmplt = (REMOTE_NDIS_KEEPALIVE_CMPLT_t *)get_encapsulated_response_buf;
ptr_keepalive_cmplt->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
ptr_keepalive_cmplt->MessageLength = sizeof(REMOTE_NDIS_KEEPALIVE_CMPLT_t);
ptr_keepalive_cmplt->RequestID = ptr_keepalive_msg->RequestID;
ptr_keepalive_cmplt->Status = status;
get_encapsulated_response_len = ptr_keepalive_cmplt->MessageLength;
break;
default:
return false;
}
if (get_encapsulated_response_len != 0U) {
// If response is prepared send notification over Interrupt Endpoint
}
return true;
}
// Callback function called upon reception of request to get encapsulated response sent by the USB Host.
// \param[in] max_len maximum number of data bytes that USB Host expects to receive
// \param[out] buf pointer to buffer containing get encapsulated response to be returned to USB Host.
// \param[out] len pointer to number of data bytes to be returned to USB Host.
// \return true get encapsulated response request processed.
// \return false get encapsulated response request not supported or not processed.
bool USBD_CDCn_ACM_GetEncapsulatedResponse (uint16_t max_len, uint8_t **buf, uint16_t *len) {
REMOTE_NDIS_INDICATE_STATUS_MSG_t *ptr_indicate_status_msg;
uint32_t status;
if (link_state_up || link_state_down) { // Generate unsolicited INDICATE STATUS message if link status has changed
if (link_state_up) {
status = RNDIS_STATUS_MEDIA_CONNECT;
} else {
status = RNDIS_STATUS_MEDIA_DISCONNECT;
}
// Prepare INDICATE STATUS message
ptr_indicate_status_msg = (REMOTE_NDIS_INDICATE_STATUS_MSG_t *)get_encapsulated_response_buf;
ptr_indicate_status_msg->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG;
ptr_indicate_status_msg->MessageLength = 20U;
ptr_indicate_status_msg->Status = status;
ptr_indicate_status_msg->StatusBufferLength = 0U;
ptr_indicate_status_msg->StatusBufferOffset = 0U;
get_encapsulated_response_len = 20U;
link_state_up = false;
link_state_down = false;
}
if (get_encapsulated_response_len != 0U) { // If response is available return it
*buf = (uint8_t *)get_encapsulated_response_buf;
*len = get_encapsulated_response_len;
get_encapsulated_response_len = 0U;
}
return true;
}
// Callback function called when all data was sent
// \return none.
}
// Callback function called when new data was received
// \param[in] len number of bytes available to read.
// \return none.
void USBD_CDCn_ACM_DataReceived (uint32_t len) {
#ifdef USB_CMSIS_RTOS2
osThreadFlagsSet(thread_id_DataOUT, 1U);
#else
osSignalSet(thread_id_DataOUT, 1U);
#endif
}
// Called upon Ethernet MAC Event
static void EthMac_Notify (uint32_t event) {
switch (event) {
case ARM_ETH_MAC_EVENT_RX_FRAME:
#ifdef USB_CMSIS_RTOS2
osThreadFlagsSet(thread_id_DataIN, 1U);
#else
osSignalSet(thread_id_DataIN, 1U);
#endif
break;
default:
break;
}
}
// Thread handling ETH Connection
#ifdef USB_CMSIS_RTOS2
__NO_RETURN static void ThreadConnection (void *arg) {
#else
__NO_RETURN static void ThreadConnection (void const *arg) {
#endif
ARM_ETH_LINK_STATE link_state_local;
uint32_t speed_local;
(void)(arg);
for (;;) {
#ifdef USB_CMSIS_RTOS2
osThreadFlagsWait(1U, osFlagsWaitAll, 500U);
#else
osSignalWait(1U, 500U);
#endif
link_state_local = EthPhy->GetLinkState();
if (link_state_local == ARM_ETH_LINK_UP) {
link_info = EthPhy->GetLinkInfo();
switch (link_info.speed) {
case 0: speed_local = 10000000U; break;
case 1: speed_local = 100000000U; break;
case 2: speed_local = 1000000000U; break;
default: speed_local = 0U;
}
} else {
speed_local = 0U;
}
if (link_speed != speed_local) {
link_speed = speed_local;
}
if (link_state != link_state_local) {
link_state = link_state_local;
if (link_state == ARM_ETH_LINK_UP) {
EthMac->Control(ARM_ETH_MAC_CONFIGURE,
(uint32_t)(link_info.speed << ARM_ETH_MAC_SPEED_Pos ) |
(uint32_t)(link_info.duplex << ARM_ETH_MAC_DUPLEX_Pos) |
packet_filter);
EthMac->Control(ARM_ETH_MAC_CONTROL_TX, 1U);
EthMac->Control(ARM_ETH_MAC_CONTROL_RX, 1U);
link_state_down = false;
link_state_up = true;
} else {
EthMac->Control(ARM_ETH_MAC_FLUSH,
ARM_ETH_MAC_FLUSH_TX |
ARM_ETH_MAC_FLUSH_RX);
EthMac->Control(ARM_ETH_MAC_CONTROL_TX, 0U);
EthMac->Control(ARM_ETH_MAC_CONTROL_RX, 0U);
link_state_up = false;
link_state_down = true;
}
}
}
}
// Thread handling Data IN (ETH -> USB)
#ifdef USB_CMSIS_RTOS2
__NO_RETURN static void ThreadDataIN (void *arg) {
#else
__NO_RETURN static void ThreadDataIN (void const *arg) {
#endif
REMOTE_NDIS_PACKET_MSG_t *ptr_packet_msg;
uint32_t eth_rx_size;
int32_t usb_cdc_acm_status;
(void)(arg);
ptr_packet_msg = (REMOTE_NDIS_PACKET_MSG_t *)packet_in;
for (;;) {
#ifdef USB_CMSIS_RTOS2
osThreadFlagsWait(1U, osFlagsWaitAll, osWaitForever);
#else
osSignalWait(1U, osWaitForever);
#endif
for (;;) {
eth_rx_size = EthMac->GetRxFrameSize();
if (eth_rx_size == 0) { // No new data available on the Ethernet
break; // exit the loop, and wait for new message
}
if ((link_state == ARM_ETH_LINK_UP) &&
(rndis_state == RNDIS_DATA_INITIALIZED) &&
(eth_rx_size >= ETH_MIN_SIZE) &&
(eth_rx_size <= ETH_MAX_SIZE)) {
EthMac->ReadFrame((uint8_t *)&ptr_packet_msg->PayLoad[0], eth_rx_size);
ptr_packet_msg->MessageType = REMOTE_NDIS_PACKET_MSG;
ptr_packet_msg->MessageLength = eth_rx_size + sizeof(REMOTE_NDIS_PACKET_MSG_t) - 4U;
ptr_packet_msg->DataOffset = sizeof(REMOTE_NDIS_PACKET_MSG_t) - 12U;
ptr_packet_msg->DataLength = eth_rx_size;
ptr_packet_msg->OutOfBandDataOffset = 0U;
ptr_packet_msg->OutOfBandDataLength = 0U;
ptr_packet_msg->NumOutOfBandDataElements = 0U;
ptr_packet_msg->PerPacketInfoOffset = 0U;
ptr_packet_msg->PerPacketInfoLength = 0U;
ptr_packet_msg->Reserved[0] = 0U;
ptr_packet_msg->Reserved[1] = 0U;
do {
usb_cdc_acm_status = USBD_CDC_ACM_WriteData (n, (const uint8_t *)ptr_packet_msg, ptr_packet_msg->MessageLength);
} while (usb_cdc_acm_status == 0);
if (usb_cdc_acm_status == ptr_packet_msg->MessageLength) {
rcv_ok++;
} else {
rcv_error++;
}
} else {
EthMac->ReadFrame(NULL, 0); // Dump packet
}
}
}
}
// Thread handling Data OUT (USB -> ETH)
#ifdef USB_CMSIS_RTOS2
__NO_RETURN static void ThreadDataOUT (void *arg) {
#else
__NO_RETURN static void ThreadDataOUT (void const *arg) {
#endif
REMOTE_NDIS_PACKET_MSG_t *ptr_packet_msg;
uint32_t eth_tx_size;
int32_t usb_cdc_acm_status, eth_mac_status;
(void)(arg);
ptr_packet_msg = (REMOTE_NDIS_PACKET_MSG_t *)packet_out;
for (;;) {
#ifdef USB_CMSIS_RTOS2
osThreadFlagsWait(1U, osFlagsWaitAll, osWaitForever);
#else
osSignalWait(1U, osWaitForever);
#endif
for (;;) {
usb_cdc_acm_status = USBD_CDC_ACM_ReadData (n, (uint8_t *)ptr_packet_msg, USBD_CDC0_ACM_RECEIVE_BUF_SIZE);
if (usb_cdc_acm_status == 0) { // No new data available on the USB
break; // exit the loop, and wait for new message
} else if (usb_cdc_acm_status > ptr_packet_msg->DataLength) {
eth_tx_size = ptr_packet_msg->DataLength;
}
if ((link_state == ARM_ETH_LINK_UP) &&
(rndis_state == RNDIS_DATA_INITIALIZED) &&
(eth_tx_size >= ETH_MIN_SIZE) &&
(eth_tx_size <= ETH_MAX_SIZE)) {
do {
eth_mac_status = EthMac->SendFrame((uint8_t *)&ptr_packet_msg->PayLoad[0], (uint32_t)(eth_tx_size), 0U);
} while (eth_mac_status == ARM_DRIVER_ERROR_BUSY);
if (eth_mac_status == ARM_DRIVER_OK) {
xmit_ok++;
} else {
xmit_error++;
}
}
}
}
}