zephyr/subsys/usb/device_next/usbd_ch9.c
Johann Fischer 2dbccf0654 usb: device_next: do not set alternate 0 for non-existent interfaces
A request to set alternate zero for a non-existent interface
would succeed because of the inappropriate order of checks.
Move the check if the desired alternate is the same as the
current one after the check if interface exists.

Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
2023-05-11 15:29:58 +02:00

867 lines
19 KiB
C

/*
* 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);
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;
}
ret = usbd_interface_set(uds_ctx, setup->wIndex, setup->wValue);
if (ret == -ENOENT) {
LOG_INF("Interface or alternate does not exist");
errno = ret;
ret = 0;
}
return ret;
}
static void sreq_feature_halt_notify(struct usbd_contex *const uds_ctx,
const uint8_t ep, const bool halted)
{
struct usbd_class_node *c_nd = usbd_class_get_by_ep(uds_ctx, ep);
if (c_nd != NULL) {
usbd_class_feature_halt(c_nd, ep, halted);
}
}
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;
if (ret == 0) {
/* Notify class instance */
sreq_feature_halt_notify(uds_ctx, ep, false);
}
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;
if (ret == 0) {
/* Notify class instance */
sreq_feature_halt_notify(uds_ctx, ep, true);
}
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) {
if (err == -ECONNABORTED) {
LOG_INF("Transfer 0x%02x aborted (bus reset?)", bi->ep);
net_buf_unref(buf);
return 0;
}
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) {
net_buf_unref(buf);
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");
}
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;
}