Class instance must not call usbd_ep_ctrl_enqueue(). Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
299 lines
8.3 KiB
C
299 lines
8.3 KiB
C
/*
|
|
* 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 intermediate 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 nil_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 */ \
|
|
.nil_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)
|
|
{
|
|
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));
|
|
|
|
LOG_WRN("Device-to-Host, wLength %u | %zu", 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 | %zu", 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_INSTANCES_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
|
|
LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
|