247 lines
7.6 KiB
C
247 lines
7.6 KiB
C
/*
|
|
* Copyright (c) 2023, sakumisu
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include "usbd_core.h"
|
|
#include "usbd_cdc_ecm.h"
|
|
|
|
#define CDC_ECM_OUT_EP_IDX 0
|
|
#define CDC_ECM_IN_EP_IDX 1
|
|
#define CDC_ECM_INT_EP_IDX 2
|
|
|
|
/* Describe EndPoints configuration */
|
|
static struct usbd_endpoint cdc_ecm_ep_data[3];
|
|
|
|
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
|
|
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
|
|
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
|
|
|
|
volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
|
|
volatile uint32_t g_cdc_ecm_rx_data_length = 0;
|
|
volatile uint32_t g_cdc_ecm_tx_data_length = 0;
|
|
|
|
static volatile uint8_t g_current_net_status = 0;
|
|
static volatile uint8_t g_cmd_intf = 0;
|
|
|
|
static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
|
|
CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
|
|
|
|
void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
|
|
{
|
|
struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ecm_notify_buf;
|
|
uint8_t bytes2send = 0;
|
|
|
|
notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
|
|
notify->bNotificationType = notifycode;
|
|
|
|
switch (notifycode) {
|
|
case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
|
|
notify->wValue = value;
|
|
notify->wIndex = g_cmd_intf;
|
|
notify->wLength = 0U;
|
|
|
|
for (uint8_t i = 0U; i < 8U; i++) {
|
|
notify->data[i] = 0U;
|
|
}
|
|
bytes2send = 8U;
|
|
break;
|
|
case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
|
|
notify->wValue = 0U;
|
|
notify->wIndex = g_cmd_intf;
|
|
notify->wLength = 0U;
|
|
for (uint8_t i = 0U; i < 8U; i++) {
|
|
notify->data[i] = 0U;
|
|
}
|
|
bytes2send = 8U;
|
|
break;
|
|
case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
|
|
notify->wValue = 0U;
|
|
notify->wIndex = g_cmd_intf;
|
|
notify->wLength = 0x0008U;
|
|
bytes2send = 16U;
|
|
|
|
memcpy(notify->data, speed, 8);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (bytes2send) {
|
|
usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
|
|
}
|
|
}
|
|
|
|
static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
|
|
{
|
|
USB_LOG_DBG("CDC ECM Class request: "
|
|
"bRequest 0x%02x\r\n",
|
|
setup->bRequest);
|
|
|
|
g_cmd_intf = LO_BYTE(setup->wIndex);
|
|
|
|
switch (setup->bRequest) {
|
|
case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
|
|
/* bit0 Promiscuous
|
|
* bit1 ALL Multicast
|
|
* bit2 Directed
|
|
* bit3 Broadcast
|
|
* bit4 Multicast
|
|
*/
|
|
if (g_current_net_status == 0) {
|
|
g_current_net_status = 1;
|
|
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
|
|
{
|
|
switch (event) {
|
|
case USBD_EVENT_RESET:
|
|
g_current_net_status = 0;
|
|
g_cdc_ecm_rx_data_length = 0;
|
|
g_cdc_ecm_tx_data_length = 0;
|
|
g_cdc_ecm_rx_data_buffer = NULL;
|
|
break;
|
|
case USBD_EVENT_CONFIGURED:
|
|
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
|
{
|
|
g_cdc_ecm_rx_data_length += nbytes;
|
|
|
|
if (nbytes < usbd_get_ep_mps(busid, ep)) {
|
|
g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
|
|
usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
|
|
} else {
|
|
usbd_ep_start_read(0, ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, ep));
|
|
}
|
|
}
|
|
|
|
void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
|
{
|
|
if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
|
|
/* send zlp */
|
|
usbd_ep_start_write(0, ep, NULL, 0);
|
|
} else {
|
|
g_cdc_ecm_tx_data_length = 0;
|
|
}
|
|
}
|
|
|
|
void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
|
|
{
|
|
if (g_current_net_status == 1) {
|
|
g_current_net_status = 2;
|
|
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
|
|
}
|
|
}
|
|
|
|
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
|
|
{
|
|
if (g_cdc_ecm_tx_data_length > 0) {
|
|
return -USB_ERR_BUSY;
|
|
}
|
|
|
|
g_cdc_ecm_tx_data_length = len;
|
|
|
|
USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
|
|
return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, g_cdc_ecm_tx_data_length);
|
|
}
|
|
|
|
void usbd_cdc_ecm_start_read_next(void)
|
|
{
|
|
g_cdc_ecm_rx_data_length = 0;
|
|
g_cdc_ecm_rx_data_buffer = NULL;
|
|
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
|
|
}
|
|
|
|
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
|
|
struct pbuf *usbd_cdc_ecm_eth_rx(void)
|
|
{
|
|
struct pbuf *p;
|
|
|
|
if (g_cdc_ecm_rx_data_buffer == NULL) {
|
|
return NULL;
|
|
}
|
|
p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
|
|
if (p == NULL) {
|
|
usbd_cdc_ecm_start_read_next();
|
|
return NULL;
|
|
}
|
|
memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
|
|
p->len = g_cdc_ecm_rx_data_length;
|
|
|
|
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
|
|
usbd_cdc_ecm_start_read_next();
|
|
return p;
|
|
}
|
|
|
|
int usbd_cdc_ecm_eth_tx(struct pbuf *p)
|
|
{
|
|
struct pbuf *q;
|
|
uint8_t *buffer;
|
|
|
|
if (g_cdc_ecm_tx_data_length > 0) {
|
|
return -USB_ERR_BUSY;
|
|
}
|
|
|
|
if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
|
|
p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
|
|
}
|
|
|
|
buffer = g_cdc_ecm_tx_buffer;
|
|
for (q = p; q != NULL; q = q->next) {
|
|
memcpy(buffer, q->payload, q->len);
|
|
buffer += q->len;
|
|
}
|
|
|
|
g_cdc_ecm_tx_data_length = p->tot_len;
|
|
|
|
return usbd_cdc_ecm_start_write(g_cdc_ecm_tx_buffer, g_cdc_ecm_tx_data_length);
|
|
}
|
|
#endif
|
|
|
|
struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep)
|
|
{
|
|
intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
|
|
intf->class_endpoint_handler = NULL;
|
|
intf->vendor_handler = NULL;
|
|
intf->notify_handler = cdc_ecm_notify_handler;
|
|
|
|
cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
|
|
cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
|
|
cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
|
|
cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
|
|
cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
|
|
cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
|
|
|
|
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
|
|
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
|
|
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
|
|
|
|
return intf;
|
|
}
|
|
|
|
void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
|
|
{
|
|
memcpy(g_connect_speed_table, speed, 8);
|
|
}
|
|
|
|
__WEAK void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len)
|
|
{
|
|
} |