/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include 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, ())