usb: add new experimental USB device stack
The device supprt brings support for multiple stack instances, multiple configuration, asynchronous transfer model, ability to change most of the properties of a device at runtime and the composition of configuration and classes at runtime. The stack requires new UDC driver API and is not compatible with old driver API (usb_dc_). The classes (functions) of old (current) USB device stack cannot be used with new ones and must be ported. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
parent
db1fb82c18
commit
cb8b9ad38a
29 changed files with 5633 additions and 0 deletions
|
@ -52,6 +52,8 @@ source "subsys/usb/usb_c/Kconfig"
|
|||
|
||||
source "subsys/sd/Kconfig"
|
||||
|
||||
source "subsys/usb/device_next/Kconfig"
|
||||
|
||||
source "subsys/dfu/Kconfig"
|
||||
|
||||
source "subsys/random/Kconfig"
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device)
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK_NEXT device_next)
|
||||
add_subdirectory_ifdef(CONFIG_USBC_STACK usb_c)
|
||||
|
|
29
subsys/usb/device_next/CMakeLists.txt
Normal file
29
subsys/usb/device_next/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
zephyr_library_sources(
|
||||
usbd_device.c
|
||||
usbd_desc.c
|
||||
usbd_ch9.c
|
||||
usbd_core.c
|
||||
usbd_init.c
|
||||
usbd_config.c
|
||||
usbd_class.c
|
||||
usbd_interface.c
|
||||
usbd_endpoint.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_USBD_SHELL
|
||||
usbd_shell.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_USBD_LOOPBACK_CLASS
|
||||
class/loopback.c
|
||||
)
|
||||
|
||||
zephyr_linker_sources(DATA_SECTIONS usbd_data.ld)
|
46
subsys/usb/device_next/Kconfig
Normal file
46
subsys/usb/device_next/Kconfig
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig USB_DEVICE_STACK_NEXT
|
||||
bool "New USB device stack [EXPERIMENTAL]"
|
||||
select EXPERIMENTAL
|
||||
select UDC_DRIVER
|
||||
select HWINFO
|
||||
help
|
||||
New experimental USB device stack.
|
||||
|
||||
if USB_DEVICE_STACK_NEXT
|
||||
|
||||
module = USBD
|
||||
module-str = usbd
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config USBD_SHELL
|
||||
bool "USB device shell"
|
||||
default y
|
||||
depends on SHELL
|
||||
help
|
||||
Enable USB device shell.
|
||||
|
||||
config USBD_THREAD_INIT_PRIO
|
||||
int
|
||||
default 90
|
||||
help
|
||||
USB device thread initialization priority level.
|
||||
|
||||
config USBD_THREAD_STACK_SIZE
|
||||
int "USB device stack thread stack size"
|
||||
default 1024
|
||||
help
|
||||
USB device stack thread stack size in bytes.
|
||||
|
||||
config USBD_MAX_UDC_MSG
|
||||
int "Maximum number of UDC events"
|
||||
default 10
|
||||
help
|
||||
Maximum number of USB device controller events that can be queued.
|
||||
|
||||
rsource "class/Kconfig"
|
||||
|
||||
endif # USB_DEVICE_STACK_NEXT
|
5
subsys/usb/device_next/class/Kconfig
Normal file
5
subsys/usb/device_next/class/Kconfig
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
rsource "Kconfig.loopback"
|
18
subsys/usb/device_next/class/Kconfig.loopback
Normal file
18
subsys/usb/device_next/class/Kconfig.loopback
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config USBD_LOOPBACK_CLASS
|
||||
bool "USB Loopback Class"
|
||||
help
|
||||
USB device loopback class.
|
||||
Primarily used for test and development purposes.
|
||||
|
||||
if USBD_LOOPBACK_CLASS
|
||||
module = USBD_LOOPBACK
|
||||
module-str = usbd loopback
|
||||
default-count = 1
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
rsource "Kconfig.template.composite_device_number"
|
||||
endif
|
|
@ -0,0 +1,11 @@
|
|||
# Kconfig template file for setting device count for
|
||||
# various USB classes.
|
||||
|
||||
# Copyright (c) 2019 Intel Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config $(module)_DEVICE_COUNT
|
||||
int "Number of $(module) Devices"
|
||||
default $(default-count)
|
||||
help
|
||||
Number of instances of this USB Device class.
|
301
subsys/usb/device_next/class/loopback.c
Normal file
301
subsys/usb/device_next/class/loopback.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usb_loopback, CONFIG_USBD_LOOPBACK_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* NOTE: this class is experimental and is in development.
|
||||
* Primary purpose currently is testing of the class initialization and
|
||||
* interface and endpoint configuration.
|
||||
*/
|
||||
|
||||
/* Internal buffer for intermidiate test data */
|
||||
static uint8_t lb_buf[1024];
|
||||
|
||||
#define LB_VENDOR_REQ_OUT 0x5b
|
||||
#define LB_VENDOR_REQ_IN 0x5c
|
||||
|
||||
/* Make supported vendor request visible for the device stack */
|
||||
static const struct usbd_cctx_vendor_req lb_vregs =
|
||||
USBD_VENDOR_REQ(LB_VENDOR_REQ_OUT, LB_VENDOR_REQ_IN);
|
||||
|
||||
struct loopback_desc {
|
||||
struct usb_if_descriptor if0;
|
||||
struct usb_ep_descriptor if0_out_ep;
|
||||
struct usb_ep_descriptor if0_in_ep;
|
||||
struct usb_ep_descriptor if0_int_out_ep;
|
||||
struct usb_ep_descriptor if0_int_in_ep;
|
||||
struct usb_ep_descriptor if0_iso_out_ep;
|
||||
struct usb_ep_descriptor if0_iso_in_ep;
|
||||
struct usb_if_descriptor if1;
|
||||
struct usb_if_descriptor if2;
|
||||
struct usb_ep_descriptor if2_out_ep;
|
||||
struct usb_ep_descriptor if2_in_ep;
|
||||
struct usb_if_descriptor if3;
|
||||
struct usb_ep_descriptor if3_out_ep;
|
||||
struct usb_ep_descriptor if3_in_ep;
|
||||
struct usb_desc_header term_desc;
|
||||
} __packed;
|
||||
|
||||
#define DEFINE_LOOPBACK_DESCRIPTOR(x, _) \
|
||||
static struct loopback_desc lb_desc_##x = { \
|
||||
/* Interface descriptor 0 */ \
|
||||
.if0 = { \
|
||||
.bLength = sizeof(struct usb_if_descriptor), \
|
||||
.bDescriptorType = USB_DESC_INTERFACE, \
|
||||
.bInterfaceNumber = 0, \
|
||||
.bAlternateSetting = 0, \
|
||||
.bNumEndpoints = 6, \
|
||||
.bInterfaceClass = USB_BCC_VENDOR, \
|
||||
.bInterfaceSubClass = 0, \
|
||||
.bInterfaceProtocol = 0, \
|
||||
.iInterface = 0, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint OUT */ \
|
||||
.if0_out_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x01, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint IN */ \
|
||||
.if0_in_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x81, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Interface Endpoint OUT */ \
|
||||
.if0_int_out_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x03, \
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x01, \
|
||||
}, \
|
||||
\
|
||||
/* Interrupt Endpoint IN */ \
|
||||
.if0_int_in_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x83, \
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x01, \
|
||||
}, \
|
||||
\
|
||||
/* Endpoint ISO OUT */ \
|
||||
.if0_iso_out_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x03, \
|
||||
.bmAttributes = USB_EP_TYPE_ISO, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x01, \
|
||||
}, \
|
||||
\
|
||||
/* Endpoint ISO IN */ \
|
||||
.if0_iso_in_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x83, \
|
||||
.bmAttributes = USB_EP_TYPE_ISO, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x01, \
|
||||
}, \
|
||||
\
|
||||
/* Interface descriptor 1, no endpoints */ \
|
||||
.if1 = { \
|
||||
.bLength = sizeof(struct usb_if_descriptor), \
|
||||
.bDescriptorType = USB_DESC_INTERFACE, \
|
||||
.bInterfaceNumber = 1, \
|
||||
.bAlternateSetting = 0, \
|
||||
.bNumEndpoints = 0, \
|
||||
.bInterfaceClass = USB_BCC_VENDOR, \
|
||||
.bInterfaceSubClass = 0, \
|
||||
.bInterfaceProtocol = 0, \
|
||||
.iInterface = 0, \
|
||||
}, \
|
||||
\
|
||||
/* Interface descriptor 1 */ \
|
||||
.if2 = { \
|
||||
.bLength = sizeof(struct usb_if_descriptor), \
|
||||
.bDescriptorType = USB_DESC_INTERFACE, \
|
||||
.bInterfaceNumber = 1, \
|
||||
.bAlternateSetting = 1, \
|
||||
.bNumEndpoints = 2, \
|
||||
.bInterfaceClass = USB_BCC_VENDOR, \
|
||||
.bInterfaceSubClass = 0, \
|
||||
.bInterfaceProtocol = 0, \
|
||||
.iInterface = 0, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint OUT */ \
|
||||
.if2_out_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x02, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 32, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint IN */ \
|
||||
.if2_in_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x82, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 32, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Interface descriptor 1 */ \
|
||||
.if3 = { \
|
||||
.bLength = sizeof(struct usb_if_descriptor), \
|
||||
.bDescriptorType = USB_DESC_INTERFACE, \
|
||||
.bInterfaceNumber = 1, \
|
||||
.bAlternateSetting = 2, \
|
||||
.bNumEndpoints = 2, \
|
||||
.bInterfaceClass = USB_BCC_VENDOR, \
|
||||
.bInterfaceSubClass = 0, \
|
||||
.bInterfaceProtocol = 0, \
|
||||
.iInterface = 0, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint OUT, get wMaxPacketSize from UDC */ \
|
||||
.if3_out_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x02, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Data Endpoint IN, get wMaxPacketSize from UDC */ \
|
||||
.if3_in_ep = { \
|
||||
.bLength = sizeof(struct usb_ep_descriptor), \
|
||||
.bDescriptorType = USB_DESC_ENDPOINT, \
|
||||
.bEndpointAddress = 0x82, \
|
||||
.bmAttributes = USB_EP_TYPE_BULK, \
|
||||
.wMaxPacketSize = 0, \
|
||||
.bInterval = 0x00, \
|
||||
}, \
|
||||
\
|
||||
/* Termination descriptor */ \
|
||||
.term_desc = { \
|
||||
.bLength = 0, \
|
||||
.bDescriptorType = 0, \
|
||||
}, \
|
||||
}; \
|
||||
|
||||
static void lb_update(struct usbd_class_node *c_nd,
|
||||
uint8_t iface, uint8_t alternate)
|
||||
{
|
||||
LOG_DBG("Instance %p, interface %u alternate %u changed",
|
||||
c_nd, iface, alternate);
|
||||
}
|
||||
|
||||
static int lb_control_to_host(struct usbd_class_node *c_nd,
|
||||
const struct usb_setup_packet *const setup,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usbd_contex *uds_ctx = c_nd->data->uds_ctx;
|
||||
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->bRequest == LB_VENDOR_REQ_IN) {
|
||||
net_buf_add_mem(buf, lb_buf,
|
||||
MIN(sizeof(lb_buf), setup->wLength));
|
||||
usbd_ep_ctrl_enqueue(uds_ctx, buf);
|
||||
|
||||
LOG_WRN("Device-to-Host, wLength %u | %u", setup->wLength,
|
||||
MIN(sizeof(lb_buf), setup->wLength));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_ERR("Class request 0x%x not supported", setup->bRequest);
|
||||
errno = -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lb_control_to_dev(struct usbd_class_node *c_nd,
|
||||
const struct usb_setup_packet *const setup,
|
||||
const struct net_buf *const buf)
|
||||
{
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->bRequest == LB_VENDOR_REQ_OUT) {
|
||||
LOG_WRN("Host-to-Device, wLength %u | %u", setup->wLength,
|
||||
MIN(sizeof(lb_buf), buf->len));
|
||||
memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_ERR("Class request 0x%x not supported", setup->bRequest);
|
||||
errno = -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lb_request_handler(struct usbd_class_node *c_nd,
|
||||
struct net_buf *buf, int err)
|
||||
{
|
||||
struct udc_buf_info *bi = NULL;
|
||||
|
||||
bi = (struct udc_buf_info *)net_buf_user_data(buf);
|
||||
LOG_DBG("%p -> ep 0x%02x, len %u, err %d", c_nd, bi->ep, buf->len, err);
|
||||
usbd_ep_buf_free(c_nd->data->uds_ctx, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lb_init(struct usbd_class_node *c_nd)
|
||||
{
|
||||
LOG_DBG("Init class instance %p", c_nd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct usbd_class_api lb_api = {
|
||||
.update = lb_update,
|
||||
.control_to_host = lb_control_to_host,
|
||||
.control_to_dev = lb_control_to_dev,
|
||||
.request = lb_request_handler,
|
||||
.init = lb_init,
|
||||
};
|
||||
|
||||
#define DEFINE_LOOPBACK_CLASS_DATA(x, _) \
|
||||
static struct usbd_class_data lb_class_##x = { \
|
||||
.desc = (struct usb_desc_header *)&lb_desc_##x, \
|
||||
.v_reqs = &lb_vregs, \
|
||||
}; \
|
||||
\
|
||||
USBD_DEFINE_CLASS(loopback_##x, &lb_api, &lb_class_##x);
|
||||
|
||||
LISTIFY(CONFIG_USBD_LOOPBACK_DEVICE_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
|
||||
LISTIFY(CONFIG_USBD_LOOPBACK_DEVICE_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
|
857
subsys/usb/device_next/usbd_ch9.c
Normal file
857
subsys/usb/device_next/usbd_ch9.c
Normal file
|
@ -0,0 +1,857 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_ch9.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_class_api.h"
|
||||
#include "usbd_interface.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_ch9, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
#define CTRL_AWAIT_SETUP_DATA 0
|
||||
#define CTRL_AWAIT_STATUS_STAGE 1
|
||||
|
||||
static bool reqtype_is_to_host(const struct usb_setup_packet *const setup)
|
||||
{
|
||||
return setup->wLength && USB_REQTYPE_GET_DIR(setup->bmRequestType);
|
||||
}
|
||||
|
||||
static bool reqtype_is_to_device(const struct usb_setup_packet *const setup)
|
||||
{
|
||||
return !reqtype_is_to_host(setup);
|
||||
}
|
||||
|
||||
static void ch9_set_ctrl_type(struct usbd_contex *const uds_ctx,
|
||||
const int type)
|
||||
{
|
||||
uds_ctx->ch9_data.ctrl_type = type;
|
||||
}
|
||||
|
||||
static int ch9_get_ctrl_type(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return uds_ctx->ch9_data.ctrl_type;
|
||||
}
|
||||
|
||||
static int set_address_after_status_stage(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
int ret;
|
||||
|
||||
ret = udc_set_address(uds_ctx->dev, setup->wValue);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to set device address 0x%x", setup->wValue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uds_ctx->ch9_data.new_address = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sreq_set_address(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
|
||||
/* Not specified if wLength is non-zero, treat as error */
|
||||
if (setup->wValue > 127 || setup->wLength) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_state_is_configured(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uds_ctx->ch9_data.new_address = true;
|
||||
if (usbd_state_is_address(uds_ctx) && setup->wValue == 0) {
|
||||
uds_ctx->ch9_data.state = USBD_STATE_DEFAULT;
|
||||
} else {
|
||||
uds_ctx->ch9_data.state = USBD_STATE_ADDRESS;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_set_configuration(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
int ret;
|
||||
|
||||
LOG_INF("Set Configuration Request value %u", setup->wValue);
|
||||
|
||||
/* Not specified if wLength is non-zero, treat as error */
|
||||
if (setup->wValue > UINT8_MAX || setup->wLength) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_state_is_default(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wValue && !usbd_config_exist(uds_ctx, setup->wValue)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wValue == usbd_get_config_value(uds_ctx)) {
|
||||
LOG_DBG("Already in the configuration %u", setup->wValue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = usbd_config_set(uds_ctx, setup->wValue);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to set configuration %u, %d",
|
||||
setup->wValue, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (setup->wValue == 0) {
|
||||
/* Enter address state */
|
||||
uds_ctx->ch9_data.state = USBD_STATE_ADDRESS;
|
||||
} else {
|
||||
uds_ctx->ch9_data.state = USBD_STATE_CONFIGURED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sreq_set_interface(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t cur_alt;
|
||||
int ret;
|
||||
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_INTERFACE) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not specified if wLength is non-zero, treat as error */
|
||||
if (setup->wLength) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wValue > UINT8_MAX || setup->wIndex > UINT8_MAX) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!usbd_state_is_configured(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_get_alt_value(uds_ctx, setup->wIndex, &cur_alt)) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_INF("Set Interfaces %u, alternate %u -> %u",
|
||||
setup->wIndex, cur_alt, setup->wValue);
|
||||
|
||||
if (setup->wValue == cur_alt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = usbd_interface_set(uds_ctx, setup->wIndex, setup->wValue);
|
||||
if (ret == -ENOENT) {
|
||||
LOG_INF("Interface alternate does not exist");
|
||||
errno = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sreq_clear_feature(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t ep = setup->wIndex;
|
||||
int ret = 0;
|
||||
|
||||
/* Not specified if wLength is non-zero, treat as error */
|
||||
if (setup->wLength) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not specified in default state, treat as error */
|
||||
if (usbd_state_is_default(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_state_is_address(uds_ctx) && setup->wIndex) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (setup->RequestType.recipient) {
|
||||
case USB_REQTYPE_RECIPIENT_DEVICE:
|
||||
if (setup->wIndex != 0) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wValue == USB_SFS_REMOTE_WAKEUP) {
|
||||
LOG_DBG("Clear feature remote wakeup");
|
||||
uds_ctx->status.rwup = false;
|
||||
}
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_ENDPOINT:
|
||||
if (setup->wValue == USB_SFS_ENDPOINT_HALT) {
|
||||
/* UDC checks if endpoint is enabled */
|
||||
errno = usbd_ep_clear_halt(uds_ctx, ep);
|
||||
ret = (errno == -EPERM) ? errno : 0;
|
||||
/* TODO: notify class instance */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_INTERFACE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sreq_set_feature(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t ep = setup->wIndex;
|
||||
int ret = 0;
|
||||
|
||||
/* Not specified if wLength is non-zero, treat as error */
|
||||
if (setup->wLength) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST_MODE is not supported yet, other are not specified
|
||||
* in default state, treat as error.
|
||||
*/
|
||||
if (usbd_state_is_default(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_state_is_address(uds_ctx) && setup->wIndex) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (setup->RequestType.recipient) {
|
||||
case USB_REQTYPE_RECIPIENT_DEVICE:
|
||||
if (setup->wIndex != 0) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wValue == USB_SFS_REMOTE_WAKEUP) {
|
||||
LOG_DBG("Set feature remote wakeup");
|
||||
uds_ctx->status.rwup = true;
|
||||
}
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_ENDPOINT:
|
||||
if (setup->wValue == USB_SFS_ENDPOINT_HALT) {
|
||||
/* UDC checks if endpoint is enabled */
|
||||
errno = usbd_ep_set_halt(uds_ctx, ep);
|
||||
ret = (errno == -EPERM) ? errno : 0;
|
||||
/* TODO: notify class instance */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_INTERFACE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int std_request_to_device(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
int ret;
|
||||
|
||||
switch (setup->bRequest) {
|
||||
case USB_SREQ_SET_ADDRESS:
|
||||
ret = sreq_set_address(uds_ctx);
|
||||
break;
|
||||
case USB_SREQ_SET_CONFIGURATION:
|
||||
ret = sreq_set_configuration(uds_ctx);
|
||||
break;
|
||||
case USB_SREQ_SET_INTERFACE:
|
||||
ret = sreq_set_interface(uds_ctx);
|
||||
break;
|
||||
case USB_SREQ_CLEAR_FEATURE:
|
||||
ret = sreq_clear_feature(uds_ctx);
|
||||
break;
|
||||
case USB_SREQ_SET_FEATURE:
|
||||
ret = sreq_set_feature(uds_ctx);
|
||||
break;
|
||||
default:
|
||||
errno = -ENOTSUP;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sreq_get_status(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t ep = setup->wIndex;
|
||||
uint16_t response = 0;
|
||||
|
||||
if (setup->wLength != sizeof(response)) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not specified in default state, treat as error */
|
||||
if (usbd_state_is_default(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_state_is_address(uds_ctx) && setup->wIndex) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (setup->RequestType.recipient) {
|
||||
case USB_REQTYPE_RECIPIENT_DEVICE:
|
||||
if (setup->wIndex != 0) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
response = uds_ctx->status.rwup ?
|
||||
USB_GET_STATUS_REMOTE_WAKEUP : 0;
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_ENDPOINT:
|
||||
response = usbd_ep_is_halted(uds_ctx, ep) ? BIT(0) : 0;
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_INTERFACE:
|
||||
/* Response is always reset to zero.
|
||||
* TODO: add check if interface exist?
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (net_buf_tailroom(buf) < setup->wLength) {
|
||||
errno = -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DBG("Get Status response 0x%04x", response);
|
||||
net_buf_add_le16(buf, response);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_get_desc_cfg(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf,
|
||||
const uint8_t idx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct usb_cfg_descriptor *cfg_desc;
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usbd_class_node *c_nd;
|
||||
uint16_t len;
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, idx + 1);
|
||||
if (cfg_nd == NULL) {
|
||||
LOG_ERR("Configuration descriptor %u not found", idx + 1);
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cfg_desc = cfg_nd->desc;
|
||||
len = MIN(setup->wLength, net_buf_tailroom(buf));
|
||||
net_buf_add_mem(buf, cfg_desc, MIN(len, cfg_desc->bLength));
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
struct usb_desc_header *head = c_nd->data->desc;
|
||||
size_t desc_len = usbd_class_desc_len(c_nd);
|
||||
|
||||
len = MIN(setup->wLength, net_buf_tailroom(buf));
|
||||
net_buf_add_mem(buf, head, MIN(len, desc_len));
|
||||
}
|
||||
|
||||
LOG_DBG("Get Configuration descriptor %u, len %u", idx, buf->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_get_desc(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf,
|
||||
const uint8_t type, const uint8_t idx)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct usb_desc_header *head;
|
||||
size_t len;
|
||||
|
||||
if (type == USB_DESC_DEVICE) {
|
||||
head = uds_ctx->desc;
|
||||
} else {
|
||||
head = usbd_get_descriptor(uds_ctx, type, idx);
|
||||
}
|
||||
|
||||
if (head == NULL) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = MIN(setup->wLength, net_buf_tailroom(buf));
|
||||
net_buf_add_mem(buf, head, MIN(len, head->bLength));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_get_descriptor(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t desc_type = USB_GET_DESCRIPTOR_TYPE(setup->wValue);
|
||||
uint8_t desc_idx = USB_GET_DESCRIPTOR_INDEX(setup->wValue);
|
||||
|
||||
LOG_DBG("Get Descriptor request type %u index %u",
|
||||
desc_type, desc_idx);
|
||||
|
||||
switch (desc_type) {
|
||||
case USB_DESC_DEVICE:
|
||||
return sreq_get_desc(uds_ctx, buf, USB_DESC_DEVICE, 0);
|
||||
case USB_DESC_CONFIGURATION:
|
||||
return sreq_get_desc_cfg(uds_ctx, buf, desc_idx);
|
||||
case USB_DESC_STRING:
|
||||
return sreq_get_desc(uds_ctx, buf, USB_DESC_STRING, desc_idx);
|
||||
case USB_DESC_INTERFACE:
|
||||
case USB_DESC_ENDPOINT:
|
||||
case USB_DESC_OTHER_SPEED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_get_configuration(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
uint8_t cfg = usbd_get_config_value(uds_ctx);
|
||||
|
||||
/* Not specified in default state, treat as error */
|
||||
if (usbd_state_is_default(uds_ctx)) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setup->wLength != sizeof(cfg)) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (net_buf_tailroom(buf) < setup->wLength) {
|
||||
errno = -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
net_buf_add_u8(buf, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sreq_get_interface(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct usb_cfg_descriptor *cfg_desc;
|
||||
struct usbd_config_node *cfg_nd;
|
||||
uint8_t cur_alt;
|
||||
|
||||
if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_INTERFACE) {
|
||||
errno = -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
cfg_desc = cfg_nd->desc;
|
||||
|
||||
if (setup->wIndex > UINT8_MAX ||
|
||||
setup->wIndex > cfg_desc->bNumInterfaces) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usbd_get_alt_value(uds_ctx, setup->wIndex, &cur_alt)) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DBG("Get Interfaces %u, alternate %u",
|
||||
setup->wIndex, cur_alt);
|
||||
|
||||
if (setup->wLength != sizeof(cur_alt)) {
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (net_buf_tailroom(buf) < setup->wLength) {
|
||||
errno = -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
net_buf_add_u8(buf, cur_alt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int std_request_to_host(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
int ret;
|
||||
|
||||
switch (setup->bRequest) {
|
||||
case USB_SREQ_GET_STATUS:
|
||||
ret = sreq_get_status(uds_ctx, buf);
|
||||
break;
|
||||
case USB_SREQ_GET_DESCRIPTOR:
|
||||
ret = sreq_get_descriptor(uds_ctx, buf);
|
||||
break;
|
||||
case USB_SREQ_GET_CONFIGURATION:
|
||||
ret = sreq_get_configuration(uds_ctx, buf);
|
||||
break;
|
||||
case USB_SREQ_GET_INTERFACE:
|
||||
ret = sreq_get_interface(uds_ctx, buf);
|
||||
break;
|
||||
default:
|
||||
errno = -ENOTSUP;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nonstd_request(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const dbuf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct usbd_class_node *c_nd = NULL;
|
||||
int ret = 0;
|
||||
|
||||
switch (setup->RequestType.recipient) {
|
||||
case USB_REQTYPE_RECIPIENT_ENDPOINT:
|
||||
c_nd = usbd_class_get_by_ep(uds_ctx, setup->wIndex);
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_INTERFACE:
|
||||
c_nd = usbd_class_get_by_iface(uds_ctx, setup->wIndex);
|
||||
break;
|
||||
case USB_REQTYPE_RECIPIENT_DEVICE:
|
||||
c_nd = usbd_class_get_by_req(uds_ctx, setup->bRequest);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (c_nd != NULL) {
|
||||
if (reqtype_is_to_device(setup)) {
|
||||
ret = usbd_class_control_to_dev(c_nd, setup, dbuf);
|
||||
} else {
|
||||
ret = usbd_class_control_to_host(c_nd, setup, dbuf);
|
||||
}
|
||||
} else {
|
||||
errno = -ENOTSUP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_setup_request(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
int ret;
|
||||
|
||||
errno = 0;
|
||||
|
||||
switch (setup->RequestType.type) {
|
||||
case USB_REQTYPE_TYPE_STANDARD:
|
||||
if (reqtype_is_to_device(setup)) {
|
||||
ret = std_request_to_device(uds_ctx, buf);
|
||||
} else {
|
||||
ret = std_request_to_host(uds_ctx, buf);
|
||||
}
|
||||
break;
|
||||
case USB_REQTYPE_TYPE_CLASS:
|
||||
case USB_REQTYPE_TYPE_VENDOR:
|
||||
ret = nonstd_request(uds_ctx, buf);
|
||||
break;
|
||||
default:
|
||||
errno = -ENOTSUP;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (errno) {
|
||||
LOG_INF("protocol error:");
|
||||
LOG_HEXDUMP_INF(setup, sizeof(*setup), "setup:");
|
||||
if (errno == -ENOTSUP) {
|
||||
LOG_INF("not supported");
|
||||
}
|
||||
if (errno == -EPERM) {
|
||||
LOG_INF("not permitted in device state %u",
|
||||
uds_ctx->ch9_data.state);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ctrl_xfer_get_setup(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct net_buf *buf_b;
|
||||
struct udc_buf_info *bi, *bi_b;
|
||||
|
||||
if (buf->len < sizeof(struct usb_setup_packet)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(setup, buf->data, sizeof(struct usb_setup_packet));
|
||||
|
||||
setup->wValue = sys_le16_to_cpu(setup->wValue);
|
||||
setup->wIndex = sys_le16_to_cpu(setup->wIndex);
|
||||
setup->wLength = sys_le16_to_cpu(setup->wLength);
|
||||
|
||||
bi = udc_get_buf_info(buf);
|
||||
|
||||
buf_b = buf->frags;
|
||||
if (buf_b == NULL) {
|
||||
LOG_ERR("Buffer for data|status is missing");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
bi_b = udc_get_buf_info(buf_b);
|
||||
|
||||
if (reqtype_is_to_device(setup)) {
|
||||
if (setup->wLength) {
|
||||
if (!bi_b->data) {
|
||||
LOG_ERR("%p is not data", buf_b);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (!bi_b->status) {
|
||||
LOG_ERR("%p is not status", buf_b);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!setup->wLength) {
|
||||
LOG_ERR("device-to-host with wLength zero");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!bi_b->data) {
|
||||
LOG_ERR("%p is not data", buf_b);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_buf *spool_data_out(struct net_buf *const buf)
|
||||
{
|
||||
struct net_buf *next_buf = buf;
|
||||
struct udc_buf_info *bi;
|
||||
|
||||
while (next_buf) {
|
||||
LOG_INF("spool %p", next_buf);
|
||||
next_buf = net_buf_frag_del(NULL, next_buf);
|
||||
if (next_buf) {
|
||||
bi = udc_get_buf_info(next_buf);
|
||||
if (bi->status) {
|
||||
return next_buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usbd_handle_ctrl_xfer(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf, const int err)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
struct udc_buf_info *bi;
|
||||
int ret = 0;
|
||||
|
||||
bi = udc_get_buf_info(buf);
|
||||
if (USB_EP_GET_IDX(bi->ep)) {
|
||||
LOG_ERR("Can only handle control requests");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (err && err != -ENOMEM && !bi->setup) {
|
||||
LOG_ERR("Control transfer for 0x%02x has error %d, halt",
|
||||
bi->ep, err);
|
||||
net_buf_unref(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_INF("Handle control %p ep 0x%02x, len %u, s:%u d:%u s:%u",
|
||||
buf, bi->ep, buf->len, bi->setup, bi->data, bi->status);
|
||||
|
||||
if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) {
|
||||
struct net_buf *next_buf;
|
||||
|
||||
if (ctrl_xfer_get_setup(uds_ctx, buf)) {
|
||||
LOG_ERR("Malformed setup packet");
|
||||
net_buf_unref(buf);
|
||||
goto ctrl_xfer_stall;
|
||||
}
|
||||
|
||||
/* Remove setup packet buffer from the chain */
|
||||
next_buf = net_buf_frag_del(NULL, buf);
|
||||
if (next_buf == NULL) {
|
||||
LOG_ERR("Buffer for data|status is missing");
|
||||
goto ctrl_xfer_stall;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle request and data stage, next_buf is either
|
||||
* data+status or status buffers.
|
||||
*/
|
||||
ret = handle_setup_request(uds_ctx, next_buf);
|
||||
if (ret) {
|
||||
net_buf_unref(next_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno) {
|
||||
/*
|
||||
* Halt, only protocol errors are recoverable.
|
||||
* Free data stage and linked status stage buffer.
|
||||
*/
|
||||
net_buf_unref(next_buf);
|
||||
goto ctrl_xfer_stall;
|
||||
}
|
||||
|
||||
ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_STATUS_STAGE);
|
||||
if (reqtype_is_to_device(setup) && setup->wLength) {
|
||||
/* Enqueue STATUS (IN) buffer */
|
||||
next_buf = spool_data_out(next_buf);
|
||||
if (next_buf == NULL) {
|
||||
LOG_ERR("Buffer for status is missing");
|
||||
goto ctrl_xfer_stall;
|
||||
}
|
||||
|
||||
ret = usbd_ep_ctrl_enqueue(uds_ctx, next_buf);
|
||||
} else {
|
||||
/* Enqueue DATA (IN) or STATUS (OUT) buffer */
|
||||
ret = usbd_ep_ctrl_enqueue(uds_ctx, next_buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bi->status && bi->ep == USB_CONTROL_EP_OUT) {
|
||||
if (ch9_get_ctrl_type(uds_ctx) == CTRL_AWAIT_STATUS_STAGE) {
|
||||
LOG_INF("s-in-status finished");
|
||||
} else {
|
||||
LOG_WRN("Awaited s-in-status not finished");
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bi->status && bi->ep == USB_CONTROL_EP_IN) {
|
||||
if (ch9_get_ctrl_type(uds_ctx) == CTRL_AWAIT_STATUS_STAGE) {
|
||||
LOG_INF("s-(out)-status finished");
|
||||
if (unlikely(uds_ctx->ch9_data.new_address)) {
|
||||
return set_address_after_status_stage(uds_ctx);
|
||||
}
|
||||
} else {
|
||||
LOG_WRN("Awaited s-(out)-status not finished");
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctrl_xfer_stall:
|
||||
/*
|
||||
* Halt only the endpoint over which the host expects
|
||||
* data or status stage. This facilitates the work of the drivers.
|
||||
*
|
||||
* In the case there is -ENOMEM for data OUT stage halt
|
||||
* control OUT endpoint.
|
||||
*/
|
||||
if (reqtype_is_to_host(setup)) {
|
||||
ret = udc_ep_set_halt(uds_ctx->dev, USB_CONTROL_EP_IN);
|
||||
} else if (setup->wLength) {
|
||||
uint8_t ep = (err == -ENOMEM) ? USB_CONTROL_EP_OUT : USB_CONTROL_EP_IN;
|
||||
|
||||
ret = udc_ep_set_halt(uds_ctx->dev, ep);
|
||||
} else {
|
||||
ret = udc_ep_set_halt(uds_ctx->dev, USB_CONTROL_EP_IN);
|
||||
}
|
||||
|
||||
ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_SETUP_DATA);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_init_control_pipe(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
uds_ctx->ch9_data.state = USBD_STATE_DEFAULT;
|
||||
ch9_set_ctrl_type(uds_ctx, CTRL_AWAIT_SETUP_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
151
subsys/usb/device_next/usbd_ch9.h
Normal file
151
subsys/usb/device_next/usbd_ch9.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_CH9_H
|
||||
#define ZEPHYR_INCLUDE_USBD_CH9_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Check whether USB device is in default state.
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*
|
||||
* @return true if USB device is in default state, false otherwise
|
||||
*/
|
||||
static inline bool usbd_state_is_default(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return (uds_ctx->ch9_data.state == USBD_STATE_DEFAULT) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether USB device is in address state.
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*
|
||||
* @return true if USB device is in address state, false otherwise
|
||||
*/
|
||||
static inline bool usbd_state_is_address(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return (uds_ctx->ch9_data.state == USBD_STATE_ADDRESS) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether USB device is in configured state.
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*
|
||||
* @return true if USB device is in configured state, false otherwise
|
||||
*/
|
||||
static inline bool usbd_state_is_configured(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return (uds_ctx->ch9_data.state == USBD_STATE_CONFIGURED) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current configuration value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return current configuration value
|
||||
*/
|
||||
static inline uint8_t usbd_get_config_value(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return uds_ctx->ch9_data.configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set current configuration value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] value New configuration value
|
||||
*/
|
||||
static inline void usbd_set_config_value(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t value)
|
||||
{
|
||||
uds_ctx->ch9_data.configuration = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get interface alternate value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] iface Interface number
|
||||
* @param[out] alt Alternate value
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline uint8_t usbd_get_alt_value(const struct usbd_contex *const uds_ctx,
|
||||
const uint8_t iface,
|
||||
uint8_t *const alt)
|
||||
{
|
||||
if (iface >= USBD_NUMOF_INTERFACES_MAX) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*alt = uds_ctx->ch9_data.alternate[iface];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set interface alternate value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] iface Interface number
|
||||
* @param[out] alt Alternate value
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline uint8_t usbd_set_alt_value(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t iface,
|
||||
const uint8_t alt)
|
||||
{
|
||||
if (iface >= USBD_NUMOF_INTERFACES_MAX) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
uds_ctx->ch9_data.alternate[iface] = alt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get pointer to last received setup packet
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return Pointer to last received setup packet
|
||||
*/
|
||||
static inline struct usb_setup_packet *
|
||||
usbd_get_setup_pkt(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return &uds_ctx->ch9_data.setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle control endpoint transfer result
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] buf Pointer to UDC request buffer
|
||||
* @param[in] err Trasnfer status
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_handle_ctrl_xfer(struct usbd_contex *uds_ctx,
|
||||
struct net_buf *buf, int err);
|
||||
|
||||
/**
|
||||
* @brief Initialize control pipe to default values
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_init_control_pipe(struct usbd_contex *uds_ctx);
|
||||
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_CH9_H */
|
348
subsys/usb/device_next/usbd_class.c
Normal file
348
subsys/usb/device_next/usbd_class.c
Normal file
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
#include <zephyr/toolchain/common.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_class_api.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_endpoint.h"
|
||||
#include "usbd_ch9.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#if IS_ENABLED(CONFIG_USBD_LOG_LEVEL)
|
||||
#define USBD_CLASS_LOG_LEVEL CONFIG_USBD_LOG_LEVEL
|
||||
#else
|
||||
#define USBD_CLASS_LOG_LEVEL LOG_LEVEL_NONE
|
||||
#endif
|
||||
LOG_MODULE_REGISTER(usbd_class, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
size_t usbd_class_desc_len(struct usbd_class_node *const c_nd)
|
||||
{
|
||||
struct usbd_class_data *data = c_nd->data;
|
||||
struct usb_desc_header *dh;
|
||||
uint8_t *ptr;
|
||||
size_t len = 0;
|
||||
|
||||
if (data->desc != NULL) {
|
||||
dh = data->desc;
|
||||
ptr = (uint8_t *)dh;
|
||||
|
||||
while (dh->bLength != 0) {
|
||||
len += dh->bLength;
|
||||
ptr += dh->bLength;
|
||||
dh = (struct usb_desc_header *)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
struct usbd_class_node *
|
||||
usbd_class_get_by_config(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cnum,
|
||||
const uint8_t inum)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cnum);
|
||||
if (cfg_nd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (c_nd->data->iface_bm & BIT(inum)) {
|
||||
return c_nd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct usbd_class_node *
|
||||
usbd_class_get_by_iface(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t inum)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
if (cfg_nd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (c_nd->data->iface_bm & BIT(inum)) {
|
||||
return c_nd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool xfer_owner_exist(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
||||
struct usbd_class_node *c_nd;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (bi->owner == c_nd) {
|
||||
uint32_t ep_active = c_nd->data->ep_active;
|
||||
uint32_t ep_assigned = c_nd->data->ep_assigned;
|
||||
|
||||
if (!usbd_ep_bm_is_set(&ep_active, bi->ep)) {
|
||||
LOG_DBG("ep 0x%02x is not active", bi->ep);
|
||||
}
|
||||
|
||||
if (!usbd_ep_bm_is_set(&ep_assigned, bi->ep)) {
|
||||
LOG_DBG("ep 0x%02x is not assigned", bi->ep);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int usbd_class_handle_xfer(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf,
|
||||
const int err)
|
||||
{
|
||||
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
||||
|
||||
if (unlikely(USBD_CLASS_LOG_LEVEL == LOG_LEVEL_DBG)) {
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
if (usbd_state_is_configured(uds_ctx)) {
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
if (xfer_owner_exist(uds_ctx, cfg_nd, buf)) {
|
||||
return usbd_class_request(bi->owner, buf, err);
|
||||
}
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
|
||||
if (xfer_owner_exist(uds_ctx, cfg_nd, buf)) {
|
||||
return usbd_class_request(bi->owner, buf, err);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return usbd_class_request(bi->owner, buf, err);
|
||||
}
|
||||
|
||||
struct usbd_class_node *
|
||||
usbd_class_get_by_ep(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t ep)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
struct usbd_config_node *cfg_nd;
|
||||
uint8_t ep_idx = USB_EP_GET_IDX(ep);
|
||||
uint8_t cfg;
|
||||
uint32_t ep_bm;
|
||||
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
ep_bm = BIT(ep_idx + 16);
|
||||
} else {
|
||||
ep_bm = BIT(ep_idx);
|
||||
}
|
||||
|
||||
if (!usbd_state_is_configured(uds_ctx)) {
|
||||
LOG_ERR("No configuration set (Address state)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cfg = usbd_get_config_value(uds_ctx);
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (c_nd->data->ep_assigned & ep_bm) {
|
||||
return c_nd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct usbd_class_node *
|
||||
usbd_class_get_by_req(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t request)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usbd_class_node *c_nd;
|
||||
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
if (cfg_nd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (c_nd->data->v_reqs == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < c_nd->data->v_reqs->len; i++) {
|
||||
/*
|
||||
* First instance always wins.
|
||||
* There is no other way to determine the recipient.
|
||||
*/
|
||||
if (c_nd->data->v_reqs->reqs[i] == request) {
|
||||
return c_nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct usbd_class_node *usbd_class_node_get(const char *name)
|
||||
{
|
||||
STRUCT_SECTION_FOREACH(usbd_class_node, c_nd) {
|
||||
if (strcmp(name, c_nd->name) == 0) {
|
||||
return c_nd;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERR("USB device class %s not found", name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int usbd_class_append(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_class_node *const c_nd,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
sys_slist_append(&cfg_nd->class_list, &c_nd->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbd_class_remove(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_class_node *const c_nd,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (!sys_slist_find_and_remove(&cfg_nd->class_list, &c_nd->node)) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* All the functions below are part of public USB device support API.
|
||||
*/
|
||||
|
||||
int usbd_register_class(struct usbd_contex *const uds_ctx,
|
||||
const char *name,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
struct usbd_class_data *data;
|
||||
int ret;
|
||||
|
||||
c_nd = usbd_class_node_get(name);
|
||||
if (c_nd == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_initialized(uds_ctx)) {
|
||||
LOG_ERR("USB device support is initialized");
|
||||
ret = -EBUSY;
|
||||
goto register_class_error;
|
||||
}
|
||||
|
||||
data = c_nd->data;
|
||||
if (data->desc == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto register_class_error;
|
||||
}
|
||||
|
||||
|
||||
/* TODO: does it still need to be atomic ? */
|
||||
if (atomic_test_bit(&data->state, USBD_CCTX_REGISTERED)) {
|
||||
LOG_WRN("Class instance allready registered");
|
||||
ret = -EBUSY;
|
||||
goto register_class_error;
|
||||
}
|
||||
|
||||
ret = usbd_class_append(uds_ctx, c_nd, cfg);
|
||||
if (ret == 0) {
|
||||
/* Initialize pointer back to the device struct */
|
||||
atomic_set_bit(&data->state, USBD_CCTX_REGISTERED);
|
||||
data->uds_ctx = uds_ctx;
|
||||
}
|
||||
|
||||
register_class_error:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_unregister_class(struct usbd_contex *const uds_ctx,
|
||||
const char *name,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
struct usbd_class_data *data;
|
||||
int ret;
|
||||
|
||||
c_nd = usbd_class_node_get(name);
|
||||
if (c_nd == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_initialized(uds_ctx)) {
|
||||
LOG_ERR("USB device support is initialized");
|
||||
ret = -EBUSY;
|
||||
goto unregister_class_error;
|
||||
}
|
||||
|
||||
data = c_nd->data;
|
||||
/* TODO: does it still need to be atomic ? */
|
||||
if (!atomic_test_bit(&data->state, USBD_CCTX_REGISTERED)) {
|
||||
LOG_WRN("Class instance not registered");
|
||||
ret = -EBUSY;
|
||||
goto unregister_class_error;
|
||||
}
|
||||
|
||||
ret = usbd_class_remove(uds_ctx, c_nd, cfg);
|
||||
if (ret == 0) {
|
||||
atomic_clear_bit(&data->state, USBD_CCTX_REGISTERED);
|
||||
data->uds_ctx = NULL;
|
||||
}
|
||||
|
||||
unregister_class_error:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
96
subsys/usb/device_next/usbd_class.h
Normal file
96
subsys/usb/device_next/usbd_class.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_CLASS_H
|
||||
#define ZEPHYR_INCLUDE_USBD_CLASS_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Handle endpoint transfer result
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to device context
|
||||
* @param[in] buf Pointer to UDC request buffer
|
||||
* @param[in] err Trasnfer status
|
||||
*
|
||||
* @return usbd_class_request() return value
|
||||
*/
|
||||
int usbd_class_handle_xfer(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf,
|
||||
const int err);
|
||||
|
||||
/**
|
||||
* @brief Calculate the length of the class descriptor
|
||||
*
|
||||
* Calculate the length of the class instance descriptor.
|
||||
* The descriptor must be terminated by a usb_desc_header structure
|
||||
* set to {bLength = 0, bDescriptorType = 0,}.
|
||||
* Calculated length does not include any string descriptors that may be
|
||||
* used by the class instance.
|
||||
*
|
||||
* @param[in] node Pointer to a class node
|
||||
*
|
||||
* @return Length of the class descriptor
|
||||
*/
|
||||
size_t usbd_class_desc_len(struct usbd_class_node *node);
|
||||
|
||||
/**
|
||||
* @brief Get class context by bInterfaceNumber value
|
||||
*
|
||||
* The function searches the class instance list for the interface number.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to device context
|
||||
* @param[in] inum Interface number
|
||||
*
|
||||
* @return Class node pointer or NULL
|
||||
*/
|
||||
struct usbd_class_node *usbd_class_get_by_iface(struct usbd_contex *uds_ctx,
|
||||
uint8_t i_n);
|
||||
|
||||
/**
|
||||
* @brief Get class context by configuration and interface number
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to device context
|
||||
* @param[in] cnum Configuration number
|
||||
* @param[in] inum Interface number
|
||||
*
|
||||
* @return Class node pointer or NULL
|
||||
*/
|
||||
struct usbd_class_node *usbd_class_get_by_config(struct usbd_contex *uds_ctx,
|
||||
uint8_t cnum,
|
||||
uint8_t inum);
|
||||
|
||||
/**
|
||||
* @brief Get class context by endpoint address
|
||||
*
|
||||
* The function searches the class instance list for the endpoint address.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to device context
|
||||
* @param[in] ep Endpoint address
|
||||
*
|
||||
* @return Class node pointer or NULL
|
||||
*/
|
||||
struct usbd_class_node *usbd_class_get_by_ep(struct usbd_contex *uds_ctx,
|
||||
uint8_t ep);
|
||||
|
||||
/**
|
||||
* @brief Get class context by request (bRequest)
|
||||
*
|
||||
* The function searches the class instance list and
|
||||
* compares the vendor request table with request value.
|
||||
* The function is only used if the request type is Vendor and
|
||||
* request recipient is Device.
|
||||
* Accordingly only the first class instance can be found.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to device context
|
||||
* @param[in] request bRequest value
|
||||
*
|
||||
* @return Class node pointer or NULL
|
||||
*/
|
||||
struct usbd_class_node *usbd_class_get_by_req(struct usbd_contex *uds_ctx,
|
||||
uint8_t request);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_CLASS_H */
|
226
subsys/usb/device_next/usbd_class_api.h
Normal file
226
subsys/usb/device_next/usbd_class_api.h
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief USB device stack class instances API
|
||||
*
|
||||
* This file contains the USB device stack class instances API.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_CLASS_API_H
|
||||
#define ZEPHYR_INCLUDE_USBD_CLASS_API_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Endpoint request completion event handler
|
||||
*
|
||||
* This is the event handler for all endpoint accommodated
|
||||
* by a class instance.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] buf Control Request Data buffer
|
||||
* @param[in] err Result of the transfer. 0 if the transfer was successful.
|
||||
*/
|
||||
static inline int usbd_class_request(struct usbd_class_node *const node,
|
||||
struct net_buf *const buf,
|
||||
int err)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->request != NULL) {
|
||||
return api->request(node, buf, err);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief USB control request handler
|
||||
*
|
||||
* Common handler for all control request.
|
||||
* Regardless requests recipient, interface or endpoint,
|
||||
* the USB device core will identify proper class instance
|
||||
* and call this handler.
|
||||
* For the vendor type request USBD_VENDOR_REQ macro must be used
|
||||
* to identify the class, if more than one class instance is
|
||||
* present, only the first one will be called.
|
||||
*
|
||||
* The execution of the handler must not block.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] setup Pointer to USB Setup Packet
|
||||
* @param[in] buf Control Request Data buffer
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline int usbd_class_control_to_host(struct usbd_class_node *const node,
|
||||
struct usb_setup_packet *const setup,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->control_to_host != NULL) {
|
||||
return api->control_to_host(node, setup, buf);
|
||||
}
|
||||
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB control request handler
|
||||
*
|
||||
* Common handler for all control request.
|
||||
* Regardless requests recipient, interface or endpoint,
|
||||
* the USB device core will identify proper class instance
|
||||
* and call this handler.
|
||||
* For the vendor type request USBD_VENDOR_REQ macro must be used
|
||||
* to identify the class, if more than one class instance is
|
||||
* present, only the first one will be called.
|
||||
*
|
||||
* The execution of the handler must not block.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] setup Pointer to USB Setup Packet
|
||||
* @param[in] buf Control Request Data buffer
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline int usbd_class_control_to_dev(struct usbd_class_node *const node,
|
||||
struct usb_setup_packet *const setup,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->control_to_dev != NULL) {
|
||||
return api->control_to_dev(node, setup, buf);
|
||||
}
|
||||
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configuration update handler
|
||||
*
|
||||
* Called when the configuration of the interface belonging
|
||||
* to the instance has been changed, either because of
|
||||
* Set Configuration or Set Interface request.
|
||||
*
|
||||
* The execution of the handler must not block.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] setup Pointer to USB setup packet
|
||||
*/
|
||||
static inline void usbd_class_update(struct usbd_class_node *const node,
|
||||
const uint8_t iface,
|
||||
const uint8_t alternate)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->update != NULL) {
|
||||
api->update(node, iface, alternate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB suspended handler
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] event Power management event
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline void usbd_class_suspended(struct usbd_class_node *const node)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->suspended != NULL) {
|
||||
api->suspended(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief USB resumed handler
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
* @param[in] event Power management event
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline void usbd_class_resumed(struct usbd_class_node *const node)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->resumed != NULL) {
|
||||
api->resumed(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Class associated configuration activ handler
|
||||
*
|
||||
* @note The execution of the handler must not block.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
*/
|
||||
static inline void usbd_class_enable(struct usbd_class_node *const node)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->enable != NULL) {
|
||||
api->enable(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Class associated configuration shutdown handler
|
||||
*
|
||||
* @note The execution of the handler must not block.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
*/
|
||||
static inline void usbd_class_disable(struct usbd_class_node *const node)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->disable != NULL) {
|
||||
api->disable(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialization of the class implementation
|
||||
*
|
||||
* This is called for each instance during the initialization phase
|
||||
* after the interface number and endpoint addresses are assigned
|
||||
* to the corresponding instance.
|
||||
* It can be used to initialize class specific descriptors or
|
||||
* underlying systems.
|
||||
*
|
||||
* @note If this call fails the core will terminate stack initialization.
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the class instance
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static inline int usbd_class_init(struct usbd_class_node *const node)
|
||||
{
|
||||
const struct usbd_class_api *api = node->api;
|
||||
|
||||
if (api->init != NULL) {
|
||||
return api->init(node);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_CLASS_API_H */
|
271
subsys/usb/device_next/usbd_config.c
Normal file
271
subsys/usb/device_next/usbd_config.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_interface.h"
|
||||
#include "usbd_ch9.h"
|
||||
#include "usbd_class_api.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_cfg, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
struct usbd_config_node *usbd_config_get(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
|
||||
if (usbd_config_get_value(cfg_nd) == cfg) {
|
||||
return cfg_nd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct usbd_config_node *
|
||||
usbd_config_get_current(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
if (!usbd_state_is_configured(uds_ctx)) {
|
||||
LOG_INF("No configuration set (Address state?)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return usbd_config_get(uds_ctx, usbd_get_config_value(uds_ctx));
|
||||
}
|
||||
|
||||
static void usbd_config_classes_enable(struct usbd_config_node *const cfg_nd,
|
||||
const bool enable)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
if (enable) {
|
||||
usbd_class_enable(c_nd);
|
||||
} else {
|
||||
usbd_class_disable(c_nd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset configuration to addressed state, shutdown all endpoints */
|
||||
static int usbd_config_reset(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
int ret = 0;
|
||||
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
if (cfg_nd == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = usbd_interface_shutdown(uds_ctx, cfg_nd);
|
||||
|
||||
memset(&uds_ctx->ch9_data.alternate, 0,
|
||||
USBD_NUMOF_INTERFACES_MAX);
|
||||
|
||||
usbd_set_config_value(uds_ctx, 0);
|
||||
usbd_config_classes_enable(cfg_nd, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool usbd_config_exist(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg)
|
||||
{
|
||||
struct usbd_config_node *config;
|
||||
|
||||
config = usbd_config_get(uds_ctx, cfg);
|
||||
|
||||
return (config != NULL) ? true : false;
|
||||
}
|
||||
|
||||
int usbd_config_set(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t new_cfg)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
int ret;
|
||||
|
||||
if (usbd_get_config_value(uds_ctx) != 0) {
|
||||
ret = usbd_config_reset(uds_ctx);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to reset configuration");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_cfg == 0) {
|
||||
usbd_set_config_value(uds_ctx, new_cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, new_cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = usbd_interface_default(uds_ctx, cfg_nd);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_set_config_value(uds_ctx, new_cfg);
|
||||
usbd_config_classes_enable(cfg_nd, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* All the functions below are part of public USB device support API.
|
||||
*/
|
||||
|
||||
int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg, const bool enable)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usb_cfg_descriptor *desc;
|
||||
struct udc_device_caps caps;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto attrib_rwup_exit;
|
||||
}
|
||||
|
||||
caps = udc_caps(uds_ctx->dev);
|
||||
if (!caps.rwup) {
|
||||
LOG_ERR("Feature not supported by controller");
|
||||
ret = -ENOTSUP;
|
||||
goto attrib_rwup_exit;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
LOG_INF("Configuration %u not found", cfg);
|
||||
ret = -ENODATA;
|
||||
goto attrib_rwup_exit;
|
||||
}
|
||||
|
||||
desc = cfg_nd->desc;
|
||||
if (enable) {
|
||||
desc->bmAttributes |= USB_SCD_REMOTE_WAKEUP;
|
||||
} else {
|
||||
desc->bmAttributes &= ~USB_SCD_REMOTE_WAKEUP;
|
||||
}
|
||||
|
||||
attrib_rwup_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg, const bool enable)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usb_cfg_descriptor *desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto attrib_self_exit;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
LOG_INF("Configuration %u not found", cfg);
|
||||
ret = -ENODATA;
|
||||
goto attrib_self_exit;
|
||||
}
|
||||
|
||||
desc = cfg_nd->desc;
|
||||
if (enable) {
|
||||
desc->bmAttributes |= USB_SCD_SELF_POWERED;
|
||||
} else {
|
||||
desc->bmAttributes &= ~USB_SCD_SELF_POWERED;
|
||||
}
|
||||
|
||||
attrib_self_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg, const uint8_t power)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usb_cfg_descriptor *desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto maxpower_exit;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get(uds_ctx, cfg);
|
||||
if (cfg_nd == NULL) {
|
||||
LOG_INF("Configuration %u not found", cfg);
|
||||
ret = -ENODATA;
|
||||
goto maxpower_exit;
|
||||
}
|
||||
|
||||
desc = cfg_nd->desc;
|
||||
desc->bMaxPower = power;
|
||||
|
||||
maxpower_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_add_configuration(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd)
|
||||
{
|
||||
struct usb_cfg_descriptor *desc = cfg_nd->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_initialized(uds_ctx)) {
|
||||
LOG_ERR("USB device support is initialized");
|
||||
ret = -EBUSY;
|
||||
goto add_configuration_exit;
|
||||
}
|
||||
|
||||
if (desc->bmAttributes & USB_SCD_REMOTE_WAKEUP) {
|
||||
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
|
||||
|
||||
if (!caps.rwup) {
|
||||
LOG_ERR("Feature not supported by controller");
|
||||
ret = -ENOTSUP;
|
||||
goto add_configuration_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (sys_slist_find_and_remove(&uds_ctx->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);
|
||||
}
|
||||
|
||||
sys_slist_append(&uds_ctx->configs, &cfg_nd->node);
|
||||
|
||||
usbd_device_unlock(uds_ctx);
|
||||
|
||||
add_configuration_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
88
subsys/usb/device_next/usbd_config.h
Normal file
88
subsys/usb/device_next/usbd_config.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_CONFIG_H
|
||||
#define ZEPHYR_INCLUDE_USBD_CONFIG_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Get configuration descriptor bConfigurationValue value
|
||||
*
|
||||
* @param[in] cfg_nd Pointer to a configuration node structure
|
||||
*
|
||||
* @return bConfigurationValue value
|
||||
*/
|
||||
static inline uint8_t usbd_config_get_value(const struct usbd_config_node *const cfg_nd)
|
||||
{
|
||||
struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
|
||||
|
||||
return cfg_desc->bConfigurationValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set configuration descriptor bConfigurationValue value
|
||||
*
|
||||
* @param[in] cfg_nd Pointer to a configuration node structure
|
||||
*/
|
||||
static inline void usbd_config_set_value(const struct usbd_config_node *const cfg_nd,
|
||||
const uint8_t value)
|
||||
{
|
||||
struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
|
||||
|
||||
cfg_desc->bConfigurationValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get configuration node
|
||||
*
|
||||
* Get configuration node with desired configuration number.
|
||||
*
|
||||
* @param[in] ctx Pointer to USB device support context
|
||||
* @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,
|
||||
uint8_t cfg);
|
||||
|
||||
/**
|
||||
* @brief Get selected configuration node
|
||||
*
|
||||
* Get configuration node based on configuration selected by the host.
|
||||
*
|
||||
* @param[in] ctx Pointer to USB device support context
|
||||
*
|
||||
* @return pointer to configuration node or NULL if does not exist
|
||||
*/
|
||||
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] cfg Configuration number (bConfigurationValue)
|
||||
*
|
||||
* @return True if a configuration exist.
|
||||
*/
|
||||
bool usbd_config_exist(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t cfg);
|
||||
|
||||
/**
|
||||
* @brief Setup new USB device configuration
|
||||
*
|
||||
* This function disables all active endpoints of current configuration
|
||||
* and enables all interface alternate 0 endpoints of a new configuration.
|
||||
* Determined to be called Set Configuration request.
|
||||
*
|
||||
* @param[in] ctx Pointer to USB device support context
|
||||
* @param[in] new_cfg New configuration number (bConfigurationValue)
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_config_set(struct usbd_contex *uds_ctx, uint8_t new_cfg);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_CONFIG_H */
|
221
subsys/usb/device_next/usbd_core.c
Normal file
221
subsys/usb/device_next/usbd_core.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/toolchain/common.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_init.h"
|
||||
#include "usbd_ch9.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_class_api.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_core, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
static K_KERNEL_STACK_DEFINE(usbd_stack, CONFIG_USBD_THREAD_STACK_SIZE);
|
||||
static struct k_thread usbd_thread_data;
|
||||
|
||||
K_MSGQ_DEFINE(usbd_msgq, sizeof(struct udc_event),
|
||||
CONFIG_USBD_MAX_UDC_MSG, sizeof(uint32_t));
|
||||
|
||||
static int usbd_event_carrier(const struct device *dev,
|
||||
const struct udc_event *const event)
|
||||
{
|
||||
return k_msgq_put(&usbd_msgq, event, K_NO_WAIT);
|
||||
}
|
||||
|
||||
static int event_handler_ep_request(struct usbd_contex *const uds_ctx,
|
||||
const struct udc_event *const event)
|
||||
{
|
||||
struct udc_buf_info *bi;
|
||||
int ret;
|
||||
|
||||
bi = udc_get_buf_info(event->buf);
|
||||
|
||||
if (USB_EP_GET_IDX(bi->ep) == 0) {
|
||||
ret = usbd_handle_ctrl_xfer(uds_ctx, event->buf,
|
||||
event->status);
|
||||
} else {
|
||||
ret = usbd_class_handle_xfer(uds_ctx, event->buf,
|
||||
event->status);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("unrecoverable error %d, ep 0x%02x, buf %p",
|
||||
ret, bi->ep, event->buf);
|
||||
/* TODO: Shutdown USB device gracefully */
|
||||
k_panic();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usbd_class_bcast_event(struct usbd_contex *const uds_ctx,
|
||||
struct udc_event *const event)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
struct usbd_class_node *c_nd;
|
||||
|
||||
if (!usbd_state_is_configured(uds_ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cfg_nd = usbd_config_get_current(uds_ctx);
|
||||
if (cfg_nd == NULL) {
|
||||
LOG_ERR("Failed to get cfg_nd, despite configured state");
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
switch (event->type) {
|
||||
case UDC_EVT_SUSPEND:
|
||||
usbd_class_suspended(c_nd);
|
||||
break;
|
||||
case UDC_EVT_RESUME:
|
||||
usbd_class_resumed(c_nd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int event_handler_bus_reset(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
LOG_WRN("Bus reset event");
|
||||
|
||||
usbd_status_suspended(uds_ctx, false);
|
||||
ret = udc_set_address(uds_ctx->dev, 0);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to set default address after bus reset");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usbd_config_set(uds_ctx, 0);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to set default state after bus reset");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("Actual device speed %d", udc_device_speed(uds_ctx->dev));
|
||||
uds_ctx->ch9_data.state = USBD_STATE_DEFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Add event broadcaster to user application */
|
||||
static int ALWAYS_INLINE usbd_event_handler(struct usbd_contex *const uds_ctx,
|
||||
struct udc_event *const event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event->type) {
|
||||
case UDC_EVT_VBUS_REMOVED:
|
||||
LOG_WRN("VBUS remove event");
|
||||
break;
|
||||
case UDC_EVT_VBUS_READY:
|
||||
LOG_WRN("VBUS detected event");
|
||||
break;
|
||||
case UDC_EVT_SUSPEND:
|
||||
LOG_WRN("SUSPEND event");
|
||||
usbd_status_suspended(uds_ctx, true);
|
||||
usbd_class_bcast_event(uds_ctx, event);
|
||||
break;
|
||||
case UDC_EVT_RESUME:
|
||||
LOG_WRN("RESUME event");
|
||||
usbd_status_suspended(uds_ctx, false);
|
||||
usbd_class_bcast_event(uds_ctx, event);
|
||||
break;
|
||||
case UDC_EVT_SOF:
|
||||
usbd_class_bcast_event(uds_ctx, event);
|
||||
break;
|
||||
case UDC_EVT_RESET:
|
||||
LOG_WRN("RESET event");
|
||||
ret = event_handler_bus_reset(uds_ctx);
|
||||
break;
|
||||
case UDC_EVT_EP_REQUEST:
|
||||
ret = event_handler_ep_request(uds_ctx, event);
|
||||
break;
|
||||
case UDC_EVT_ERROR:
|
||||
LOG_ERR("Error event");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usbd_thread(void)
|
||||
{
|
||||
struct udc_event event;
|
||||
|
||||
while (true) {
|
||||
k_msgq_get(&usbd_msgq, &event, K_FOREVER);
|
||||
|
||||
STRUCT_SECTION_FOREACH(usbd_contex, uds_ctx) {
|
||||
if (uds_ctx->dev == event.dev) {
|
||||
usbd_event_handler(uds_ctx, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int usbd_device_init_core(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = udc_init(uds_ctx->dev, usbd_event_carrier);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to init device driver");
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_set_config_value(uds_ctx, 0);
|
||||
|
||||
ret = usbd_init_configurations(uds_ctx);
|
||||
if (ret != 0) {
|
||||
udc_shutdown(uds_ctx->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_shutdown_core(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return udc_shutdown(uds_ctx->dev);
|
||||
}
|
||||
|
||||
static int usbd_pre_init(const struct device *unused)
|
||||
{
|
||||
k_thread_create(&usbd_thread_data, usbd_stack,
|
||||
K_KERNEL_STACK_SIZEOF(usbd_stack),
|
||||
(k_thread_entry_t)usbd_thread,
|
||||
NULL, NULL, NULL,
|
||||
K_PRIO_COOP(8), 0, K_NO_WAIT);
|
||||
|
||||
k_thread_name_set(&usbd_thread_data, "usbd");
|
||||
|
||||
LOG_DBG("Available USB class nodes:");
|
||||
STRUCT_SECTION_FOREACH(usbd_class_node, node) {
|
||||
atomic_set(&node->data->state, 0);
|
||||
LOG_DBG("\t%p, name %s", node, node->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(usbd_pre_init, POST_KERNEL, CONFIG_USBD_THREAD_INIT_PRIO);
|
2
subsys/usb/device_next/usbd_data.ld
Normal file
2
subsys/usb/device_next/usbd_data.ld
Normal file
|
@ -0,0 +1,2 @@
|
|||
ITERABLE_SECTION_RAM(usbd_contex, 4)
|
||||
ITERABLE_SECTION_RAM(usbd_class_node, 4)
|
171
subsys/usb/device_next/usbd_desc.c
Normal file
171
subsys/usb/device_next/usbd_desc.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2017 PHYTEC Messtechnik GmbH
|
||||
* Copyright (c) 2017, 2018 Intel Corporation
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
#include <zephyr/drivers/hwinfo.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_desc, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* The last index of the initializer_string without null character is:
|
||||
* ascii_idx_max = bLength / 2 - 2
|
||||
* Use this macro to determine the last index of ASCII7 string.
|
||||
*/
|
||||
#define USB_BSTRING_ASCII_IDX_MAX(n) (n / 2 - 2)
|
||||
|
||||
/*
|
||||
* The last index of the bString is:
|
||||
* utf16le_idx_max = sizeof(initializer_string) * 2 - 2 - 1
|
||||
* utf16le_idx_max = bLength - 2 - 1
|
||||
* Use this macro to determine the last index of UTF16LE string.
|
||||
*/
|
||||
#define USB_BSTRING_UTF16LE_IDX_MAX(n) (n - 3)
|
||||
|
||||
/**
|
||||
* @brief Transform ASCII-7 string descriptor to UTF16-LE
|
||||
*
|
||||
* This function transforms ASCII-7 string descriptor
|
||||
* into a UTF16-LE.
|
||||
*
|
||||
* @param[in] dn Pointer to descriptor node
|
||||
*/
|
||||
static void usbd_ascii7_to_utf16le(struct usbd_desc_node *const dn)
|
||||
{
|
||||
struct usb_string_descriptor *desc = dn->desc;
|
||||
int idx_max = USB_BSTRING_UTF16LE_IDX_MAX(desc->bLength);
|
||||
int ascii_idx_max = USB_BSTRING_ASCII_IDX_MAX(desc->bLength);
|
||||
uint8_t *buf = (uint8_t *)&desc->bString;
|
||||
|
||||
LOG_DBG("idx_max %d, ascii_idx_max %d, buf %p",
|
||||
idx_max, ascii_idx_max, buf);
|
||||
|
||||
for (int i = idx_max; i >= 0; i -= 2) {
|
||||
LOG_DBG("char %c : %x, idx %d -> %d",
|
||||
buf[ascii_idx_max],
|
||||
buf[ascii_idx_max],
|
||||
ascii_idx_max, i);
|
||||
__ASSERT(buf[ascii_idx_max] > 0x1F && buf[ascii_idx_max] < 0x7F,
|
||||
"Only printable ascii-7 characters are allowed in USB "
|
||||
"string descriptors");
|
||||
buf[i] = 0U;
|
||||
buf[i - 1] = buf[ascii_idx_max--];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get common USB descriptor
|
||||
*
|
||||
* Get descriptor from internal descrptor list.
|
||||
*
|
||||
* @param[in] dn Pointer to descriptor node
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
static int usbd_get_sn_from_hwid(struct usbd_desc_node *const dn)
|
||||
{
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
struct usb_string_descriptor *desc = dn->desc;
|
||||
uint8_t *desc_data = (uint8_t *)&desc->bString;
|
||||
uint8_t hwid[16];
|
||||
ssize_t hwid_len;
|
||||
ssize_t min_len;
|
||||
|
||||
hwid_len = hwinfo_get_device_id(hwid, sizeof(hwid));
|
||||
if (hwid_len < 0) {
|
||||
if (hwid_len == -ENOSYS) {
|
||||
LOG_WRN("hwinfo not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hwid_len;
|
||||
}
|
||||
|
||||
min_len = MIN(hwid_len, desc->bLength / 2);
|
||||
for (size_t i = 0; i < min_len; i++) {
|
||||
desc_data[i * 2] = hex[hwid[i] >> 4];
|
||||
desc_data[i * 2 + 1] = hex[hwid[i] & 0xF];
|
||||
}
|
||||
|
||||
LOG_HEXDUMP_DBG(&desc->bString, desc->bLength, "SerialNumber");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *usbd_get_descriptor(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t type, const uint8_t idx)
|
||||
{
|
||||
struct usbd_desc_node *tmp;
|
||||
struct usb_desc_header *dh;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->descriptors, tmp, node) {
|
||||
dh = tmp->desc;
|
||||
if (tmp->idx == idx && dh->bDescriptorType == type) {
|
||||
return tmp->desc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usbd_add_descriptor(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_desc_node *const desc_nd)
|
||||
{
|
||||
struct usb_desc_header *head;
|
||||
uint8_t type;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
head = desc_nd->desc;
|
||||
type = head->bDescriptorType;
|
||||
if (usbd_get_descriptor(uds_ctx, type, desc_nd->idx)) {
|
||||
ret = -EALREADY;
|
||||
goto add_descriptor_error;
|
||||
}
|
||||
|
||||
if (type == USB_DESC_STRING) {
|
||||
struct usb_device_descriptor *dev_desc = uds_ctx->desc;
|
||||
|
||||
if (dev_desc == NULL) {
|
||||
ret = -EPERM;
|
||||
goto add_descriptor_error;
|
||||
}
|
||||
|
||||
switch (desc_nd->idx) {
|
||||
case USBD_DESC_MANUFACTURER_IDX:
|
||||
dev_desc->iManufacturer = desc_nd->idx;
|
||||
break;
|
||||
case USBD_DESC_PRODUCT_IDX:
|
||||
dev_desc->iProduct = desc_nd->idx;
|
||||
break;
|
||||
case USBD_DESC_SERIAL_NUMBER_IDX:
|
||||
/* FIXME, should we force the use of hwid here? */
|
||||
ret = usbd_get_sn_from_hwid(desc_nd);
|
||||
dev_desc->iSerialNumber = desc_nd->idx;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (desc_nd->idx) {
|
||||
/* FIXME, should we force ascii7 -> utf16le? */
|
||||
usbd_ascii7_to_utf16le(desc_nd);
|
||||
}
|
||||
}
|
||||
|
||||
sys_slist_append(&uds_ctx->descriptors, &desc_nd->node);
|
||||
|
||||
add_descriptor_error:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
26
subsys/usb/device_next/usbd_desc.h
Normal file
26
subsys/usb/device_next/usbd_desc.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_DESC_H
|
||||
#define ZEPHYR_INCLUDE_USBD_DESC_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Get common USB descriptor
|
||||
*
|
||||
* Get descriptor from internal descrptor list.
|
||||
*
|
||||
* @param[in] ctx Pointer to USB device support context
|
||||
* @param[in] type Descriptor type (bDescriptorType)
|
||||
* @param[in] idx Descriptor index
|
||||
*
|
||||
* @return pointer to descriptor or NULL if not found.
|
||||
*/
|
||||
void *usbd_get_descriptor(struct usbd_contex *uds_ctx,
|
||||
const uint8_t type, const uint8_t idx);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_DESC_H */
|
292
subsys/usb/device_next/usbd_device.c
Normal file
292
subsys/usb/device_next/usbd_device.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_ch9.h"
|
||||
#include "usbd_desc.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_dev, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* All the functions below are part of public USB device support API.
|
||||
*/
|
||||
|
||||
int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
|
||||
const uint16_t bcd)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_bcd_exit;
|
||||
}
|
||||
|
||||
desc->bcdUSB = sys_cpu_to_le16(bcd);
|
||||
|
||||
set_bcd_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
|
||||
const uint16_t vid)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_vid_exit;
|
||||
}
|
||||
|
||||
desc->idVendor = sys_cpu_to_le16(vid);
|
||||
|
||||
set_vid_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
|
||||
const uint16_t pid)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_pid_exit;
|
||||
}
|
||||
|
||||
desc->idProduct = sys_cpu_to_le16(pid);
|
||||
|
||||
set_pid_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_set_class(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t value)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_class_exit;
|
||||
}
|
||||
|
||||
desc->bDeviceClass = value;
|
||||
|
||||
set_class_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_set_subclass(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t value)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_subclass_exit;
|
||||
}
|
||||
|
||||
desc->bDeviceSubClass = value;
|
||||
|
||||
set_subclass_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_device_set_proto(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t value)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
ret = -EALREADY;
|
||||
goto set_proto_exit;
|
||||
}
|
||||
|
||||
desc->bDeviceProtocol = value;
|
||||
|
||||
set_proto_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_wakeup_request(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
|
||||
int ret = 0;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (!caps.rwup) {
|
||||
LOG_ERR("Remote wakeup feature not supported");
|
||||
ret = -ENOTSUP;
|
||||
goto wakeup_request_error;
|
||||
}
|
||||
|
||||
if (!uds_ctx->status.rwup || !usbd_is_suspended(uds_ctx)) {
|
||||
LOG_ERR("Remote wakeup feature not enabled or not suspended");
|
||||
ret = -EACCES;
|
||||
goto wakeup_request_error;
|
||||
}
|
||||
|
||||
ret = udc_host_wakeup(uds_ctx->dev);
|
||||
|
||||
wakeup_request_error:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool usbd_is_suspended(struct usbd_contex *uds_ctx)
|
||||
{
|
||||
return uds_ctx->status.suspended;
|
||||
}
|
||||
|
||||
int usbd_init(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (uds_ctx->dev == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
if (usbd_is_initialized(uds_ctx)) {
|
||||
LOG_WRN("USB device support is already initialized");
|
||||
ret = -EALREADY;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
if (!device_is_ready(uds_ctx->dev)) {
|
||||
LOG_ERR("USB device controller is not ready");
|
||||
ret = -ENODEV;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
ret = usbd_device_init_core(uds_ctx);
|
||||
if (ret) {
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
|
||||
uds_ctx->status.initialized = true;
|
||||
|
||||
init_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_enable(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
if (!usbd_is_initialized(uds_ctx)) {
|
||||
LOG_WRN("USB device support is not initialized");
|
||||
ret = -EPERM;
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
if (usbd_is_enabled(uds_ctx)) {
|
||||
LOG_WRN("USB device support is already enabled");
|
||||
ret = -EALREADY;
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
ret = udc_enable(uds_ctx->dev);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to enable controller");
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
ret = usbd_init_control_pipe(uds_ctx);
|
||||
if (ret != 0) {
|
||||
udc_disable(uds_ctx->dev);
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
uds_ctx->status.enabled = true;
|
||||
|
||||
enable_exit:
|
||||
usbd_device_unlock(uds_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_disable(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!usbd_is_enabled(uds_ctx)) {
|
||||
LOG_WRN("USB device support is already disabled");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
ret = usbd_config_set(uds_ctx, 0);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to reset configuration");
|
||||
}
|
||||
|
||||
ret = udc_disable(uds_ctx->dev);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to disable USB device");
|
||||
}
|
||||
|
||||
uds_ctx->status.enabled = false;
|
||||
|
||||
usbd_device_unlock(uds_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_shutdown(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usbd_device_lock(uds_ctx);
|
||||
|
||||
/* TODO: control request dequeue ? */
|
||||
ret = usbd_device_shutdown_core(uds_ctx);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to shutdown USB device");
|
||||
}
|
||||
|
||||
uds_ctx->status.initialized = false;
|
||||
usbd_device_unlock(uds_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
115
subsys/usb/device_next/usbd_device.h
Normal file
115
subsys/usb/device_next/usbd_device.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_DEVICE_H
|
||||
#define ZEPHYR_INCLUDE_USBD_DEVICE_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Get device descriptor bNumConfigurations value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return bNumConfigurations value
|
||||
*/
|
||||
static inline uint8_t usbd_get_num_configs(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
|
||||
return desc->bNumConfigurations;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set device descriptor bNumConfigurations value
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] value new bNumConfigurations value
|
||||
*/
|
||||
static inline void usbd_set_num_configs(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t value)
|
||||
{
|
||||
struct usb_device_descriptor *desc = uds_ctx->desc;
|
||||
|
||||
desc->bNumConfigurations = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether USB device is enabled
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*
|
||||
* @return true if USB device is in enabled, false otherwise
|
||||
*/
|
||||
static inline bool usbd_is_enabled(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return uds_ctx->status.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether USB device is enabled
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*
|
||||
* @return true if USB device is in enabled, false otherwise
|
||||
*/
|
||||
static inline bool usbd_is_initialized(const struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
return uds_ctx->status.initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set device suspended status
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
* @param[in] value new suspended value
|
||||
*/
|
||||
static inline void usbd_status_suspended(struct usbd_contex *const uds_ctx,
|
||||
const bool value)
|
||||
{
|
||||
uds_ctx->status.suspended = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lock USB device stack context
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*/
|
||||
static inline void usbd_device_lock(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
k_mutex_lock(&uds_ctx->mutex, K_FOREVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lock USB device stack context
|
||||
*
|
||||
* @param[in] node Pointer to a device context
|
||||
*/
|
||||
static inline void usbd_device_unlock(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
k_mutex_unlock(&uds_ctx->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init USB device stack core
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_device_init_core(struct usbd_contex *uds_ctx);
|
||||
|
||||
/**
|
||||
* @brief Shutdown USB device stack core
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_device_shutdown_core(struct usbd_contex *const uds_ctx);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_DEVICE_H */
|
195
subsys/usb/device_next/usbd_endpoint.c
Normal file
195
subsys/usb/device_next/usbd_endpoint.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_ch9.h"
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_endpoint.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_ep, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
int usbd_ep_enable(const struct device *dev,
|
||||
const struct usb_ep_descriptor *const ed,
|
||||
uint32_t *const ep_bm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes,
|
||||
ed->wMaxPacketSize, ed->bInterval);
|
||||
if (ret == 0) {
|
||||
usbd_ep_bm_set(ep_bm, ed->bEndpointAddress);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_ep_disable(const struct device *dev,
|
||||
const uint8_t ep,
|
||||
uint32_t *const ep_bm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = udc_ep_disable(dev, ep);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_ep_bm_clear(ep_bm, ep);
|
||||
|
||||
ret = udc_ep_dequeue(dev, ep);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_yield();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usbd_ep_ctrl_set_zlp(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
|
||||
size_t min_len = MIN(setup->wLength, buf->len);
|
||||
|
||||
if (buf->len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ZLP flag when host asks for a bigger length and the
|
||||
* last chunk is wMaxPacketSize long, to indicate the last
|
||||
* packet.
|
||||
*/
|
||||
if (setup->wLength > min_len && !(min_len % USB_CONTROL_EP_MPS)) {
|
||||
/*
|
||||
* Transfer length is less as requested by wLength and
|
||||
* is multiple of wMaxPacketSize.
|
||||
*/
|
||||
LOG_DBG("add ZLP, wLength %u buf length %u",
|
||||
setup->wLength, min_len);
|
||||
udc_ep_buf_set_zlp(buf);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All the functions below are part of public USB device support API.
|
||||
*/
|
||||
|
||||
struct net_buf *usbd_ep_ctrl_buf_alloc(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t ep, const size_t size)
|
||||
{
|
||||
if (USB_EP_GET_IDX(ep)) {
|
||||
/* Not a control endpoint */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return udc_ep_buf_alloc(uds_ctx->dev, ep, size);
|
||||
}
|
||||
|
||||
int usbd_ep_ctrl_enqueue(struct usbd_contex *const uds_ctx,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct udc_buf_info *bi;
|
||||
|
||||
bi = udc_get_buf_info(buf);
|
||||
if (USB_EP_GET_IDX(bi->ep)) {
|
||||
/* Not a control endpoint */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (USB_EP_DIR_IS_IN(bi->ep)) {
|
||||
if (usbd_is_suspended(uds_ctx)) {
|
||||
LOG_ERR("device is suspended");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
usbd_ep_ctrl_set_zlp(uds_ctx, buf);
|
||||
}
|
||||
|
||||
return udc_ep_enqueue(uds_ctx->dev, buf);
|
||||
}
|
||||
|
||||
struct net_buf *usbd_ep_buf_alloc(const struct usbd_class_node *const c_nd,
|
||||
const uint8_t ep, const size_t size)
|
||||
{
|
||||
struct usbd_contex *uds_ctx = c_nd->data->uds_ctx;
|
||||
|
||||
return udc_ep_buf_alloc(uds_ctx->dev, ep, size);
|
||||
}
|
||||
|
||||
int usbd_ep_enqueue(const struct usbd_class_node *const c_nd,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
struct usbd_contex *uds_ctx = c_nd->data->uds_ctx;
|
||||
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
||||
|
||||
if (USB_EP_DIR_IS_IN(bi->ep)) {
|
||||
if (usbd_is_suspended(uds_ctx)) {
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
bi->owner = (void *)c_nd;
|
||||
|
||||
return udc_ep_enqueue(uds_ctx->dev, buf);
|
||||
}
|
||||
|
||||
int usbd_ep_buf_free(struct usbd_contex *const uds_ctx, struct net_buf *buf)
|
||||
{
|
||||
return udc_ep_buf_free(uds_ctx->dev, buf);
|
||||
}
|
||||
|
||||
int usbd_ep_dequeue(struct usbd_contex *const uds_ctx, const uint8_t ep)
|
||||
{
|
||||
return udc_ep_dequeue(uds_ctx->dev, ep);
|
||||
}
|
||||
|
||||
int usbd_ep_set_halt(struct usbd_contex *const uds_ctx, const uint8_t ep)
|
||||
{
|
||||
struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
|
||||
int ret;
|
||||
|
||||
ret = udc_ep_set_halt(uds_ctx->dev, ep);
|
||||
if (ret) {
|
||||
LOG_WRN("Set halt 0x%02x failed", ep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_ep_bm_set(&ch9_data->ep_halt, ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbd_ep_clear_halt(struct usbd_contex *const uds_ctx, const uint8_t ep)
|
||||
{
|
||||
struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
|
||||
int ret;
|
||||
|
||||
ret = udc_ep_clear_halt(uds_ctx->dev, ep);
|
||||
if (ret) {
|
||||
LOG_WRN("Clear halt 0x%02x failed", ep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_ep_bm_clear(&ch9_data->ep_halt, ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool usbd_ep_is_halted(struct usbd_contex *const uds_ctx, const uint8_t ep)
|
||||
{
|
||||
struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
|
||||
|
||||
return usbd_ep_bm_is_set(&ch9_data->ep_halt, ep);
|
||||
}
|
99
subsys/usb/device_next/usbd_endpoint.h
Normal file
99
subsys/usb/device_next/usbd_endpoint.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_ENDPOINT_H
|
||||
#define ZEPHYR_INCLUDE_USBD_ENDPOINT_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Set bit associated with the endpoint
|
||||
*
|
||||
* The IN endpoints are mapped in the upper nibble.
|
||||
*
|
||||
* @param[in] ep_bm Pointer to endpoint bitmap
|
||||
* @param[in] ep Endpoint address
|
||||
*/
|
||||
static inline void usbd_ep_bm_set(uint32_t *const ep_bm, const uint8_t ep)
|
||||
{
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
*ep_bm |= BIT(USB_EP_GET_IDX(ep) + 16U);
|
||||
} else {
|
||||
*ep_bm |= BIT(USB_EP_GET_IDX(ep));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear bit associated with the endpoint
|
||||
*
|
||||
* The IN endpoints are mapped in the upper nibble.
|
||||
*
|
||||
* @param[in] ep_bm Pointer to endpoint bitmap
|
||||
* @param[in] ep Endpoint address
|
||||
*/
|
||||
static inline void usbd_ep_bm_clear(uint32_t *const ep_bm, const uint8_t ep)
|
||||
{
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
*ep_bm &= ~BIT(USB_EP_GET_IDX(ep) + 16U);
|
||||
} else {
|
||||
*ep_bm &= ~BIT(USB_EP_GET_IDX(ep));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether bit associated with the endpoint is set
|
||||
*
|
||||
* The IN endpoints are mapped in the upper nibble.
|
||||
*
|
||||
* @param[in] ep_bm Pointer to endpoint bitmap
|
||||
* @param[in] ep Endpoint address
|
||||
*
|
||||
* @return true if bit is set, false otherwise
|
||||
*/
|
||||
static inline bool usbd_ep_bm_is_set(const uint32_t *const ep_bm, const uint8_t ep)
|
||||
{
|
||||
unsigned int bit;
|
||||
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
bit = USB_EP_GET_IDX(ep) + 16U;
|
||||
} else {
|
||||
bit = USB_EP_GET_IDX(ep);
|
||||
}
|
||||
|
||||
return (*ep_bm & BIT(bit)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable endpoint
|
||||
*
|
||||
* This function enables endpoint and sets corresponding bit.
|
||||
*
|
||||
* @param[in] dev Pointer to UDC device
|
||||
* @param[in] ed Pointer to endpoint descriptor
|
||||
* @param[in] ep_bm Pointer to endpoint bitmap
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_ep_enable(const struct device *dev,
|
||||
const struct usb_ep_descriptor *const ed,
|
||||
uint32_t *const ep_bm);
|
||||
|
||||
/**
|
||||
* @brief Disable endpoint
|
||||
*
|
||||
* This function disables endpoint and clears corresponding bit.
|
||||
*
|
||||
* @param[in] dev Pointer to UDC device
|
||||
* @param[in] ep Endpoint address
|
||||
* @param[in] ep_bm Pointer to endpoint bitmap
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_ep_disable(const struct device *dev,
|
||||
const uint8_t ep,
|
||||
uint32_t *const ep_bm);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_ENDPOINT_H */
|
259
subsys/usb/device_next/usbd_init.c
Normal file
259
subsys/usb/device_next/usbd_init.c
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_config.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_class_api.h"
|
||||
#include "usbd_endpoint.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_init, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
/* TODO: Allow to disable automatic assignment of endpoint features */
|
||||
|
||||
/* Assign endpoint address and update wMaxPacketSize */
|
||||
static int assign_ep_addr(const struct device *dev,
|
||||
struct usb_ep_descriptor *const ed,
|
||||
uint32_t *const config_ep_bm,
|
||||
uint32_t *const class_ep_bm)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (unsigned int idx = 1; idx < 16U; idx++) {
|
||||
uint16_t mps = ed->wMaxPacketSize;
|
||||
uint8_t ep;
|
||||
|
||||
if (USB_EP_DIR_IS_IN(ed->bEndpointAddress)) {
|
||||
ep = USB_EP_DIR_IN | idx;
|
||||
} else {
|
||||
ep = idx;
|
||||
}
|
||||
|
||||
if (usbd_ep_bm_is_set(config_ep_bm, ep) ||
|
||||
usbd_ep_bm_is_set(class_ep_bm, ep)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ret = udc_ep_try_config(dev, ep,
|
||||
ed->bmAttributes, &mps,
|
||||
ed->bInterval);
|
||||
|
||||
if (ret == 0) {
|
||||
LOG_DBG("ep 0x%02x -> 0x%02x", ed->bEndpointAddress, ep);
|
||||
ed->bEndpointAddress = ep;
|
||||
ed->wMaxPacketSize = mps;
|
||||
usbd_ep_bm_set(class_ep_bm, ed->bEndpointAddress);
|
||||
usbd_ep_bm_set(config_ep_bm, ed->bEndpointAddress);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Unassign all endpoint of a class instance based on class_ep_bm */
|
||||
static int unassign_eps(struct usbd_contex *const uds_ctx,
|
||||
uint32_t *const config_ep_bm,
|
||||
uint32_t *const class_ep_bm)
|
||||
{
|
||||
for (unsigned int idx = 1; idx < 16U && *class_ep_bm; idx++) {
|
||||
uint8_t ep_in = USB_EP_DIR_IN | idx;
|
||||
uint8_t ep_out = idx;
|
||||
|
||||
if (usbd_ep_bm_is_set(class_ep_bm, ep_in)) {
|
||||
if (!usbd_ep_bm_is_set(config_ep_bm, ep_in)) {
|
||||
LOG_ERR("Endpoing 0x%02x not assigned", ep_in);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
usbd_ep_bm_clear(config_ep_bm, ep_in);
|
||||
usbd_ep_bm_clear(class_ep_bm, ep_in);
|
||||
}
|
||||
|
||||
if (usbd_ep_bm_is_set(class_ep_bm, ep_out)) {
|
||||
if (!usbd_ep_bm_is_set(config_ep_bm, ep_out)) {
|
||||
LOG_ERR("Endpoing 0x%02x not assigned", ep_out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
usbd_ep_bm_clear(config_ep_bm, ep_out);
|
||||
usbd_ep_bm_clear(class_ep_bm, ep_out);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure all interfaces and endpoints of a class instance
|
||||
*
|
||||
* The total number of interfaces is stored in the configuration descriptor's
|
||||
* value bNumInterfaces. This value is reset at the beginning of configuration
|
||||
* initialization and is increased according to the number of interfaces.
|
||||
* The respective bInterfaceNumber must be assigned to all interfaces
|
||||
* of a class instance.
|
||||
*
|
||||
* Like bInterfaceNumber the endpoint addresses must be assigned
|
||||
* for all registered instances and respective endpoint descriptors.
|
||||
* We use config_ep_bm variable as map for assigned endpoint for an
|
||||
* USB device configuration.
|
||||
*/
|
||||
static int init_configuration_inst(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_class_data *const data,
|
||||
uint32_t *const config_ep_bm,
|
||||
uint8_t *const nif)
|
||||
{
|
||||
struct usb_desc_header *dh = data->desc;
|
||||
uint8_t *ptr = (uint8_t *)dh;
|
||||
struct usb_if_descriptor *ifd = NULL;
|
||||
struct usb_ep_descriptor *ed;
|
||||
uint32_t class_ep_bm = 0;
|
||||
uint8_t tmp_nif;
|
||||
int ret;
|
||||
|
||||
tmp_nif = *nif;
|
||||
data->iface_bm = 0U;
|
||||
data->ep_active = 0U;
|
||||
|
||||
while (dh->bLength != 0) {
|
||||
|
||||
if (dh->bDescriptorType == USB_DESC_INTERFACE) {
|
||||
ifd = (struct usb_if_descriptor *)ptr;
|
||||
|
||||
data->ep_active |= class_ep_bm;
|
||||
|
||||
if (ifd->bAlternateSetting == 0) {
|
||||
ifd->bInterfaceNumber = tmp_nif;
|
||||
data->iface_bm |= BIT(tmp_nif);
|
||||
tmp_nif++;
|
||||
} else {
|
||||
ifd->bInterfaceNumber = tmp_nif - 1;
|
||||
/*
|
||||
* Unassign endpoints from last alternate,
|
||||
* to work properly it requires that the
|
||||
* characteristics of endpoints in alternate
|
||||
* interfaces are ascending.
|
||||
*/
|
||||
unassign_eps(uds_ctx, config_ep_bm, &class_ep_bm);
|
||||
}
|
||||
|
||||
class_ep_bm = 0;
|
||||
LOG_INF("interface %u alternate %u",
|
||||
ifd->bInterfaceNumber, ifd->bAlternateSetting);
|
||||
}
|
||||
|
||||
if (dh->bDescriptorType == USB_DESC_ENDPOINT) {
|
||||
ed = (struct usb_ep_descriptor *)ptr;
|
||||
ret = assign_ep_addr(uds_ctx->dev, ed,
|
||||
config_ep_bm, &class_ep_bm);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("\tep 0x%02x interface ep-bm 0x%08x",
|
||||
ed->bEndpointAddress, class_ep_bm);
|
||||
}
|
||||
|
||||
ptr += dh->bLength;
|
||||
dh = (struct usb_desc_header *)ptr;
|
||||
}
|
||||
|
||||
if (tmp_nif <= *nif) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*nif = tmp_nif;
|
||||
data->ep_active |= class_ep_bm;
|
||||
|
||||
LOG_INF("Instance iface-bm 0x%08x ep-bm 0x%08x",
|
||||
data->iface_bm, data->ep_active);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a device configuration
|
||||
*
|
||||
* Iterate on a list of all classes in a configuration
|
||||
*/
|
||||
static int init_configuration(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd)
|
||||
{
|
||||
struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
|
||||
struct usbd_class_node *c_nd;
|
||||
uint32_t config_ep_bm = 0;
|
||||
size_t cfg_len = 0;
|
||||
uint8_t nif = 0;
|
||||
int ret;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
|
||||
ret = init_configuration_inst(uds_ctx, c_nd->data,
|
||||
&config_ep_bm, &nif);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to assign endpoint addresses");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usbd_class_init(c_nd);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to initialize class instance");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("Init class node %p, descriptor length %u",
|
||||
c_nd, usbd_class_desc_len(c_nd));
|
||||
cfg_len += usbd_class_desc_len(c_nd);
|
||||
}
|
||||
|
||||
/* Update wTotalLength and bNumInterfaces of configuration descriptor */
|
||||
sys_put_le16(sizeof(struct usb_cfg_descriptor) + cfg_len,
|
||||
(uint8_t *)&cfg_desc->wTotalLength);
|
||||
cfg_desc->bNumInterfaces = nif;
|
||||
|
||||
LOG_INF("bNumInterfaces %u wTotalLength %u",
|
||||
cfg_desc->bNumInterfaces,
|
||||
cfg_desc->wTotalLength);
|
||||
|
||||
/* Finally reset configuration's endpoint assignment */
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
c_nd->data->ep_assigned = c_nd->data->ep_active;
|
||||
ret = unassign_eps(uds_ctx, &config_ep_bm, &c_nd->data->ep_active);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbd_init_configurations(struct usbd_contex *const uds_ctx)
|
||||
{
|
||||
struct usbd_config_node *cfg_nd;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
|
||||
int ret;
|
||||
|
||||
ret = init_configuration(uds_ctx, cfg_nd);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to init configuration %u",
|
||||
usbd_config_get_value(cfg_nd));
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("bNumConfigurations %u",
|
||||
usbd_get_num_configs(uds_ctx));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
25
subsys/usb/device_next/usbd_init.h
Normal file
25
subsys/usb/device_next/usbd_init.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_INIT_H
|
||||
#define ZEPHYR_INCLUDE_USBD_INIT_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize all device configurations
|
||||
*
|
||||
* Iterate on a list of all configurations and initialize all
|
||||
* configurations and interfaces. Called only once in sequence per
|
||||
* usbd_init().
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to a device context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_init_configurations(struct usbd_contex *const uds_ctx);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_INIT_H */
|
208
subsys/usb/device_next/usbd_interface.c
Normal file
208
subsys/usb/device_next/usbd_interface.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
#include "usbd_device.h"
|
||||
#include "usbd_class.h"
|
||||
#include "usbd_class_api.h"
|
||||
#include "usbd_endpoint.h"
|
||||
#include "usbd_ch9.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbd_iface, CONFIG_USBD_LOG_LEVEL);
|
||||
|
||||
enum ep_op {
|
||||
EP_OP_TEST, /* Test if interface alternate available */
|
||||
EP_OP_UP, /* Enable endpoint and update endpoints bitmap */
|
||||
EP_OP_DOWN, /* Disable endpoint and update endpoints bitmap */
|
||||
};
|
||||
|
||||
static int handle_ep_op(struct usbd_contex *const uds_ctx,
|
||||
const enum ep_op op,
|
||||
struct usb_ep_descriptor *const ed,
|
||||
uint32_t *const ep_bm)
|
||||
{
|
||||
const uint8_t ep = ed->bEndpointAddress;
|
||||
int ret;
|
||||
|
||||
switch (op) {
|
||||
case EP_OP_TEST:
|
||||
ret = 0;
|
||||
break;
|
||||
case EP_OP_UP:
|
||||
ret = usbd_ep_enable(uds_ctx->dev, ed, ep_bm);
|
||||
break;
|
||||
case EP_OP_DOWN:
|
||||
ret = usbd_ep_disable(uds_ctx->dev, ep, ep_bm);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to handle op %d, ep 0x%02x, bm 0x%08x, %d",
|
||||
op, ep, *ep_bm, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usbd_interface_modify(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_class_node *const node,
|
||||
const enum ep_op op,
|
||||
const uint8_t iface,
|
||||
const uint8_t alt)
|
||||
{
|
||||
struct usb_desc_header *dh;
|
||||
bool found_iface = false;
|
||||
uint8_t *ptr;
|
||||
int ret;
|
||||
|
||||
dh = node->data->desc;
|
||||
ptr = (uint8_t *)dh;
|
||||
|
||||
while (dh->bLength != 0) {
|
||||
struct usb_if_descriptor *ifd;
|
||||
struct usb_ep_descriptor *ed;
|
||||
|
||||
if (dh->bDescriptorType == USB_DESC_INTERFACE) {
|
||||
ifd = (struct usb_if_descriptor *)ptr;
|
||||
|
||||
if (found_iface) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ifd->bInterfaceNumber == iface &&
|
||||
ifd->bAlternateSetting == alt) {
|
||||
found_iface = true;
|
||||
LOG_DBG("Found interface %u %p", iface, node);
|
||||
if (ifd->bNumEndpoints == 0) {
|
||||
LOG_INF("No endpoints, skip interface");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dh->bDescriptorType == USB_DESC_ENDPOINT && found_iface) {
|
||||
ed = (struct usb_ep_descriptor *)ptr;
|
||||
ret = handle_ep_op(uds_ctx, op, ed, &node->data->ep_active);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("Modify interface %u ep 0x%02x by op %u ep_bm %x",
|
||||
iface, ed->bEndpointAddress,
|
||||
op, node->data->ep_active);
|
||||
}
|
||||
|
||||
ptr += dh->bLength;
|
||||
dh = (struct usb_desc_header *)ptr;
|
||||
}
|
||||
|
||||
/* TODO: rollback ep_bm on error? */
|
||||
|
||||
return found_iface ? 0 : -ENODATA;
|
||||
}
|
||||
|
||||
int usbd_interface_shutdown(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd)
|
||||
{
|
||||
struct usbd_class_node *c_nd;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
||||
uint32_t *ep_bm = &c_nd->data->ep_active;
|
||||
|
||||
for (int idx = 1; idx < 16 && *ep_bm; idx++) {
|
||||
uint8_t ep_in = USB_EP_DIR_IN | idx;
|
||||
uint8_t ep_out = idx;
|
||||
int ret;
|
||||
|
||||
if (usbd_ep_bm_is_set(ep_bm, ep_in)) {
|
||||
ret = usbd_ep_disable(uds_ctx->dev, ep_in, ep_bm);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (usbd_ep_bm_is_set(ep_bm, ep_out)) {
|
||||
ret = usbd_ep_disable(uds_ctx->dev, ep_out, ep_bm);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbd_interface_default(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd)
|
||||
{
|
||||
struct usb_cfg_descriptor *desc = cfg_nd->desc;
|
||||
const uint8_t new_cfg = desc->bConfigurationValue;
|
||||
|
||||
/* Set default alternate for all interfaces */
|
||||
for (int i = 0; i < desc->bNumInterfaces; i++) {
|
||||
struct usbd_class_node *class;
|
||||
int ret;
|
||||
|
||||
class = usbd_class_get_by_config(uds_ctx, new_cfg, i);
|
||||
if (class == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = usbd_interface_modify(uds_ctx, class, EP_OP_UP, i, 0);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbd_interface_set(struct usbd_contex *const uds_ctx,
|
||||
const uint8_t iface,
|
||||
const uint8_t alt)
|
||||
{
|
||||
struct usbd_class_node *class;
|
||||
uint8_t cur_alt;
|
||||
int ret;
|
||||
|
||||
ret = usbd_get_alt_value(uds_ctx, iface, &cur_alt);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
class = usbd_class_get_by_iface(uds_ctx, iface);
|
||||
if (class == NULL) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* Test if interface or interface alternate exist */
|
||||
ret = usbd_interface_modify(uds_ctx, class, EP_OP_TEST, iface, alt);
|
||||
if (ret) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Shutdown current interface alternate */
|
||||
ret = usbd_interface_modify(uds_ctx, class, EP_OP_DOWN, iface, cur_alt);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup new interface alternate */
|
||||
ret = usbd_interface_modify(uds_ctx, class, EP_OP_UP, iface, alt);
|
||||
if (ret) {
|
||||
/* TODO: rollback on error? */
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbd_class_update(class, iface, alt);
|
||||
usbd_set_alt_value(uds_ctx, iface, alt);
|
||||
|
||||
return 0;
|
||||
}
|
51
subsys/usb/device_next/usbd_interface.h
Normal file
51
subsys/usb/device_next/usbd_interface.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBD_INTERFACE_H
|
||||
#define ZEPHYR_INCLUDE_USBD_INTERFACE_H
|
||||
|
||||
#include <zephyr/usb/usbd.h>
|
||||
|
||||
/**
|
||||
* @brief Shutdown all interfaces in a configuration.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to USB device support context
|
||||
* @param[in] cfg_nd Pointer to configuration node
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_interface_shutdown(struct usbd_contex *const uds_ctx,
|
||||
struct usbd_config_node *const cfg_nd);
|
||||
|
||||
/**
|
||||
* @brief Setup all interfaces in a configuration to default alternate.
|
||||
*
|
||||
* @note Used only for configuration change.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to USB device support context
|
||||
* @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,
|
||||
struct usbd_config_node *const cfg_nd);
|
||||
|
||||
/**
|
||||
* @brief Set interface alternate
|
||||
*
|
||||
* @note Used only for configuration change.
|
||||
*
|
||||
* @param[in] uds_ctx Pointer to USB device support context
|
||||
* @param[in] iface Interface number (bInterfaceNumber)
|
||||
* @param[in] alternate Interface alternate (bAlternateSetting)
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbd_interface_set(struct usbd_contex *uds_ctx,
|
||||
const uint8_t iface,
|
||||
const uint8_t alternate);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBD_INTERFACE_H */
|
813
subsys/usb/device_next/usbd_shell.c
Normal file
813
subsys/usb/device_next/usbd_shell.c
Normal file
|
@ -0,0 +1,813 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/usb/usbd.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
const struct shell *ctx_shell;
|
||||
|
||||
/*
|
||||
* We define foobaz USB device class which is to be used for the
|
||||
* specific shell commands like register and submit.
|
||||
*/
|
||||
|
||||
#define FOOBAZ_VREQ_OUT 0x5b
|
||||
#define FOOBAZ_VREQ_IN 0x5c
|
||||
|
||||
static uint8_t foobaz_buf[512];
|
||||
|
||||
/* Make supported vendor request visible for the device stack */
|
||||
static const struct usbd_cctx_vendor_req foobaz_vregs =
|
||||
USBD_VENDOR_REQ(FOOBAZ_VREQ_OUT, FOOBAZ_VREQ_IN);
|
||||
|
||||
struct foobaz_iface_desc {
|
||||
struct usb_if_descriptor if0;
|
||||
struct usb_if_descriptor if1;
|
||||
struct usb_ep_descriptor if1_out_ep;
|
||||
struct usb_ep_descriptor if1_in_ep;
|
||||
struct usb_desc_header term_desc;
|
||||
} __packed;
|
||||
|
||||
static struct foobaz_iface_desc foobaz_desc = {
|
||||
.if0 = {
|
||||
.bLength = sizeof(struct usb_if_descriptor),
|
||||
.bDescriptorType = USB_DESC_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = USB_BCC_VENDOR,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.iInterface = 0,
|
||||
},
|
||||
|
||||
.if1 = {
|
||||
.bLength = sizeof(struct usb_if_descriptor),
|
||||
.bDescriptorType = USB_DESC_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_BCC_VENDOR,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.iInterface = 0,
|
||||
},
|
||||
|
||||
.if1_out_ep = {
|
||||
.bLength = sizeof(struct usb_ep_descriptor),
|
||||
.bDescriptorType = USB_DESC_ENDPOINT,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
|
||||
.if1_in_ep = {
|
||||
.bLength = sizeof(struct usb_ep_descriptor),
|
||||
.bDescriptorType = USB_DESC_ENDPOINT,
|
||||
.bEndpointAddress = 0x81,
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
|
||||
/* Termination descriptor */
|
||||
.term_desc = {
|
||||
.bLength = 0,
|
||||
.bDescriptorType = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static size_t foobaz_get_ep_mps(struct usbd_class_data *const data)
|
||||
{
|
||||
struct foobaz_iface_desc *desc = data->desc;
|
||||
|
||||
return desc->if1_out_ep.wMaxPacketSize;
|
||||
}
|
||||
|
||||
static size_t foobaz_ep_addr_out(struct usbd_class_data *const data)
|
||||
{
|
||||
struct foobaz_iface_desc *desc = data->desc;
|
||||
|
||||
return desc->if1_out_ep.bEndpointAddress;
|
||||
}
|
||||
|
||||
static size_t foobaz_ep_addr_in(struct usbd_class_data *const data)
|
||||
{
|
||||
struct foobaz_iface_desc *desc = data->desc;
|
||||
|
||||
return desc->if1_in_ep.bEndpointAddress;
|
||||
}
|
||||
|
||||
static void foobaz_update(struct usbd_class_node *const node,
|
||||
uint8_t iface, uint8_t alternate)
|
||||
{
|
||||
shell_info(ctx_shell,
|
||||
"dev: New configuration, interface %u alternate %u",
|
||||
iface, alternate);
|
||||
}
|
||||
|
||||
static int foobaz_cth(struct usbd_class_node *const node,
|
||||
const struct usb_setup_packet *const setup,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
size_t min_len = MIN(sizeof(foobaz_buf), setup->wLength);
|
||||
|
||||
if (setup->bRequest == FOOBAZ_VREQ_IN) {
|
||||
if (buf == NULL) {
|
||||
errno = -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
net_buf_add_mem(buf, foobaz_buf, min_len);
|
||||
shell_info(ctx_shell,
|
||||
"dev: conrol transfer to host, wLength %u | %u",
|
||||
setup->wLength, min_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int foobaz_ctd(struct usbd_class_node *const node,
|
||||
const struct usb_setup_packet *const setup,
|
||||
const struct net_buf *const buf)
|
||||
{
|
||||
size_t min_len = MIN(sizeof(foobaz_buf), setup->wLength);
|
||||
|
||||
if (setup->bRequest == FOOBAZ_VREQ_OUT) {
|
||||
shell_info(ctx_shell,
|
||||
"dev: control transfer to device, wLength %u | %u",
|
||||
setup->wLength, min_len);
|
||||
memcpy(foobaz_buf, buf->data, min_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int foobaz_request_cancelled(struct usbd_class_node *const node,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
struct udc_buf_info *bi;
|
||||
|
||||
bi = udc_get_buf_info(buf);
|
||||
shell_warn(ctx_shell, "Request ep 0x%02x cancelled", bi->ep);
|
||||
shell_warn(ctx_shell, "|-> %p", buf);
|
||||
for (struct net_buf *n = buf; n->frags != NULL; n = n->frags) {
|
||||
shell_warn(ctx_shell, "|-> %p", n->frags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int foobaz_ep_request(struct usbd_class_node *const node,
|
||||
struct net_buf *buf, int err)
|
||||
{
|
||||
struct usbd_contex *uds_ctx = node->data->uds_ctx;
|
||||
struct udc_buf_info *bi;
|
||||
|
||||
bi = udc_get_buf_info(buf);
|
||||
shell_info(ctx_shell, "dev: Handle request ep 0x%02x, len %u",
|
||||
bi->ep, buf->len);
|
||||
|
||||
if (err) {
|
||||
if (err == -ECONNABORTED) {
|
||||
foobaz_request_cancelled(node, buf);
|
||||
} else {
|
||||
shell_error(ctx_shell,
|
||||
"dev: Request failed (%d) ep 0x%02x, len %u",
|
||||
err, bi->ep, buf->len);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == 0 && USB_EP_DIR_IS_OUT(bi->ep)) {
|
||||
shell_hexdump(ctx_shell, buf->data, buf->len);
|
||||
}
|
||||
|
||||
return usbd_ep_buf_free(uds_ctx, buf);
|
||||
}
|
||||
|
||||
static void foobaz_suspended(struct usbd_class_node *const node)
|
||||
{
|
||||
shell_info(ctx_shell, "dev: Device suspended");
|
||||
}
|
||||
|
||||
static void foobaz_resumed(struct usbd_class_node *const node)
|
||||
{
|
||||
shell_info(ctx_shell, "dev: Device resumed");
|
||||
}
|
||||
|
||||
static int foobaz_init(struct usbd_class_node *const node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Define foobaz interface to USB device class API */
|
||||
struct usbd_class_api foobaz_api = {
|
||||
.update = foobaz_update,
|
||||
.control_to_host = foobaz_cth,
|
||||
.control_to_dev = foobaz_ctd,
|
||||
.request = foobaz_ep_request,
|
||||
.suspended = foobaz_suspended,
|
||||
.resumed = foobaz_resumed,
|
||||
.init = foobaz_init,
|
||||
};
|
||||
|
||||
static struct usbd_class_data foobaz_data = {
|
||||
.desc = (struct usb_desc_header *)&foobaz_desc,
|
||||
.v_reqs = &foobaz_vregs,
|
||||
};
|
||||
|
||||
USBD_DEFINE_CLASS(foobaz, &foobaz_api, &foobaz_data);
|
||||
|
||||
USBD_CONFIGURATION_DEFINE(config_foo, USB_SCD_SELF_POWERED, 200);
|
||||
USBD_CONFIGURATION_DEFINE(config_baz, USB_SCD_REMOTE_WAKEUP, 200);
|
||||
|
||||
USBD_DESC_LANG_DEFINE(lang);
|
||||
USBD_DESC_STRING_DEFINE(mfr, "ZEPHYR", 1);
|
||||
USBD_DESC_STRING_DEFINE(product, "Zephyr USBD foobaz", 2);
|
||||
USBD_DESC_STRING_DEFINE(sn, "0123456789ABCDEF", 3);
|
||||
|
||||
USBD_DEVICE_DEFINE(uds_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
|
||||
0x2fe3, 0xffff);
|
||||
|
||||
int cmd_wakeup_request(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_wakeup_request(&uds_ctx);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to wakeup remote %d", err);
|
||||
} else {
|
||||
shell_print(sh, "dev: Requested remote wakeup");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_submit_request(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
size_t len;
|
||||
uint8_t ep;
|
||||
int ret;
|
||||
|
||||
ep = strtol(argv[1], NULL, 16);
|
||||
|
||||
if (ep != foobaz_ep_addr_out(&foobaz_data) &&
|
||||
ep != foobaz_ep_addr_in(&foobaz_data)) {
|
||||
struct foobaz_iface_desc *desc = foobaz_data.desc;
|
||||
|
||||
shell_error(sh, "dev: Endpoint address not valid %x",
|
||||
desc->if1_out_ep.bEndpointAddress);
|
||||
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
len = strtol(argv[2], NULL, 10);
|
||||
} else {
|
||||
len = foobaz_get_ep_mps(&foobaz_data);
|
||||
}
|
||||
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
len = MIN(len, sizeof(foobaz_buf));
|
||||
}
|
||||
|
||||
buf = usbd_ep_buf_alloc(&foobaz, ep, len);
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (USB_EP_DIR_IS_IN(ep)) {
|
||||
net_buf_add_mem(buf, foobaz_buf, len);
|
||||
}
|
||||
|
||||
shell_print(sh, "dev: Submit ep 0x%02x len %u buf %p", ep, len, buf);
|
||||
ret = usbd_ep_enqueue(&foobaz, buf);
|
||||
if (ret) {
|
||||
shell_print(sh, "dev: Failed to queue request buffer");
|
||||
usbd_ep_buf_free(&uds_ctx, buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_cancel_request(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t ep;
|
||||
int ret;
|
||||
|
||||
shell_print(sh, "dev: Request %s %s", argv[1], argv[2]);
|
||||
|
||||
ep = strtol(argv[1], NULL, 16);
|
||||
if (ep != foobaz_ep_addr_out(&foobaz_data) &&
|
||||
ep != foobaz_ep_addr_in(&foobaz_data)) {
|
||||
shell_error(sh, "dev: Endpoint address not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = usbd_ep_dequeue(&uds_ctx, ep);
|
||||
if (ret) {
|
||||
shell_print(sh, "dev: Failed to dequeue request buffer");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_endpoint_halt(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t ep;
|
||||
int ret = 0;
|
||||
|
||||
ep = strtol(argv[1], NULL, 16);
|
||||
if (ep != foobaz_ep_addr_out(&foobaz_data) &&
|
||||
ep != foobaz_ep_addr_in(&foobaz_data)) {
|
||||
shell_error(sh, "dev: Endpoint address not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[2], "set")) {
|
||||
ret = usbd_ep_set_halt(&uds_ctx, ep);
|
||||
} else if (!strcmp(argv[2], "clear")) {
|
||||
ret = usbd_ep_clear_halt(&uds_ctx, ep);
|
||||
} else {
|
||||
shell_error(sh, "dev: Invalid argument: %s", argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
shell_print(sh, "dev: endpoint %s %s halt failed",
|
||||
argv[2], argv[1]);
|
||||
} else {
|
||||
shell_print(sh, "dev: endpoint %s %s halt successful",
|
||||
argv[2], argv[1]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_register(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t cfg;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[2], NULL, 10);
|
||||
ret = usbd_register_class(&uds_ctx, argv[1], cfg);
|
||||
|
||||
if (ret) {
|
||||
shell_error(sh,
|
||||
"dev: failed to add USB class %s to configuration %u",
|
||||
argv[1], cfg);
|
||||
} else {
|
||||
shell_print(sh,
|
||||
"dev: added USB class %s to configuration %u",
|
||||
argv[1], cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_unregister(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t cfg;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[2], NULL, 10);
|
||||
ret = usbd_unregister_class(&uds_ctx, argv[1], cfg);
|
||||
if (ret) {
|
||||
shell_error(sh,
|
||||
"dev: failed to remove USB class %s from configuration %u",
|
||||
argv[1], cfg);
|
||||
} else {
|
||||
shell_print(sh,
|
||||
"dev: removed USB class %s from configuration %u",
|
||||
argv[1], cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_usbd_magic(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_add_descriptor(&uds_ctx, &lang);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &mfr);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &product);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &sn);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to initialize descriptors, %d", err);
|
||||
}
|
||||
|
||||
err = usbd_add_configuration(&uds_ctx, &config_foo);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to add configuration");
|
||||
}
|
||||
|
||||
err = usbd_register_class(&uds_ctx, "foobaz", 1);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to add foobaz class");
|
||||
}
|
||||
|
||||
ctx_shell = sh;
|
||||
err = usbd_init(&uds_ctx);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to initialize device support");
|
||||
}
|
||||
|
||||
err = usbd_enable(&uds_ctx);
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to enable device support");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbd_defaults(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_add_descriptor(&uds_ctx, &lang);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &mfr);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &product);
|
||||
err |= usbd_add_descriptor(&uds_ctx, &sn);
|
||||
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to initialize descriptors, %d", err);
|
||||
} else {
|
||||
shell_print(sh, "dev: USB descriptors initialized");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbd_init(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
ctx_shell = sh;
|
||||
err = usbd_init(&uds_ctx);
|
||||
|
||||
if (err == -EALREADY) {
|
||||
shell_error(sh, "dev: USB already initialized");
|
||||
} else if (err) {
|
||||
shell_error(sh, "dev: Failed to initialize %d", err);
|
||||
} else {
|
||||
shell_print(sh, "dev: USB initialized");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbd_enable(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_enable(&uds_ctx);
|
||||
|
||||
if (err == -EALREADY) {
|
||||
shell_error(sh, "dev: USB already enabled");
|
||||
} else if (err) {
|
||||
shell_error(sh, "dev: Failed to enable USB, error %d", err);
|
||||
} else {
|
||||
shell_print(sh, "dev: USB enabled");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbd_disable(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_disable(&uds_ctx);
|
||||
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to disable USB");
|
||||
return err;
|
||||
}
|
||||
|
||||
shell_print(sh, "dev: USB disabled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_usbd_shutdown(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbd_shutdown(&uds_ctx);
|
||||
|
||||
if (err) {
|
||||
shell_error(sh, "dev: Failed to shutdown USB");
|
||||
return err;
|
||||
}
|
||||
|
||||
shell_print(sh, "dev: USB completely disabled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_device_bcd(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint16_t bcd;
|
||||
int ret;
|
||||
|
||||
bcd = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_bcd(&uds_ctx, bcd);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device bcdUSB to %x", bcd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_device_pid(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint16_t pid;
|
||||
int ret;
|
||||
|
||||
pid = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_pid(&uds_ctx, pid);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device idProduct to %x", pid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_device_vid(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint16_t vid;
|
||||
int ret;
|
||||
|
||||
vid = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_vid(&uds_ctx, vid);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device idVendor to %x", vid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_device_class(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint8_t value;
|
||||
int ret;
|
||||
|
||||
value = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_class(&uds_ctx, value);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device class to %x", value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_device_subclass(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint8_t value;
|
||||
int ret;
|
||||
|
||||
value = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_subclass(&uds_ctx, value);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device subclass to %x", value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_device_proto(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint8_t value;
|
||||
int ret;
|
||||
|
||||
value = strtol(argv[1], NULL, 16);
|
||||
ret = usbd_device_set_proto(&uds_ctx, value);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set device proto to %x", value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_config_add(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint8_t cfg;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[1], NULL, 10);
|
||||
|
||||
if (cfg == 1) {
|
||||
ret = usbd_add_configuration(&uds_ctx, &config_foo);
|
||||
} else if (cfg == 2) {
|
||||
ret = usbd_add_configuration(&uds_ctx, &config_baz);
|
||||
} else {
|
||||
shell_error(sh, "dev: Configuration %u not available", cfg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to add configuration %u", cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_config_self(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
bool self;
|
||||
uint8_t cfg;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[1], NULL, 10);
|
||||
if (!strcmp(argv[2], "yes")) {
|
||||
self = true;
|
||||
} else {
|
||||
self = false;
|
||||
}
|
||||
|
||||
ret = usbd_config_attrib_self(&uds_ctx, cfg, self);
|
||||
if (ret) {
|
||||
shell_error(sh,
|
||||
"dev: failed to set attribute self powered to %u",
|
||||
cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_config_rwup(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
bool rwup;
|
||||
uint8_t cfg;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[1], NULL, 10);
|
||||
if (!strcmp(argv[2], "yes")) {
|
||||
rwup = true;
|
||||
} else {
|
||||
rwup = false;
|
||||
}
|
||||
|
||||
ret = usbd_config_attrib_rwup(&uds_ctx, cfg, rwup);
|
||||
if (ret) {
|
||||
shell_error(sh,
|
||||
"dev: failed to set attribute remote wakeup to %x",
|
||||
cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_config_power(const struct shell *sh, size_t argc,
|
||||
char *argv[])
|
||||
{
|
||||
uint8_t cfg;
|
||||
uint8_t power;
|
||||
int ret;
|
||||
|
||||
cfg = strtol(argv[1], NULL, 10);
|
||||
power = strtol(argv[1], NULL, 10);
|
||||
|
||||
ret = usbd_config_maxpower(&uds_ctx, cfg, power);
|
||||
if (ret) {
|
||||
shell_error(sh, "dev: failed to set bMaxPower value to %u", cfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void class_node_name_lookup(size_t idx, struct shell_static_entry *entry)
|
||||
{
|
||||
size_t match_idx = 0;
|
||||
|
||||
entry->syntax = NULL;
|
||||
entry->handler = NULL;
|
||||
entry->help = NULL;
|
||||
entry->subcmd = NULL;
|
||||
|
||||
STRUCT_SECTION_FOREACH(usbd_class_node, node) {
|
||||
if ((node->name != NULL) && (strlen(node->name) != 0)) {
|
||||
if (match_idx == idx) {
|
||||
entry->syntax = node->name;
|
||||
break;
|
||||
}
|
||||
|
||||
++match_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SHELL_DYNAMIC_CMD_CREATE(dsub_node_name, class_node_name_lookup);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds,
|
||||
SHELL_CMD_ARG(bcd, NULL, "<bcdUSB>",
|
||||
cmd_device_bcd, 2, 0),
|
||||
SHELL_CMD_ARG(pid, NULL, "<idProduct>",
|
||||
cmd_device_pid, 2, 0),
|
||||
SHELL_CMD_ARG(vid, NULL, "<idVendor>",
|
||||
cmd_device_vid, 2, 0),
|
||||
SHELL_CMD_ARG(class, NULL, "<bDeviceClass>",
|
||||
cmd_device_class, 2, 0),
|
||||
SHELL_CMD_ARG(subclass, NULL, "<bDeviceSubClass>",
|
||||
cmd_device_subclass, 2, 0),
|
||||
SHELL_CMD_ARG(proto, NULL, "<bDeviceProtocol>",
|
||||
cmd_device_proto, 2, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds,
|
||||
SHELL_CMD_ARG(add, NULL, "<configuration>",
|
||||
cmd_config_add, 2, 0),
|
||||
SHELL_CMD_ARG(power, NULL, "<configuration> <bMaxPower>",
|
||||
cmd_config_power, 3, 0),
|
||||
SHELL_CMD_ARG(rwup, NULL, "<configuration> <yes, no>",
|
||||
cmd_config_rwup, 3, 0),
|
||||
SHELL_CMD_ARG(self, NULL, "<configuration> <yes, no>",
|
||||
cmd_config_self, 3, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(class_cmds,
|
||||
SHELL_CMD_ARG(add, &dsub_node_name, "<name> <configuration>",
|
||||
cmd_register, 3, 0),
|
||||
SHELL_CMD_ARG(add, &dsub_node_name, "<name> <configuration>",
|
||||
cmd_unregister, 3, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(endpoint_cmds,
|
||||
SHELL_CMD_ARG(halt, NULL, "<endpoint> <set clear>",
|
||||
cmd_endpoint_halt, 3, 0),
|
||||
SHELL_CMD_ARG(submit, NULL, "<endpoint> [length]",
|
||||
cmd_submit_request, 2, 1),
|
||||
SHELL_CMD_ARG(cancel, NULL, "<endpoint>",
|
||||
cmd_cancel_request, 2, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbd_cmds,
|
||||
SHELL_CMD_ARG(wakeup, NULL, "[none]",
|
||||
cmd_wakeup_request, 1, 0),
|
||||
SHELL_CMD_ARG(magic, NULL, "[none]",
|
||||
cmd_usbd_magic, 1, 0),
|
||||
SHELL_CMD_ARG(defaults, NULL, "[none]",
|
||||
cmd_usbd_defaults, 1, 0),
|
||||
SHELL_CMD_ARG(init, NULL, "[none]",
|
||||
cmd_usbd_init, 1, 0),
|
||||
SHELL_CMD_ARG(enable, NULL, "[none]",
|
||||
cmd_usbd_enable, 1, 0),
|
||||
SHELL_CMD_ARG(disable, NULL, "[none]",
|
||||
cmd_usbd_disable, 1, 0),
|
||||
SHELL_CMD_ARG(shutdown, NULL, "[none]",
|
||||
cmd_usbd_shutdown, 1, 0),
|
||||
SHELL_CMD_ARG(device, &device_cmds, "device commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(config, &config_cmds, "configuration commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(class, &class_cmds, "class commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(endpoint, &endpoint_cmds, "endpoint commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_CMD_REGISTER(usbd, &sub_usbd_cmds, "USB device support commands", NULL);
|
Loading…
Add table
Add a link
Reference in a new issue