usb: device_next: Introduce speed specific configurations

USB High-Speed devices must be able to operate at both High-Speed and
Full-Speed. The USB specification allows the device to have different
configurations depending on connection speed. Modify the API to reflect
USB Specification requirements on what can (e.g. configurations) and
what cannot (e.g. VID, PID) be speed dependent.

While the class configurations for different speeds are completely
independent, the actual class instances are shared between operating
speeds (because only one speed can be active at a time). Classes are
free to provide different number of interfaces and/or endpoints for
different speeds. The endpoints are assigned for all operating speeds
during initialization.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
This commit is contained in:
Tomasz Moń 2024-02-29 11:18:28 +01:00 committed by Carles Cufí
parent 99cd7ee991
commit 5144d0f65f
23 changed files with 477 additions and 200 deletions

View file

@ -121,7 +121,8 @@ endif()
if(CONFIG_USB_DEVICE_STACK OR CONFIG_USB_DEVICE_STACK_NEXT)
zephyr_iterable_section(NAME usb_cfg_data GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
zephyr_iterable_section(NAME usbd_contex GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
zephyr_iterable_section(NAME usbd_class_iter GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
zephyr_iterable_section(NAME usbd_class_fs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
zephyr_iterable_section(NAME usbd_class_hs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
endif()
if(CONFIG_USB_HOST_STACK)

View file

@ -188,12 +188,16 @@ struct usbd_contex {
struct usbd_ch9_data ch9_data;
/** slist to manage descriptors like string, bos */
sys_dlist_t descriptors;
/** slist to manage device configurations */
sys_slist_t configs;
/** slist to manage Full-Speed device configurations */
sys_slist_t fs_configs;
/** slist to manage High-Speed device configurations */
sys_slist_t hs_configs;
/** Status of the USB device support */
struct usbd_status status;
/** Pointer to device descriptor */
void *desc;
/** Pointer to Full-Speed device descriptor */
void *fs_desc;
/** Pointer to High-Speed device descriptor */
void *hs_desc;
};
/**
@ -349,7 +353,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_node *const c
#define USBD_DEVICE_DEFINE(device_name, uhc_dev, vid, pid) \
static struct usb_device_descriptor \
desc_##device_name = { \
fs_desc_##device_name = { \
.bLength = sizeof(struct usb_device_descriptor), \
.bDescriptorType = USB_DESC_DEVICE, \
.bcdUSB = sys_cpu_to_le16(USB_SRN_2_0), \
@ -365,10 +369,28 @@ static inline void *usbd_class_get_private(const struct usbd_class_node *const c
.iSerialNumber = 0, \
.bNumConfigurations = 0, \
}; \
static struct usb_device_descriptor \
hs_desc_##device_name = { \
.bLength = sizeof(struct usb_device_descriptor), \
.bDescriptorType = USB_DESC_DEVICE, \
.bcdUSB = sys_cpu_to_le16(USB_SRN_2_0), \
.bDeviceClass = USB_BCC_MISCELLANEOUS, \
.bDeviceSubClass = 2, \
.bDeviceProtocol = 1, \
.bMaxPacketSize0 = 64, \
.idVendor = vid, \
.idProduct = pid, \
.bcdDevice = sys_cpu_to_le16(USB_BCD_DRN), \
.iManufacturer = 0, \
.iProduct = 0, \
.iSerialNumber = 0, \
.bNumConfigurations = 0, \
}; \
static STRUCT_SECTION_ITERABLE(usbd_contex, device_name) = { \
.name = STRINGIFY(device_name), \
.dev = uhc_dev, \
.desc = &desc_##device_name, \
.fs_desc = &fs_desc_##device_name, \
.hs_desc = &hs_desc_##device_name, \
}
#define USBD_CONFIGURATION_DEFINE(name, attrib, power) \
@ -474,14 +496,19 @@ static inline void *usbd_class_get_private(const struct usbd_class_node *const c
#define USBD_DESC_SERIAL_NUMBER_DEFINE(d_name, d_string) \
USBD_DESC_STRING_DEFINE(d_name, d_string, USBD_DUT_STRING_SERIAL_NUMBER)
#define USBD_DEFINE_CLASS(class_name, class_api, class_data) \
static struct usbd_class_node class_name = { \
.name = STRINGIFY(class_name), \
.api = class_api, \
.data = class_data, \
}; \
static STRUCT_SECTION_ITERABLE(usbd_class_iter, class_name##_iter) = { \
.c_nd = &class_name, \
#define USBD_DEFINE_CLASS(class_name, class_api, class_data) \
static struct usbd_class_node class_name = { \
.name = STRINGIFY(class_name), \
.api = class_api, \
.data = class_data, \
}; \
static STRUCT_SECTION_ITERABLE_ALTERNATE( \
usbd_class_fs, usbd_class_iter, class_name##_fs) = { \
.c_nd = &class_name, \
}; \
static STRUCT_SECTION_ITERABLE_ALTERNATE( \
usbd_class_hs, usbd_class_iter, class_name##_hs) = { \
.c_nd = &class_name, \
}
/** @brief Helper to declare request table of usbd_cctx_vendor_req
@ -521,11 +548,13 @@ int usbd_add_descriptor(struct usbd_contex *uds_ctx,
* @brief Add a USB device configuration
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Speed at which this configuration operates
* @param[in] cd Pointer to USB configuration node
*
* @return 0 on success, other values on fail.
*/
int usbd_add_configuration(struct usbd_contex *uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *cd);
/**
@ -544,13 +573,14 @@ int usbd_add_configuration(struct usbd_contex *uds_ctx,
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] name Class instance name
* @param[in] speed Configuration speed
* @param[in] cfg Configuration value (similar to bConfigurationValue)
*
* @return 0 on success, other values on fail.
*/
int usbd_register_class(struct usbd_contex *uds_ctx,
const char *name,
uint8_t cfg);
const enum usbd_speed speed, uint8_t cfg);
/**
* @brief Unregister an USB class instance
@ -561,13 +591,14 @@ int usbd_register_class(struct usbd_contex *uds_ctx,
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] name Class instance name
* @param[in] speed Configuration speed
* @param[in] cfg Configuration value (similar to bConfigurationValue)
*
* @return 0 on success, other values on fail.
*/
int usbd_unregister_class(struct usbd_contex *uds_ctx,
const char *name,
uint8_t cfg);
const enum usbd_speed speed, uint8_t cfg);
/**
* @brief Register USB notification message callback
@ -772,12 +803,13 @@ enum usbd_speed usbd_caps_speed(const struct usbd_contex *const uds_ctx);
* @brief Set USB device descriptor value bcdUSB
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Speed for which the bcdUSB should be set
* @param[in] bcd bcdUSB value
*
* @return 0 on success, other values on fail.
*/
int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
const uint16_t bcd);
const enum usbd_speed speed, const uint16_t bcd);
/**
* @brief Set USB device descriptor value idVendor
@ -805,6 +837,7 @@ int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
* @brief Set USB device descriptor code triple Base Class, SubClass, and Protocol
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Speed for which the code triple should be set
* @param[in] base_class bDeviceClass value
* @param[in] subclass bDeviceSubClass value
* @param[in] protocol bDeviceProtocol value
@ -812,6 +845,7 @@ int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
* @return 0 on success, other values on fail.
*/
int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t base_class,
const uint8_t subclass, const uint8_t protocol);
@ -819,36 +853,42 @@ int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
* @brief Setup USB device configuration attribute Remote Wakeup
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Configuration speed
* @param[in] cfg Configuration number
* @param[in] enable Sets attribute if true, clears it otherwise
*
* @return 0 on success, other values on fail.
*/
int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const bool enable);
/**
* @brief Setup USB device configuration attribute Self-powered
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Configuration speed
* @param[in] cfg Configuration number
* @param[in] enable Sets attribute if true, clears it otherwise
*
* @return 0 on success, other values on fail.
*/
int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const bool enable);
/**
* @brief Setup USB device configuration power consumption
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Configuration speed
* @param[in] cfg Configuration number
* @param[in] power Maximum power consumption value (bMaxPower)
*
* @return 0 on success, other values on fail.
*/
int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const uint8_t power);
/**
* @}

View file

@ -29,10 +29,94 @@ static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
(IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
USB_SCD_REMOTE_WAKEUP : 0);
USBD_CONFIGURATION_DEFINE(sample_config,
USBD_CONFIGURATION_DEFINE(sample_fs_config,
attributes,
CONFIG_SAMPLE_USBD_MAX_POWER);
USBD_CONFIGURATION_DEFINE(sample_hs_config,
attributes,
CONFIG_SAMPLE_USBD_MAX_POWER);
static int register_fs_classes(struct usbd_contex *uds_ctx)
{
int err = 0;
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
/* Pull everything that is enabled in our configuration. */
err = usbd_register_class(uds_ctx, iter->c_nd->name,
USBD_SPEED_FS, 1);
if (err) {
LOG_ERR("Failed to register FS %s (%d)",
iter->c_nd->name, err);
return err;
}
LOG_DBG("Register FS %s", iter->c_nd->name);
}
return err;
}
static int register_hs_classes(struct usbd_contex *uds_ctx)
{
int err = 0;
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_iter, iter) {
/* Pull everything that is enabled in our configuration. */
err = usbd_register_class(uds_ctx, iter->c_nd->name,
USBD_SPEED_HS, 1);
if (err) {
LOG_ERR("Failed to register HS %s (%d)",
iter->c_nd->name, err);
return err;
}
LOG_DBG("Register HS %s", iter->c_nd->name);
}
return err;
}
static int sample_add_configuration(struct usbd_contex *uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *config)
{
int err;
err = usbd_add_configuration(uds_ctx, speed, config);
if (err) {
LOG_ERR("Failed to add configuration (%d)", err);
return err;
}
if (speed == USBD_SPEED_FS) {
err = register_fs_classes(uds_ctx);
} else if (speed == USBD_SPEED_HS) {
err = register_hs_classes(uds_ctx);
}
if (err) {
return err;
}
/* Always use class code information from Interface Descriptors */
if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
/*
* Class with multiple interfaces have an Interface
* Association Descriptor available, use an appropriate triple
* to indicate it.
*/
usbd_device_set_code_triple(uds_ctx, speed,
USB_BCC_MISCELLANEOUS, 0x02, 0x01);
} else {
usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
}
return 0;
}
struct usbd_contex *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
{
int err;
@ -61,37 +145,20 @@ struct usbd_contex *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
return NULL;
}
err = usbd_add_configuration(&sample_usbd, &sample_config);
if (err) {
LOG_ERR("Failed to add configuration (%d)", err);
return NULL;
}
STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
/* Pull everything that is enabled in our configuration. */
err = usbd_register_class(&sample_usbd, iter->c_nd->name, 1);
if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
err = sample_add_configuration(&sample_usbd, USBD_SPEED_HS,
&sample_hs_config);
if (err) {
LOG_ERR("Failed to register %s (%d)",
iter->c_nd->name, err);
LOG_ERR("Failed to add High-Speed configuration");
return NULL;
}
LOG_DBG("Register %s", iter->c_nd->name);
}
/* Always use class code information from Interface Descriptors */
if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
/*
* Class with multiple interfaces have an Interface
* Association Descriptor available, use an appropriate triple
* to indicate it.
*/
usbd_device_set_code_triple(&sample_usbd,
USB_BCC_MISCELLANEOUS, 0x02, 0x01);
} else {
usbd_device_set_code_triple(&sample_usbd, 0, 0, 0);
err = sample_add_configuration(&sample_usbd, USBD_SPEED_FS,
&sample_fs_config);
if (err) {
LOG_ERR("Failed to add Full-Speed configuration");
return NULL;
}
if (msg_cb != NULL) {

View file

@ -460,16 +460,10 @@ static void *bt_hci_get_desc(struct usbd_class_node *const c_nd,
static int bt_hci_init(struct usbd_class_node *const c_nd)
{
struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
struct bt_hci_data *data = usbd_class_get_private(c_nd);
struct usbd_bt_hci_desc *desc = data->desc;
desc->iad.bFirstInterface = desc->if0.bInterfaceNumber;
if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
LOG_INF("FS endpoint descriptor needs to be updated");
desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
}
return 0;
}

View file

@ -135,16 +135,10 @@ static void *lb_get_desc(struct usbd_class_node *const c_nd,
static int lb_init(struct usbd_class_node *c_nd)
{
struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
struct lb_data *data = usbd_class_get_private(c_nd);
struct loopback_desc *desc = data->desc;
desc->iad.bFirstInterface = desc->if0.bInterfaceNumber;
if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
LOG_INF("FS endpoint descriptor needs to be updated");
desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
}
LOG_DBG("Init class instance %p", c_nd);

View file

@ -444,7 +444,6 @@ static int usbd_cdc_acm_ctd(struct usbd_class_node *const c_nd,
static int usbd_cdc_acm_init(struct usbd_class_node *const c_nd)
{
struct usbd_contex *uds_ctx = usbd_class_get_ctx(c_nd);
const struct device *dev = usbd_class_get_private(c_nd);
struct cdc_acm_uart_data *data = dev->data;
struct usbd_cdc_acm_desc *desc = data->desc;
@ -453,13 +452,6 @@ static int usbd_cdc_acm_init(struct usbd_class_node *const c_nd)
desc->if0_union.bControlInterface = desc->if0.bInterfaceNumber;
desc->if0_union.bSubordinateInterface0 = desc->if1.bInterfaceNumber;
if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
LOG_INF("FS endpoint descriptor needs to be updated");
desc->if0_int_ep.bEndpointAddress = desc->if0_hs_int_ep.bEndpointAddress;
desc->if1_in_ep.bEndpointAddress = desc->if1_hs_in_ep.bEndpointAddress;
desc->if1_out_ep.bEndpointAddress = desc->if1_hs_out_ep.bEndpointAddress;
}
return 0;
}

View file

@ -454,18 +454,6 @@ static int usbd_cdc_ecm_init(struct usbd_class_node *const c_nd)
desc->if0_union.bSubordinateInterface0 = if_num + 1;
LOG_DBG("CDC ECM class initialized");
/* Core device support configures the instance's endpoint addresses,
* but only for the highest supported speed descriptor set. Fix this
* for the case where a high-speed capable controller is connected to
* the full-speed bus.
*/
if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
LOG_INF("FS endpoint descriptor needs to be updated");
desc->if0_int_ep.bEndpointAddress = desc->if0_hs_int_ep.bEndpointAddress;
desc->if1_1_in_ep.bEndpointAddress = desc->if1_1_hs_in_ep.bEndpointAddress;
desc->if1_1_out_ep.bEndpointAddress = desc->if1_1_hs_out_ep.bEndpointAddress;
}
if (usbd_add_descriptor(c_nd->data->uds_ctx, data->mac_desc_nd)) {
LOG_ERR("Failed to add iMACAddress string descriptor");
} else {

View file

@ -766,9 +766,7 @@ static void *msc_bot_get_desc(struct usbd_class_node *const node,
/* Initialization of the class implementation */
static int msc_bot_init(struct usbd_class_node *const node)
{
struct usbd_contex *uds_ctx = usbd_class_get_ctx(node);
struct msc_bot_ctx *ctx = usbd_class_get_private(node);
struct msc_bot_desc *desc = ctx->desc;
ctx->class_node = node;
ctx->state = MSC_BBB_EXPECT_CBW;
@ -784,12 +782,6 @@ static int msc_bot_init(struct usbd_class_node *const node)
lun->vendor, lun->product, lun->revision);
}
if (usbd_caps_speed(uds_ctx) == USBD_SPEED_HS) {
LOG_INF("FS endpoint descriptor needs to be updated");
desc->if0_in_ep.bEndpointAddress = desc->if0_hs_in_ep.bEndpointAddress;
desc->if0_out_ep.bEndpointAddress = desc->if0_hs_out_ep.bEndpointAddress;
}
return 0;
}

View file

@ -122,6 +122,7 @@ static int sreq_set_address(struct usbd_contex *const uds_ctx)
static int sreq_set_configuration(struct usbd_contex *const uds_ctx)
{
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
const enum usbd_speed speed = usbd_bus_speed(uds_ctx);
int ret;
LOG_INF("Set Configuration Request value %u", setup->wValue);
@ -142,7 +143,7 @@ static int sreq_set_configuration(struct usbd_contex *const uds_ctx)
return 0;
}
if (setup->wValue && !usbd_config_exist(uds_ctx, setup->wValue)) {
if (setup->wValue && !usbd_config_exist(uds_ctx, speed, setup->wValue)) {
errno = -EPERM;
return 0;
}
@ -467,7 +468,17 @@ static int sreq_get_desc_cfg(struct usbd_contex *const uds_ctx,
return 0;
}
cfg_nd = usbd_config_get(uds_ctx, idx + 1);
if (other_cfg) {
if (speed == USBD_SPEED_FS) {
get_desc_speed = USBD_SPEED_HS;
} else {
get_desc_speed = USBD_SPEED_FS;
}
} else {
get_desc_speed = speed;
}
cfg_nd = usbd_config_get(uds_ctx, get_desc_speed, idx + 1);
if (cfg_nd == NULL) {
LOG_ERR("Configuration descriptor %u not found", idx + 1);
errno = -ENOTSUP;
@ -475,29 +486,12 @@ static int sreq_get_desc_cfg(struct usbd_contex *const uds_ctx,
}
if (other_cfg) {
/*
* Because the structure of the other-speed-configuration is
* the same as a configuration descriptor, and the other speed
* function collection has the same length and the number of
* interfaces and endpoints, we simply copy the configuration
* descriptor and update the type.
* If at some point the number of interfaces or endpoints for
* full and high speed descritpors in a class implementation
* becomes different, we need to revisit this and compute the
* configuration descriptor properties on the fly.
*/
/* Copy the configuration descriptor and update the type */
memcpy(&other_desc, cfg_nd->desc, sizeof(other_desc));
other_desc.bDescriptorType = USB_DESC_OTHER_SPEED;
cfg_desc = &other_desc;
if (speed != USBD_SPEED_HS) {
get_desc_speed = USBD_SPEED_HS;
} else {
get_desc_speed = USBD_SPEED_FS;
}
} else {
cfg_desc = cfg_nd->desc;
get_desc_speed = speed;
}
net_buf_add_mem(buf, cfg_desc, MIN(net_buf_tailroom(buf), cfg_desc->bLength));
@ -535,7 +529,17 @@ static int sreq_get_desc(struct usbd_contex *const uds_ctx,
size_t len;
if (type == USB_DESC_DEVICE) {
head = uds_ctx->desc;
switch (usbd_bus_speed(uds_ctx)) {
case USBD_SPEED_FS:
head = uds_ctx->fs_desc;
break;
case USBD_SPEED_HS:
head = uds_ctx->hs_desc;
break;
default:
errno = -ENOTSUP;
return 0;
}
} else {
head = usbd_get_descriptor(uds_ctx, type, idx);
}
@ -555,7 +559,10 @@ static int sreq_get_dev_qualifier(struct usbd_contex *const uds_ctx,
struct net_buf *const buf)
{
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
struct usb_device_descriptor *d_desc = uds_ctx->desc;
/* At Full-Speed we want High-Speed descriptor and vice versa */
struct usb_device_descriptor *d_desc =
usbd_bus_speed(uds_ctx) == USBD_SPEED_FS ?
uds_ctx->hs_desc : uds_ctx->fs_desc;
struct usb_device_qualifier_descriptor q_desc = {
.bLength = sizeof(struct usb_device_qualifier_descriptor),
.bDescriptorType = USB_DESC_DEVICE_QUALIFIER,

View file

@ -45,13 +45,14 @@ size_t usbd_class_desc_len(struct usbd_class_node *const c_nd,
struct usbd_class_iter *
usbd_class_get_by_config(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cnum,
const uint8_t inum)
{
struct usbd_class_iter *iter;
struct usbd_config_node *cfg_nd;
cfg_nd = usbd_config_get(uds_ctx, cnum);
cfg_nd = usbd_config_get(uds_ctx, speed, cnum);
if (cfg_nd == NULL) {
return NULL;
}
@ -141,6 +142,7 @@ usbd_class_get_by_ep(struct usbd_contex *const uds_ctx,
{
struct usbd_class_iter *iter;
struct usbd_config_node *cfg_nd;
enum usbd_speed speed;
uint8_t ep_idx = USB_EP_GET_IDX(ep);
uint8_t cfg;
uint32_t ep_bm;
@ -157,7 +159,8 @@ usbd_class_get_by_ep(struct usbd_contex *const uds_ctx,
}
cfg = usbd_get_config_value(uds_ctx);
cfg_nd = usbd_config_get(uds_ctx, cfg);
speed = usbd_bus_speed(uds_ctx);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
return NULL;
}
@ -202,11 +205,22 @@ usbd_class_get_by_req(struct usbd_contex *const uds_ctx,
return NULL;
}
static struct usbd_class_iter *usbd_class_iter_get(const char *name)
static struct usbd_class_iter *
usbd_class_iter_get(const char *name, const enum usbd_speed speed)
{
STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
if (strcmp(name, iter->c_nd->name) == 0) {
return iter;
if (speed == USBD_SPEED_FS) {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
usbd_class_iter, iter) {
if (strcmp(name, iter->c_nd->name) == 0) {
return iter;
}
}
} else if (speed == USBD_SPEED_HS) {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
usbd_class_iter, iter) {
if (strcmp(name, iter->c_nd->name) == 0) {
return iter;
}
}
}
@ -217,11 +231,12 @@ static struct usbd_class_iter *usbd_class_iter_get(const char *name)
static int usbd_class_append(struct usbd_contex *const uds_ctx,
struct usbd_class_iter *const iter,
const enum usbd_speed speed,
const uint8_t cfg)
{
struct usbd_config_node *cfg_nd;
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
return -ENODATA;
}
@ -233,11 +248,12 @@ static int usbd_class_append(struct usbd_contex *const uds_ctx,
static int usbd_class_remove(struct usbd_contex *const uds_ctx,
struct usbd_class_iter *const iter,
const enum usbd_speed speed,
const uint8_t cfg)
{
struct usbd_config_node *cfg_nd;
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
return -ENODATA;
}
@ -250,13 +266,14 @@ static int usbd_class_remove(struct usbd_contex *const uds_ctx,
}
int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg)
{
struct usbd_config_node *cfg_nd;
struct usbd_class_iter *iter;
sys_snode_t *node;
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
return -ENODATA;
}
@ -277,13 +294,13 @@ int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
int usbd_register_class(struct usbd_contex *const uds_ctx,
const char *name,
const uint8_t cfg)
const enum usbd_speed speed, const uint8_t cfg)
{
struct usbd_class_iter *iter;
struct usbd_class_data *data;
int ret;
iter = usbd_class_iter_get(name);
iter = usbd_class_iter_get(name, speed);
if (iter == NULL) {
return -ENODEV;
}
@ -305,7 +322,13 @@ int usbd_register_class(struct usbd_contex *const uds_ctx,
goto register_class_error;
}
ret = usbd_class_append(uds_ctx, iter, cfg);
if ((data->uds_ctx != NULL) && (data->uds_ctx != uds_ctx)) {
LOG_ERR("Class registered to other context at different speed");
ret = -EBUSY;
goto register_class_error;
}
ret = usbd_class_append(uds_ctx, iter, speed, cfg);
if (ret == 0) {
/* Initialize pointer back to the device struct */
atomic_set_bit(&iter->state, USBD_CCTX_REGISTERED);
@ -319,13 +342,14 @@ register_class_error:
int usbd_unregister_class(struct usbd_contex *const uds_ctx,
const char *name,
const uint8_t cfg)
const enum usbd_speed speed, const uint8_t cfg)
{
struct usbd_class_iter *iter;
struct usbd_class_data *data;
bool can_release_data = true;
int ret;
iter = usbd_class_iter_get(name);
iter = usbd_class_iter_get(name, speed);
if (iter == NULL) {
return -ENODEV;
}
@ -346,11 +370,37 @@ int usbd_unregister_class(struct usbd_contex *const uds_ctx,
goto unregister_class_error;
}
ret = usbd_class_remove(uds_ctx, iter, cfg);
/* TODO: The use of atomic here does not make this code thread safe.
* The atomic should be changed to something else.
*/
if (speed == USBD_SPEED_HS) {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
usbd_class_iter, i) {
if ((i->c_nd == iter->c_nd) &&
atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
can_release_data = false;
break;
}
}
} else {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
usbd_class_iter, i) {
if ((i->c_nd == iter->c_nd) &&
atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
can_release_data = false;
break;
}
}
}
ret = usbd_class_remove(uds_ctx, iter, speed, cfg);
if (ret == 0) {
atomic_clear_bit(&iter->state, USBD_CCTX_REGISTERED);
usbd_class_shutdown(iter->c_nd);
data->uds_ctx = NULL;
if (can_release_data) {
data->uds_ctx = NULL;
}
}
unregister_class_error:

View file

@ -54,12 +54,14 @@ struct usbd_class_iter *usbd_class_get_by_iface(struct usbd_contex *uds_ctx,
* @brief Get class context by configuration and interface number
*
* @param[in] uds_ctx Pointer to device context
* @param[in] speed Speed the configuration number refers to
* @param[in] cnum Configuration number
* @param[in] inum Interface number
*
* @return Class iter pointer or NULL
*/
struct usbd_class_iter *usbd_class_get_by_config(struct usbd_contex *uds_ctx,
const enum usbd_speed speed,
uint8_t cnum,
uint8_t inum);
@ -97,11 +99,13 @@ struct usbd_class_iter *usbd_class_get_by_req(struct usbd_contex *uds_ctx,
* @brief Remove all registered class instances from a configuration
*
* @param[in] uds_ctx Pointer to device context
* @param[in] speed Speed the configuration number applies to
* @param[in] cfg Configuration number (bConfigurationValue)
*
* @return 0 on success, other values on fail.
*/
int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg);
#endif /* ZEPHYR_INCLUDE_USBD_CLASS_H */

View file

@ -16,12 +16,26 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usbd_cfg, CONFIG_USBD_LOG_LEVEL);
static sys_slist_t *usbd_configs(struct usbd_contex *uds_ctx,
const enum usbd_speed speed)
{
switch (speed) {
case USBD_SPEED_FS:
return &uds_ctx->fs_configs;
case USBD_SPEED_HS:
return &uds_ctx->hs_configs;
default:
return NULL;
}
}
struct usbd_config_node *usbd_config_get(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg)
{
struct usbd_config_node *cfg_nd;
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
SYS_SLIST_FOR_EACH_CONTAINER(usbd_configs(uds_ctx, speed), cfg_nd, node) {
if (usbd_config_get_value(cfg_nd) == cfg) {
return cfg_nd;
}
@ -38,7 +52,8 @@ usbd_config_get_current(struct usbd_contex *const uds_ctx)
return NULL;
}
return usbd_config_get(uds_ctx, usbd_get_config_value(uds_ctx));
return usbd_config_get(uds_ctx, usbd_bus_speed(uds_ctx),
usbd_get_config_value(uds_ctx));
}
static void usbd_config_classes_enable(struct usbd_config_node *const cfg_nd,
@ -78,11 +93,12 @@ static int usbd_config_reset(struct usbd_contex *const uds_ctx)
}
bool usbd_config_exist(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg)
{
struct usbd_config_node *config;
config = usbd_config_get(uds_ctx, cfg);
config = usbd_config_get(uds_ctx, speed, cfg);
return (config != NULL) ? true : false;
}
@ -91,6 +107,7 @@ int usbd_config_set(struct usbd_contex *const uds_ctx,
const uint8_t new_cfg)
{
struct usbd_config_node *cfg_nd;
const enum usbd_speed speed = usbd_bus_speed(uds_ctx);
int ret;
if (usbd_get_config_value(uds_ctx) != 0) {
@ -106,12 +123,12 @@ int usbd_config_set(struct usbd_contex *const uds_ctx,
return 0;
}
cfg_nd = usbd_config_get(uds_ctx, new_cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, new_cfg);
if (cfg_nd == NULL) {
return -ENODATA;
}
ret = usbd_interface_default(uds_ctx, cfg_nd);
ret = usbd_interface_default(uds_ctx, speed, cfg_nd);
if (ret) {
return ret;
}
@ -127,6 +144,7 @@ int usbd_config_set(struct usbd_contex *const uds_ctx,
*/
int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const bool enable)
{
struct usbd_config_node *cfg_nd;
@ -148,7 +166,7 @@ int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
goto attrib_rwup_exit;
}
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
LOG_INF("Configuration %u not found", cfg);
ret = -ENODATA;
@ -168,6 +186,7 @@ attrib_rwup_exit:
}
int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const bool enable)
{
struct usbd_config_node *cfg_nd;
@ -181,7 +200,7 @@ int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
goto attrib_self_exit;
}
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
LOG_INF("Configuration %u not found", cfg);
ret = -ENODATA;
@ -201,6 +220,7 @@ attrib_self_exit:
}
int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg, const uint8_t power)
{
struct usbd_config_node *cfg_nd;
@ -214,7 +234,7 @@ int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
goto maxpower_exit;
}
cfg_nd = usbd_config_get(uds_ctx, cfg);
cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
if (cfg_nd == NULL) {
LOG_INF("Configuration %u not found", cfg);
ret = -ENODATA;
@ -230,9 +250,12 @@ maxpower_exit:
}
int usbd_add_configuration(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *const cfg_nd)
{
struct usb_cfg_descriptor *desc = cfg_nd->desc;
sys_slist_t *configs;
sys_snode_t *node;
int ret = 0;
usbd_device_lock(uds_ctx);
@ -243,6 +266,13 @@ int usbd_add_configuration(struct usbd_contex *const uds_ctx,
goto add_configuration_exit;
}
if (speed == USBD_SPEED_HS &&
usbd_caps_speed(uds_ctx) == USBD_SPEED_FS) {
LOG_ERR("Controller doesn't support HS");
ret = -ENOTSUP;
goto add_configuration_exit;
}
if (desc->bmAttributes & USB_SCD_REMOTE_WAKEUP) {
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
@ -253,15 +283,43 @@ int usbd_add_configuration(struct usbd_contex *const uds_ctx,
}
}
if (sys_slist_find_and_remove(&uds_ctx->configs, &cfg_nd->node)) {
configs = usbd_configs(uds_ctx, speed);
switch (speed) {
case USBD_SPEED_HS:
SYS_SLIST_FOR_EACH_NODE(&uds_ctx->fs_configs, node) {
if (node == &cfg_nd->node) {
LOG_ERR("HS config already on FS list");
ret = -EINVAL;
goto add_configuration_exit;
}
}
break;
case USBD_SPEED_FS:
SYS_SLIST_FOR_EACH_NODE(&uds_ctx->hs_configs, node) {
if (node == &cfg_nd->node) {
LOG_ERR("FS config already on HS list");
ret = -EINVAL;
goto add_configuration_exit;
}
}
break;
default:
LOG_ERR("Unsupported configuration speed");
ret = -ENOTSUP;
goto add_configuration_exit;
}
if (sys_slist_find_and_remove(configs, &cfg_nd->node)) {
LOG_WRN("Configuration %u re-inserted",
usbd_config_get_value(cfg_nd));
} else {
usbd_config_set_value(cfg_nd, usbd_get_num_configs(uds_ctx) + 1);
usbd_set_num_configs(uds_ctx, usbd_get_num_configs(uds_ctx) + 1);
uint8_t num = usbd_get_num_configs(uds_ctx, speed) + 1;
usbd_config_set_value(cfg_nd, num);
usbd_set_num_configs(uds_ctx, speed, num);
}
sys_slist_append(&uds_ctx->configs, &cfg_nd->node);
sys_slist_append(configs, &cfg_nd->node);
usbd_device_unlock(uds_ctx);

View file

@ -42,11 +42,13 @@ static inline void usbd_config_set_value(const struct usbd_config_node *const cf
* Get configuration node with desired configuration number.
*
* @param[in] ctx Pointer to USB device support context
* @param[in] speed Speed the configuration number applies to
* @param[in] cfg Configuration number (bConfigurationValue)
*
* @return pointer to configuration node or NULL if does not exist
*/
struct usbd_config_node *usbd_config_get(struct usbd_contex *uds_ctx,
const enum usbd_speed speed,
uint8_t cfg);
/**
@ -64,11 +66,13 @@ struct usbd_config_node *usbd_config_get_current(struct usbd_contex *uds_ctx);
* @brief Check whether a configuration exist
*
* @param[in] ctx Pointer to USB device support context
* @param[in] speed Speed at which the configuration should be checked
* @param[in] cfg Configuration number (bConfigurationValue)
*
* @return True if a configuration exist.
*/
bool usbd_config_exist(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t cfg);
/**

View file

@ -226,10 +226,19 @@ int usbd_device_shutdown_core(struct usbd_contex *const uds_ctx)
struct usbd_config_node *cfg_nd;
int ret;
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
uint8_t cfg_value = usbd_config_get_value(cfg_nd);
ret = usbd_class_remove_all(uds_ctx, cfg_value);
ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_HS, cfg_value);
if (ret) {
LOG_ERR("Failed to cleanup registered classes, %d", ret);
}
}
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
uint8_t cfg_value = usbd_config_get_value(cfg_nd);
ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_FS, cfg_value);
if (ret) {
LOG_ERR("Failed to cleanup registered classes, %d", ret);
}
@ -254,7 +263,11 @@ static int usbd_pre_init(void)
k_thread_name_set(&usbd_thread_data, "usbd");
LOG_DBG("Available USB class iterators:");
STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
atomic_set(&iter->state, 0);
LOG_DBG("\t%p->%p, name %s", iter, iter->c_nd, iter->c_nd->name);
}
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_iter, iter) {
atomic_set(&iter->state, 0);
LOG_DBG("\t%p->%p, name %s", iter, iter->c_nd, iter->c_nd->name);
}

View file

@ -1,4 +1,5 @@
#include <zephyr/linker/iterable_sections.h>
ITERABLE_SECTION_RAM(usbd_contex, Z_LINK_ITERABLE_SUBALIGN)
ITERABLE_SECTION_RAM(usbd_class_iter, Z_LINK_ITERABLE_SUBALIGN)
ITERABLE_SECTION_RAM(usbd_class_fs, Z_LINK_ITERABLE_SUBALIGN)
ITERABLE_SECTION_RAM(usbd_class_hs, Z_LINK_ITERABLE_SUBALIGN)

View file

@ -203,13 +203,15 @@ int usbd_desc_remove_all(struct usbd_contex *const uds_ctx)
int usbd_add_descriptor(struct usbd_contex *const uds_ctx,
struct usbd_desc_node *const desc_nd)
{
struct usb_device_descriptor *dev_desc = uds_ctx->desc;
struct usb_device_descriptor *hs_desc, *fs_desc;
struct usb_desc_header *head;
int ret = 0;
usbd_device_lock(uds_ctx);
if (dev_desc == NULL || usbd_is_initialized(uds_ctx)) {
hs_desc = uds_ctx->hs_desc;
fs_desc = uds_ctx->fs_desc;
if (!fs_desc || !hs_desc || usbd_is_initialized(uds_ctx)) {
ret = -EPERM;
goto add_descriptor_error;
}
@ -237,10 +239,12 @@ int usbd_add_descriptor(struct usbd_contex *const uds_ctx,
case USBD_DUT_STRING_LANG:
break;
case USBD_DUT_STRING_MANUFACTURER:
dev_desc->iManufacturer = desc_nd->idx;
hs_desc->iManufacturer = desc_nd->idx;
fs_desc->iManufacturer = desc_nd->idx;
break;
case USBD_DUT_STRING_PRODUCT:
dev_desc->iProduct = desc_nd->idx;
hs_desc->iProduct = desc_nd->idx;
fs_desc->iProduct = desc_nd->idx;
break;
case USBD_DUT_STRING_SERIAL_NUMBER:
if (!desc_nd->custom_sn) {
@ -248,7 +252,8 @@ int usbd_add_descriptor(struct usbd_contex *const uds_ctx,
desc_nd->utf16le = false;
}
dev_desc->iSerialNumber = desc_nd->idx;
hs_desc->iSerialNumber = desc_nd->idx;
fs_desc->iSerialNumber = desc_nd->idx;
break;
default:
break;

View file

@ -37,10 +37,25 @@ enum usbd_speed usbd_caps_speed(const struct usbd_contex *const uds_ctx)
return USBD_SPEED_FS;
}
int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
const uint16_t bcd)
static struct usb_device_descriptor *
get_device_descriptor(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
switch (speed) {
case USBD_SPEED_FS:
return uds_ctx->fs_desc;
case USBD_SPEED_HS:
return uds_ctx->hs_desc;
default:
__ASSERT(false, "Not supported speed");
return NULL;
}
}
int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed, const uint16_t bcd)
{
struct usb_device_descriptor *desc;
int ret = 0;
usbd_device_lock(uds_ctx);
@ -50,6 +65,7 @@ int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
goto set_bcd_exit;
}
desc = get_device_descriptor(uds_ctx, speed);
desc->bcdUSB = sys_cpu_to_le16(bcd);
set_bcd_exit:
@ -60,7 +76,7 @@ set_bcd_exit:
int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
const uint16_t vid)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *fs_desc, *hs_desc;
int ret = 0;
usbd_device_lock(uds_ctx);
@ -70,7 +86,11 @@ int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
goto set_vid_exit;
}
desc->idVendor = sys_cpu_to_le16(vid);
fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
fs_desc->idVendor = sys_cpu_to_le16(vid);
hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
hs_desc->idVendor = sys_cpu_to_le16(vid);
set_vid_exit:
usbd_device_unlock(uds_ctx);
@ -80,7 +100,7 @@ set_vid_exit:
int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
const uint16_t pid)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *fs_desc, *hs_desc;
int ret = 0;
usbd_device_lock(uds_ctx);
@ -90,7 +110,11 @@ int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
goto set_pid_exit;
}
desc->idProduct = sys_cpu_to_le16(pid);
fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
fs_desc->idProduct = sys_cpu_to_le16(pid);
hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
hs_desc->idProduct = sys_cpu_to_le16(pid);
set_pid_exit:
usbd_device_unlock(uds_ctx);
@ -98,10 +122,11 @@ set_pid_exit:
}
int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t base_class,
const uint8_t subclass, const uint8_t protocol)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *desc;
int ret = 0;
usbd_device_lock(uds_ctx);
@ -111,6 +136,7 @@ int usbd_device_set_code_triple(struct usbd_contex *const uds_ctx,
goto set_code_triple_exit;
}
desc = get_device_descriptor(uds_ctx, speed);
desc->bDeviceClass = base_class;
desc->bDeviceSubClass = subclass;
desc->bDeviceProtocol = protocol;

View file

@ -14,12 +14,22 @@
* @brief Get device descriptor bNumConfigurations value
*
* @param[in] uds_ctx Pointer to a device context
* @param[in] speed Speed for which the bNumConfigurations should be returned
*
* @return bNumConfigurations value
*/
static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_ctx)
static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_ctx,
const enum usbd_speed speed)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *desc;
if (speed == USBD_SPEED_FS) {
desc = uds_ctx->fs_desc;
} else if (speed == USBD_SPEED_HS) {
desc = uds_ctx->hs_desc;
} else {
return 0;
}
return desc->bNumConfigurations;
}
@ -29,12 +39,22 @@ static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_c
* @brief Set device descriptor bNumConfigurations value
*
* @param[in] uds_ctx Pointer to a device context
* @param[in] speed Speed for which the bNumConfigurations should be set
* @param[in] value new bNumConfigurations value
*/
static inline void usbd_set_num_configs(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
const uint8_t value)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *desc;
if (speed == USBD_SPEED_FS) {
desc = uds_ctx->fs_desc;
} else if (speed == USBD_SPEED_HS) {
desc = uds_ctx->hs_desc;
} else {
return;
}
desc->bNumConfigurations = value;
}

View file

@ -109,6 +109,7 @@ static int unassign_eps(struct usbd_contex *const uds_ctx,
* USB device configuration.
*/
static int init_configuration_inst(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
struct usbd_class_iter *const iter,
uint32_t *const config_ep_bm,
uint8_t *const nif)
@ -116,27 +117,11 @@ static int init_configuration_inst(struct usbd_contex *const uds_ctx,
struct usb_desc_header **dhp;
struct usb_if_descriptor *ifd = NULL;
struct usb_ep_descriptor *ed;
enum usbd_speed speed;
uint32_t class_ep_bm = 0;
uint8_t tmp_nif;
int ret;
/*
* Read the highest speed supported by the controller and use it to get
* the appropriate function descriptor from the instance. If the
* controller only supports full speed, the code below will configure
* the function descriptor for full speed only, and high speed will
* never be used after the device instance is initialized. If the
* controller supports high speed, the code will only configure the
* function's high speed descriptor, and the function implementation
* must update the full speed descriptor during the init callback
* processing, which is required to properly respond to
* other-speed-configuration descriptor requests and for the unlikely
* case where the high speed controller is connected to a full speed bus.
*/
speed = usbd_caps_speed(uds_ctx);
LOG_DBG("Highest speed supported by the controller is %u", speed);
LOG_DBG("Initializing configuration for %u speed", speed);
dhp = usbd_class_get_desc(iter->c_nd, speed);
if (dhp == NULL) {
return 0;
@ -207,6 +192,7 @@ static int init_configuration_inst(struct usbd_contex *const uds_ctx,
* Iterate on a list of all classes in a configuration
*/
static int init_configuration(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *const cfg_nd)
{
struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
@ -218,7 +204,7 @@ static int init_configuration(struct usbd_contex *const uds_ctx,
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, iter, node) {
ret = init_configuration_inst(uds_ctx, iter,
ret = init_configuration_inst(uds_ctx, speed, iter,
&config_ep_bm, &nif);
if (ret != 0) {
LOG_ERR("Failed to assign endpoint addresses");
@ -231,9 +217,9 @@ static int init_configuration(struct usbd_contex *const uds_ctx,
return ret;
}
LOG_INF("Init class node %p, descriptor length %zu", iter->c_nd,
usbd_class_desc_len(iter->c_nd, usbd_caps_speed(uds_ctx)));
cfg_len += usbd_class_desc_len(iter->c_nd, usbd_caps_speed(uds_ctx));
LOG_INF("Init class node %p, descriptor length %zu",
iter->c_nd, usbd_class_desc_len(iter->c_nd, speed));
cfg_len += usbd_class_desc_len(iter->c_nd, speed);
}
/* Update wTotalLength and bNumInterfaces of configuration descriptor */
@ -257,10 +243,10 @@ static int init_configuration(struct usbd_contex *const uds_ctx,
return 0;
}
static void usbd_init_update_mps0(struct usbd_contex *const uds_ctx)
static void usbd_init_update_fs_mps0(struct usbd_contex *const uds_ctx)
{
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
struct usb_device_descriptor *desc = uds_ctx->desc;
struct usb_device_descriptor *desc = uds_ctx->fs_desc;
switch (caps.mps0) {
case UDC_MPS0_8:
@ -282,20 +268,34 @@ int usbd_init_configurations(struct usbd_contex *const uds_ctx)
{
struct usbd_config_node *cfg_nd;
usbd_init_update_mps0(uds_ctx);
usbd_init_update_fs_mps0(uds_ctx);
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
int ret;
ret = init_configuration(uds_ctx, cfg_nd);
ret = init_configuration(uds_ctx, USBD_SPEED_HS, cfg_nd);
if (ret) {
LOG_ERR("Failed to init configuration %u",
LOG_ERR("Failed to init HS configuration %u",
usbd_config_get_value(cfg_nd));
return ret;
}
LOG_INF("bNumConfigurations %u",
usbd_get_num_configs(uds_ctx));
LOG_INF("HS bNumConfigurations %u",
usbd_get_num_configs(uds_ctx, USBD_SPEED_HS));
}
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
int ret;
ret = init_configuration(uds_ctx, USBD_SPEED_FS, cfg_nd);
if (ret) {
LOG_ERR("Failed to init FS configuration %u",
usbd_config_get_value(cfg_nd));
return ret;
}
LOG_INF("FS bNumConfigurations %u",
usbd_get_num_configs(uds_ctx, USBD_SPEED_FS));
}
return 0;

View file

@ -140,6 +140,7 @@ int usbd_interface_shutdown(struct usbd_contex *const uds_ctx,
}
int usbd_interface_default(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *const cfg_nd)
{
struct usb_cfg_descriptor *desc = cfg_nd->desc;
@ -150,7 +151,7 @@ int usbd_interface_default(struct usbd_contex *const uds_ctx,
struct usbd_class_iter *class;
int ret;
class = usbd_class_get_by_config(uds_ctx, new_cfg, i);
class = usbd_class_get_by_config(uds_ctx, speed, new_cfg, i);
if (class == NULL) {
return -ENODATA;
}

View file

@ -26,11 +26,13 @@ int usbd_interface_shutdown(struct usbd_contex *const uds_ctx,
* @note Used only for configuration change.
*
* @param[in] uds_ctx Pointer to USB device support context
* @param[in] speed Configuration speed
* @param[in] cfg_nd Pointer to configuration node
*
* @return 0 on success, other values on fail.
*/
int usbd_interface_default(struct usbd_contex *const uds_ctx,
const enum usbd_speed speed,
struct usbd_config_node *const cfg_nd);
/**

View file

@ -28,6 +28,8 @@ USBD_DEVICE_DEFINE(sh_uds_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
0x2fe3, 0xffff);
static struct usbd_contex *my_uds_ctx = &sh_uds_ctx;
/* TODO: Rework commands to allow specifying speed */
static enum usbd_speed speed = USBD_SPEED_FS;
int cmd_wakeup_request(const struct shell *sh,
size_t argc, char **argv)
@ -51,7 +53,7 @@ static int cmd_register(const struct shell *sh,
int ret;
cfg = strtol(argv[2], NULL, 10);
ret = usbd_register_class(my_uds_ctx, argv[1], cfg);
ret = usbd_register_class(my_uds_ctx, argv[1], speed, cfg);
if (ret) {
shell_error(sh,
@ -73,7 +75,7 @@ static int cmd_unregister(const struct shell *sh,
int ret;
cfg = strtol(argv[2], NULL, 10);
ret = usbd_unregister_class(my_uds_ctx, argv[1], cfg);
ret = usbd_unregister_class(my_uds_ctx, argv[1], speed, cfg);
if (ret) {
shell_error(sh,
"dev: failed to remove USB class %s from configuration %u",
@ -101,13 +103,13 @@ static int cmd_usbd_magic(const struct shell *sh,
shell_error(sh, "dev: Failed to initialize descriptors, %d", err);
}
err = usbd_add_configuration(my_uds_ctx, &config_foo);
err = usbd_add_configuration(my_uds_ctx, speed, &config_foo);
if (err) {
shell_error(sh, "dev: Failed to add configuration");
}
if (IS_ENABLED(CONFIG_USBD_LOOPBACK_CLASS)) {
err = usbd_register_class(my_uds_ctx, "loopback_0", 1);
err = usbd_register_class(my_uds_ctx, "loopback_0", speed, 1);
if (err) {
shell_error(sh, "dev: Failed to add loopback_0 class");
}
@ -242,7 +244,7 @@ static int cmd_device_bcd(const struct shell *sh, size_t argc,
int ret;
bcd = strtol(argv[1], NULL, 16);
ret = usbd_device_set_bcd(my_uds_ctx, bcd);
ret = usbd_device_set_bcd(my_uds_ctx, speed, bcd);
if (ret) {
shell_error(sh, "dev: failed to set device bcdUSB to %x", bcd);
}
@ -289,7 +291,8 @@ static int cmd_device_code_triple(const struct shell *sh, size_t argc,
class = strtol(argv[1], NULL, 16);
subclass = strtol(argv[2], NULL, 16);
protocol = strtol(argv[3], NULL, 16);
ret = usbd_device_set_code_triple(my_uds_ctx, class, subclass, protocol);
ret = usbd_device_set_code_triple(my_uds_ctx, speed,
class, subclass, protocol);
if (ret) {
shell_error(sh, "dev: failed to set device code triple to %x %x %x",
class, subclass, protocol);
@ -307,9 +310,9 @@ static int cmd_config_add(const struct shell *sh, size_t argc,
cfg = strtol(argv[1], NULL, 10);
if (cfg == 1) {
ret = usbd_add_configuration(my_uds_ctx, &config_foo);
ret = usbd_add_configuration(my_uds_ctx, speed, &config_foo);
} else if (cfg == 2) {
ret = usbd_add_configuration(my_uds_ctx, &config_baz);
ret = usbd_add_configuration(my_uds_ctx, speed, &config_baz);
} else {
shell_error(sh, "dev: Configuration %u not available", cfg);
return -EINVAL;
@ -336,7 +339,7 @@ static int cmd_config_self(const struct shell *sh, size_t argc,
self = false;
}
ret = usbd_config_attrib_self(my_uds_ctx, cfg, self);
ret = usbd_config_attrib_self(my_uds_ctx, speed, cfg, self);
if (ret) {
shell_error(sh,
"dev: failed to set attribute self powered to %u",
@ -360,7 +363,7 @@ static int cmd_config_rwup(const struct shell *sh, size_t argc,
rwup = false;
}
ret = usbd_config_attrib_rwup(my_uds_ctx, cfg, rwup);
ret = usbd_config_attrib_rwup(my_uds_ctx, speed, cfg, rwup);
if (ret) {
shell_error(sh,
"dev: failed to set attribute remote wakeup to %x",
@ -380,7 +383,7 @@ static int cmd_config_power(const struct shell *sh, size_t argc,
cfg = strtol(argv[1], NULL, 10);
power = strtol(argv[1], NULL, 10);
ret = usbd_config_maxpower(my_uds_ctx, cfg, power);
ret = usbd_config_maxpower(my_uds_ctx, speed, cfg, power);
if (ret) {
shell_error(sh, "dev: failed to set bMaxPower value to %u", cfg);
}
@ -397,7 +400,7 @@ static void class_node_name_lookup(size_t idx, struct shell_static_entry *entry)
entry->help = NULL;
entry->subcmd = NULL;
STRUCT_SECTION_FOREACH(usbd_class_iter, iter) {
STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_iter, iter) {
if ((iter->c_nd->name != NULL) &&
(strlen(iter->c_nd->name) != 0)) {
if (match_idx == idx) {

View file

@ -17,10 +17,15 @@ LOG_MODULE_REGISTER(usb_test, LOG_LEVEL_INF);
#define TEST_DEFAULT_INTERFACE 0
#define TEST_DEFAULT_ALTERNATE 1
USBD_CONFIGURATION_DEFINE(test_config,
USBD_CONFIGURATION_DEFINE(test_fs_config,
USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP,
200);
USBD_CONFIGURATION_DEFINE(test_hs_config,
USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP,
200);
USBD_DESC_LANG_DEFINE(test_lang);
USBD_DESC_STRING_DEFINE(test_mfg, "ZEPHYR", 1);
USBD_DESC_STRING_DEFINE(test_product, "Zephyr USB Test", 2);
@ -120,10 +125,20 @@ static void *usb_test_enable(void)
err = usbd_add_descriptor(&test_usbd, &test_sn);
zassert_equal(err, 0, "Failed to initialize descriptor (%d)", err);
err = usbd_add_configuration(&test_usbd, &test_config);
if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) {
err = usbd_add_configuration(&test_usbd, USBD_SPEED_HS, &test_hs_config);
zassert_equal(err, 0, "Failed to add configuration (%d)");
}
err = usbd_add_configuration(&test_usbd, USBD_SPEED_FS, &test_fs_config);
zassert_equal(err, 0, "Failed to add configuration (%d)");
err = usbd_register_class(&test_usbd, "loopback_0", 1);
if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) {
err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_HS, 1);
zassert_equal(err, 0, "Failed to register loopback_0 class (%d)");
}
err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_FS, 1);
zassert_equal(err, 0, "Failed to register loopback_0 class (%d)");
err = usbd_init(&test_usbd);