Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dts/bindings/usb/zephyr,usbh-cdc-ecm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2025 Linumiz GmbH
# SPDX-License-Identifier: Apache-2.0

description: |
USB Host CDC Ethernet Control Model (ECM) implementation.
Supports communication with CDC ECM-compliant USB Ethernet devices.

compatible: "zephyr,usbh-cdc-ecm"

include: base.yaml

properties:
local-mac-address:
type: uint8-array
description: "Local MAC address for the CDC ECM device"
127 changes: 99 additions & 28 deletions include/zephyr/usb/usbh.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
* Copyright (c) 2025 Nordic Semiconductor ASA
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -33,6 +34,16 @@ extern "C" {
* @{
*/

/**
* USB host support status
*/
struct usbh_status {
/** USB host support is initialized */
unsigned int initialized : 1;
/** USB host support is enabled */
unsigned int enabled : 1;
};

/**
* USB host support runtime context
*/
Expand All @@ -43,6 +54,8 @@ struct usbh_context {
struct k_mutex mutex;
/** Pointer to UHC device struct */
const struct device *dev;
/** Status of the USB host support */
struct usbh_status status;
/** USB device list */
sys_dlist_t udevs;
/** USB root device */
Expand All @@ -60,47 +73,105 @@ struct usbh_context {
.addr_ba = &ba_##device_name, \
}

struct usbh_class_data;

/**
* @brief USB Class Code triple
* @brief Information about a device, which is relevant for matching a particular class.
*/
struct usbh_code_triple {
/** Device Class Code */
uint8_t dclass;
/** Class Subclass Code */
struct usbh_class_filter {
/** Vendor ID */
uint16_t vid;
/** Product ID */
uint16_t pid;
/** Class Code */
uint8_t class;
/** Subclass Code */
uint8_t sub;
/** Class Protocol Code */
/** Protocol Code */
uint8_t proto;
/** Flags that tell which field to match */
uint8_t flags;
};

/**
* @brief USB host class data and class instance API
* @brief USB host class instance API
*/
struct usbh_class_data {
/** Class code supported by this instance */
struct usbh_code_triple code;

/** Initialization of the class implementation */
/* int (*init)(struct usbh_context *const uhs_ctx); */
struct usbh_class_api {
/** Host init handler, before any device is connected */
int (*init)(struct usbh_class_data *const c_data,
struct usbh_context *const uhs_ctx);
/** Request completion event handler */
int (*request)(struct usbh_context *const uhs_ctx,
struct uhc_transfer *const xfer, int err);
/** Device connected handler */
int (*connected)(struct usbh_context *const uhs_ctx);
/** Device removed handler */
int (*removed)(struct usbh_context *const uhs_ctx);
/** Bus remote wakeup handler */
int (*rwup)(struct usbh_context *const uhs_ctx);
/** Bus suspended handler */
int (*suspended)(struct usbh_context *const uhs_ctx);
/** Bus resumed handler */
int (*resumed)(struct usbh_context *const uhs_ctx);
int (*completion_cb)(struct usbh_class_data *const c_data,
struct uhc_transfer *const xfer);
/** Device connection handler */
int (*probe)(struct usbh_class_data *const c_data,
struct usb_device *const udev,
const uint8_t iface);
/** Device removal handler */
int (*removed)(struct usbh_class_data *const c_data);
/** Bus suspended handler */
int (*suspended)(struct usbh_class_data *const c_data);
/** Bus resumed handler */
int (*resumed)(struct usbh_class_data *const c_data);
};

/**
* @brief USB host class instance data
*/
#define USBH_DEFINE_CLASS(name) \
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
struct usbh_class_data {
/** Name of the USB host class instance */
const char *name;
/** Pointer to USB host stack context structure */
struct usbh_context *uhs_ctx;
/** Pointer to USB device this class is used for */
struct usb_device *udev;
/** Interface number for which this class matched */
uint8_t iface;
/** Pointer to host support class API */
struct usbh_class_api *api;
/** Pointer to private data */
void *priv;
};

/**
* @cond INTERNAL_HIDDEN
*
* Variables used by the USB host stack but not exposed to the class
* through the class API.
*/
struct usbh_class_node {
/** Class information exposed to host class implementations (drivers). */
struct usbh_class_data *const c_data;
/** Filter rules to match this USB host class instance against a device class **/
struct usbh_class_filter *filters;
/** Number of filters in the array */
size_t num_filters;
};
/* @endcond */

/**
* @brief Define USB host support class data
*
* Macro defines class (function) data, as well as corresponding node
* structures used internally by the stack.
*
* @param[in] class_name Class name
* @param[in] class_api Pointer to struct usbh_class_api
* @param[in] class_priv Class private data
* @param[in] filt Array of @ref usbh_class_filter to match this class or NULL to match everything
* @param[in] num_filt Number of filters in the array
*/
#define USBH_DEFINE_CLASS(class_name, class_api, class_priv, filt, num_filt) \
static struct usbh_class_data class_data_##class_name = { \
.name = STRINGIFY(class_name), \
.api = class_api, \
.priv = class_priv, \
}; \
static STRUCT_SECTION_ITERABLE(usbh_class_node, class_name) = { \
.c_data = &class_data_##class_name, \
.filters = filt, \
.num_filters = num_filt, \
};

/**
* @brief Initialize the USB host support;
Expand Down
1 change: 1 addition & 0 deletions subsys/usb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device)
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK_NEXT device_next)
add_subdirectory_ifdef(CONFIG_USB_HOST_STACK host)
add_subdirectory_ifdef(CONFIG_USBC_STACK usb_c)
add_subdirectory(common)
6 changes: 6 additions & 0 deletions subsys/usb/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) 2025 Linumiz GmbH
# SPDX-License-Identifier: Apache-2.0

zephyr_include_directories(include)

zephyr_library_sources_ifdef(CONFIG_USBH_CDC_ECM_CLASS usb_cdc_ecm.c)
37 changes: 37 additions & 0 deletions subsys/usb/common/include/usb_cdc_ecm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
* Copyright (c) 2025 Linumiz GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_COMMON_USB_CDC_ECM_H_
#define ZEPHYR_INCLUDE_COMMON_USB_CDC_ECM_H_

#include <zephyr/logging/log.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/usb/usb_ch9.h>
#include <zephyr/usb/class/usb_cdc.h>

#define CDC_ECM_EP_MPS_INT 16U
#define CDC_ECM_INTERVAL_DEFAULT 10000UL
#define CDC_ECM_FS_INT_EP_INTERVAL USB_FS_INT_EP_INTERVAL(10000U)
#define CDC_ECM_HS_INT_EP_INTERVAL USB_HS_INT_EP_INTERVAL(10000U)

struct cdc_ecm_notification {
union {
uint8_t bmRequestType;
struct usb_req_type_field RequestType;
};
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __packed;

size_t ecm_eth_size(void *const ecm_pkt, const size_t len);

#endif /* ZEPHYR_INCLUDE_COMMON_USB_CDC_ECM_H_ */
35 changes: 35 additions & 0 deletions subsys/usb/common/usb_cdc_ecm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 Linumiz GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "usb_cdc_ecm.h"

/* Retrieve expected pkt size from ethernet/ip header */
size_t ecm_eth_size(void *const ecm_pkt, const size_t len)
{
uint8_t *ip_data = (uint8_t *)ecm_pkt + sizeof(struct net_eth_hdr);
struct net_eth_hdr *hdr = (void *)ecm_pkt;
uint16_t ip_len;

if (len < NET_IPV6H_LEN + sizeof(struct net_eth_hdr)) {
/* Too short */
return 0;
}

switch (ntohs(hdr->type)) {
case NET_ETH_PTYPE_IP:
__fallthrough;
case NET_ETH_PTYPE_ARP:
ip_len = ntohs(((struct net_ipv4_hdr *)ip_data)->len);
break;
case NET_ETH_PTYPE_IPV6:
ip_len = ntohs(((struct net_ipv6_hdr *)ip_data)->len);
break;
default:
return 0;
}

return sizeof(struct net_eth_hdr) + ip_len;
}
9 changes: 8 additions & 1 deletion subsys/usb/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ zephyr_library()
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})

zephyr_library_sources(
usbh_api.c
usbh_ch9.c
usbh_class.c
usbh_core.c
usbh_api.c
usbh_desc.c
usbh_device.c
)

Expand All @@ -16,6 +18,11 @@ zephyr_library_sources_ifdef(
usbh_shell.c
)

zephyr_library_sources_ifdef(
CONFIG_USBH_CDC_ECM_CLASS
class/usbh_cdc_ecm.c
)

zephyr_library_sources_ifdef(
CONFIG_USBIP
usbip.c
Expand Down
1 change: 1 addition & 0 deletions subsys/usb/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ config USBH_MAX_UHC_MSG
Maximum number of USB host controller events that can be queued.

rsource "Kconfig.usbip"
rsource "class/Kconfig"

endif # USB_HOST_STACK
7 changes: 7 additions & 0 deletions subsys/usb/host/class/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright (c) 2025 Linumiz GmbH
#
# SPDX-License-Identifier: Apache-2.0
#

rsource "Kconfig.cdc_ecm_host"
20 changes: 20 additions & 0 deletions subsys/usb/host/class/Kconfig.cdc_ecm_host
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2025 Linumiz GmbH
# SPDX-License-Identifier: Apache-2.0

config USBH_CDC_ECM_CLASS
bool "USB Host CDC ECM Class implementation"
default y
depends on NET_L2_ETHERNET
depends on DT_HAS_ZEPHYR_USBH_CDC_ECM_ENABLED
help
USB Host CDC Ethernet Control Model (ECM) implementation.
Supports communication with CDC ECM-compliant USB Ethernet devices.

if USBH_CDC_ECM_CLASS

module = USBH_CDC_ECM
module-str = "usbh cdc_ecm"
default-count = 1
source "subsys/logging/Kconfig.template.log_config"

endif # USBH_CDC_ECM_CLASS
Loading