Skip to content
Open
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
105 changes: 75 additions & 30 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,79 @@ struct usbh_context {
.addr_ba = &ba_##device_name, \
}

struct usbh_class_data;

/**
* @brief USB Class Code triple
* @brief USB host class instance API
*/
struct usbh_code_triple {
/** Device Class Code */
uint8_t dclass;
/** Class Subclass Code */
uint8_t sub;
/** Class Protocol Code */
uint8_t proto;
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 (*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 data and class instance API
* @brief USB host class instance data
*/
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); */
/** 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);
/** 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 ifnum;
/** 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.
*/
#define USBH_DEFINE_CLASS(name) \
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
struct usbh_class_node {
/** Class information exposed to host class implementations (drivers). */
struct usbh_class_data *const c_data;
};
/* @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
*/
#define USBH_DEFINE_CLASS(class_name, class_api, class_priv) \
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, \
};

/**
* @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)
4 changes: 4 additions & 0 deletions subsys/usb/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) 2025 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0

zephyr_include_directories(include)
3 changes: 2 additions & 1 deletion subsys/usb/device_next/class/usbd_uvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
#include <zephyr/usb/usb_ch9.h>
#include <zephyr/usb/class/usbd_uvc.h>

#include "usbd_uvc.h"
#include "usb_uvc.h"

#include "../../../drivers/video/video_ctrls.h"
#include "../../../drivers/video/video_device.h"

Expand Down
4 changes: 3 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 Down
156 changes: 156 additions & 0 deletions subsys/usb/host/usbh_class.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/usb/usbh.h>
#include <zephyr/logging/log.h>

#include "usbh_class.h"
#include "usbh_class_api.h"
#include "usbh_desc.h"

LOG_MODULE_REGISTER(usbh_class, CONFIG_USBH_LOG_LEVEL);

int usbh_class_init_all(struct usbh_context *const uhs_ctx)
{
int ret;

STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
ret = usbh_class_init(c_node->c_data, uhs_ctx);
if (ret != 0) {
LOG_ERR("Failed to initialize all registered class instances");
return ret;
}
}

return 0;
}

int usbh_class_remove_all(const struct usb_device *const udev)
{
int ret;

STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
if (c_node->c_data->udev == udev) {
ret = usbh_class_removed(c_node->c_data);
if (ret != 0) {
LOG_ERR("Failed to handle device removal for each classes");
return ret;
}

/* The class instance is now free to bind to a new device */
c_node->c_data->udev = NULL;
}
}

return 0;
}

static int usbh_class_probe_one(struct usbh_class_data *const c_data, struct usb_device *const udev)
{
const uint8_t *const desc_beg = usbh_desc_get_cfg_beg(udev);
const uint8_t *const desc_end = usbh_desc_get_cfg_end(udev);
const struct usb_desc_header *desc;
const struct usb_cfg_descriptor *cfg_desc;
int ret;

desc = usbh_desc_get_by_type(desc_beg, desc_end, USB_DESC_CONFIGURATION);
if (desc == NULL) {
LOG_ERR("Unable to find a configuration descriptor");
return -EBADMSG;
}

cfg_desc = (struct usb_cfg_descriptor *)desc;

for (uint8_t ifnum = 0; ifnum < cfg_desc->bNumInterfaces; ifnum++) {
ret = usbh_class_probe(c_data, udev, ifnum);
if (ret == -ENOTSUP) {
LOG_DBG("Class %s not supporting this device, skipping", c_data->name);
continue;
}
if (ret != 0) {
LOG_ERR("Failed to register the class %s for interface %u",
c_data->name, ifnum);
return ret;
}
if (ret == 0) {
LOG_INF("Class %s is matching this device for interface %u, assigning it",
c_data->name, ifnum);
c_data->udev = udev;
c_data->ifnum = ifnum;
return 0;
}
}

return -ENOTSUP;
}

int usbh_class_probe_all(struct usb_device *const udev)
{
int ret;

STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
struct usbh_class_data *const c_data = c_node->c_data;

/* If the class is not already having a device */
if (c_data->udev == NULL) {
ret = usbh_class_probe_one(c_data, udev);
if (ret == -ENOTSUP) {
continue;
}
if (ret != 0) {
return ret;
}
}
}

LOG_WRN("Could not find any class for this device");
return -ENODEV;
}

bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t n_filters,
struct usb_device_descriptor *const desc)
{
for (int i = 0; i < n_filters; i++) {
const struct usbh_class_filter *filt = &filters[i];

if (filt->flags & USBH_CLASS_MATCH_VID) {
if (desc->idVendor != filt->vid) {
continue;
}
}

if (filt->flags & USBH_CLASS_MATCH_PID) {
if (desc->idProduct != filt->pid) {
continue;
}
}

if (filt->flags & USBH_CLASS_MATCH_DCLASS) {
if (desc->bDeviceClass != filt->dclass) {
continue;
}
}

if (filt->flags & USBH_CLASS_MATCH_SUB) {
if (desc->bDeviceSubClass != filt->sub) {
continue;
}
}

if (filt->flags & USBH_CLASS_MATCH_PROTO) {
if (desc->bDeviceProtocol != filt->proto) {
continue;
}
}

/* All the selected filters did match */
return true;
}

/* At the end of the filter table and still no match */
return false;
}
Loading
Loading