Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite USB device example #242

Open
PocketPi opened this issue Mar 7, 2023 · 7 comments
Open

Composite USB device example #242

PocketPi opened this issue Mar 7, 2023 · 7 comments

Comments

@PocketPi
Copy link

PocketPi commented Mar 7, 2023

Hey

I am trying to make a composite usb device consisting of cdcacm and msc profiles.

I am using a STM32F411CEU

I have tried to merge the cdcacm and msc example but when i connect my device to the pc i get the following error:

Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: new full-speed USB device number 36 using xhci_hcd
Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: New USB device found, idVendor=aabb, idProduct=ccdd, bcdDevice= 2.00
Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: Product: wqer
Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: Manufacturer: asdf
Mar 07 13:47:02 thickpad kernel: usb 3-6.1.4: SerialNumber: 62908886
Mar 07 13:47:02 thickpad kernel: usb-storage 3-6.1.4:1.1: USB Mass Storage device detected
Mar 07 13:47:02 thickpad kernel: scsi host0: usb-storage 3-6.1.4:1.1
Mar 07 13:47:34 thickpad kernel: usb 3-6.1.4: reset full-speed USB device number 36 using xhci_hcd
Mar 07 13:47:49 thickpad kernel: usb 3-6.1.4: device descriptor read/64, error -110
Mar 07 13:48:05 thickpad kernel: usb 3-6.1.4: device descriptor read/64, error -110
Mar 07 13:48:05 thickpad kernel: usb 3-6.1.4: reset full-speed USB device number 36 using xhci_hcd

This is most of the code i am running:

#include "usb.h"
#include "buffered_printf.h"
#include "ramdisk.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/usb/cdc.h>
// clang-format off
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/msc.h>
// clang-format on
#include <stddef.h>

#define DEVICE_ID_ADDR 0x1FFF7A10

static char serialno[9] = {0};

static usbd_device *usb_device;

static const struct usb_device_descriptor dev_descr = {
    .bLength = USB_DT_DEVICE_SIZE,
    .bDescriptorType = USB_DT_DEVICE,
    .bcdUSB = 0x0200,
    .bDeviceClass = 0,
    .bDeviceSubClass = 0,
    .bDeviceProtocol = 0,
    .bMaxPacketSize0 = 64,
    .idVendor = 0xaabb,
    .idProduct = 0xccdd,
    .bcdDevice = 0x0200,
    .iManufacturer = 1,
    .iProduct = 2,
    .iSerialNumber = 3,
    .bNumConfigurations = 1,
};

static const struct usb_endpoint_descriptor data_endp[] = {{
                                                               .bLength = USB_DT_ENDPOINT_SIZE,
                                                               .bDescriptorType = USB_DT_ENDPOINT,
                                                               .bEndpointAddress = 0x01,
                                                               .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                               .wMaxPacketSize = 64,
                                                               .bInterval = 1,
                                                           },
                                                           {
                                                               .bLength = USB_DT_ENDPOINT_SIZE,
                                                               .bDescriptorType = USB_DT_ENDPOINT,
                                                               .bEndpointAddress = 0x81,
                                                               .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                               .wMaxPacketSize = 64,
                                                               .bInterval = 1,
                                                           }};

static const struct usb_endpoint_descriptor msc_endp[] = {{
                                                              .bLength = USB_DT_ENDPOINT_SIZE,
                                                              .bDescriptorType = USB_DT_ENDPOINT,
                                                              .bEndpointAddress = 0x03,
                                                              .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                              .wMaxPacketSize = 64,
                                                              .bInterval = 1,
                                                          },
                                                          {
                                                              .bLength = USB_DT_ENDPOINT_SIZE,
                                                              .bDescriptorType = USB_DT_ENDPOINT,
                                                              .bEndpointAddress = 0x83,
                                                              .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                              .wMaxPacketSize = 64,
                                                              .bInterval = 1,
                                                          }};

static const struct usb_interface_descriptor data_iface[] = {{
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = 0,
    .bAlternateSetting = 0,
    .bNumEndpoints = 2,
    .bInterfaceClass = USB_CLASS_DATA,
    .bInterfaceSubClass = 0,
    .bInterfaceProtocol = 0,
    .iInterface = 0,

    .endpoint = data_endp,
}};

static const struct usb_interface_descriptor msc_iface[] = {{
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = 1,
    .bAlternateSetting = 0,
    .bNumEndpoints = 2,
    .bInterfaceClass = USB_CLASS_MSC,
    .bInterfaceSubClass = USB_MSC_SUBCLASS_SCSI,
    .bInterfaceProtocol = USB_MSC_PROTOCOL_BBB,
    .iInterface = 0,

    .endpoint = msc_endp,
}};

static const struct usb_interface ifaces[] = {{
                                                  .num_altsetting = 1,
                                                  .altsetting = data_iface,
                                              },
                                              {
                                                  .num_altsetting = 1,
                                                  .altsetting = msc_iface,
                                              }};

static const struct usb_config_descriptor config_descr = {
    .bLength = USB_DT_CONFIGURATION_SIZE,
    .bDescriptorType = USB_DT_CONFIGURATION,
    .wTotalLength = 0,
    .bNumInterfaces = 2,
    .bConfigurationValue = 1,
    .iConfiguration = 0,
    .bmAttributes = 0x80,
    .bMaxPower = 0x32,

    .interface = ifaces,
};

static const char *usb_strings[] = {
    "asdf",
    "wqer",
    serialno,
};

/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];

static void cdcacm_sof_callback(void) {
    char buf[64];
    uint16_t i = 0;
    while (!usb_fifo_is_empty() && i < 64) {
        usb_fifo_get(&buf[i++]);
    }

    if (i && usb_device) {
        while (usbd_ep_write_packet(usb_device, 0x82, buf, i) == 0) {
        };
    }
}

static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) {
    (void)ep;

    char buf[64];
    uint16_t len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);
}

static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) {
    (void)wValue;

    usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
    usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, NULL);

    usbd_register_sof_callback(usbd_dev, cdcacm_sof_callback);
}

static void serialno_read(char *buf) {
    volatile uint32_t *DEVICE_ID = (volatile uint32_t *)DEVICE_ID_ADDR;
    const uint32_t uid = *DEVICE_ID + *(DEVICE_ID + 1) + *(DEVICE_ID + 2);

    /* Fetch serial number from chip's unique ID. */
    for (int i = 0; i < 8; i++)
        buf[7 - i] = ((uid >> (4 * i)) & 0xF) + '0';

    for (int i = 0; i < 8; i++)
        if (buf[i] > '9')
            buf[i] += 'A' - '9' - 1;
    buf[8] = 0;
}

void usb_setup(void) {
    serialno_read(serialno);

    usb_device = usbd_init(
        &otgfs_usb_driver, &dev_descr, &config_descr, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));

    usbd_register_set_config_callback(usb_device, cdcacm_set_config);

    ramdisk_init();
    usb_msc_init(
        usb_device, 0x83, 64, 0x03, 64, "VendorID", "ProductID", "0.00", ramdisk_blocks(), ramdisk_read, ramdisk_write);

    /* NVIC setup. */
    nvic_enable_irq(NVIC_OTG_FS_IRQ);
    nvic_set_priority(NVIC_OTG_FS_IRQ, 1);
}

void otg_fs_isr(void) {
    if (usb_device) {
        usbd_poll(usb_device);
    }
}

Can you guys help me find out how to progress either with concrete changes to the code or how to debug the problem.

@karlp
Copy link
Member

karlp commented Mar 7, 2023

You probably need to make your control buffer bigger, as your descriptors will now exceed it's space. until libopencm3/libopencm3#1140 is merged, you need to have space to build the descriptors in ram for talking to the host

@PocketPi
Copy link
Author

PocketPi commented Mar 7, 2023

changing the control buffer size to 2048 does not solve the problem.

@karlp
Copy link
Member

karlp commented Mar 7, 2023

I would recheck your descriptors carefully then, I don't have any other ideas, and I've not personally setup a composite with msc. you might want to check that usb_msc_init isn't making any assumptions internally? it's the only class handler that offers such an init() method.

@PocketPi
Copy link
Author

PocketPi commented Mar 7, 2023

That is some good pointers i shall have a look and try with e.g. HID instead of MSC to see if that makes a difference :)

@PocketPi
Copy link
Author

PocketPi commented Mar 7, 2023

CDC and MIDI works together.

I shall try to figure out why MSC does not work

@Tubatstuff
Copy link

Did you ever resolve this one? I'm looking or a CDCACM+MSC composite solution using libopencm3 currently.

@PocketPi
Copy link
Author

PocketPi commented Nov 22, 2023

Did you ever resolve this one? I'm looking or a CDCACM+MSC composite solution using libopencm3 currently.

yes i did.

Its a while ago, but i think that the main problem was not assigning the correct values to the structs.

Here is a copy of what i got working

#define SIZEOFARRAY(x) (sizeof(x) / sizeof((x)[0]))

#define USB_DATA_ENDPOINT_IN  0x81
#define USB_DATA_ENDPOINT_OUT 0x01
#define USB_MSC_ENDPOINT_IN   0x82
#define USB_MSC_ENDPOINT_OUT  0x02
#define USB_CDC_ENDPOINT_IN   0x83

#define INTERFACE_NO_1 0
#define INTERFACE_NO_2 2

#define USB_PACKET_SIZE 64

static usb_endpoint_data_callback_t usb_data_callback = NULL;

static usbd_device *usb_device;

static bool usb_is_connected = false;
static const struct usb_device_descriptor dev_descr = {
    .bLength = USB_DT_DEVICE_SIZE,
    .bDescriptorType = USB_DT_DEVICE,
    .bcdUSB = 0x0200,
    .bDeviceClass = USB_CLASS_CDC,
    .bDeviceSubClass = 0,
    .bDeviceProtocol = 0,
    .bMaxPacketSize0 = 64,
    .idVendor = 0xaabb,
    .idProduct = 0xccdd,
    .bcdDevice = 0x0200,
    .iManufacturer = 1,
    .iProduct = 2,
    .iSerialNumber = 3,
    .bNumConfigurations = 1,
};

static const struct {
    struct usb_cdc_header_descriptor header;
    struct usb_cdc_call_management_descriptor call_mgmt;
    struct usb_cdc_acm_descriptor acm;
    struct usb_cdc_union_descriptor cdc_union;
} __attribute__((packed))
cdcacm_functional_descriptors = {.header =
                                     {
                                         .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
                                         .bDescriptorType = CS_INTERFACE,
                                         .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
                                         .bcdCDC = 0x0110,
                                     },
                                 .call_mgmt =
                                     {
                                         .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor),
                                         .bDescriptorType = CS_INTERFACE,
                                         .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
                                         .bmCapabilities = 0,
                                         .bDataInterface = INTERFACE_NO_1 + 1,
                                     },
                                 .acm =
                                     {
                                         .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
                                         .bDescriptorType = CS_INTERFACE,
                                         .bDescriptorSubtype = USB_CDC_TYPE_ACM,
                                         .bmCapabilities = 0,
                                     },
                                 .cdc_union = {
                                     .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
                                     .bDescriptorType = CS_INTERFACE,
                                     .bDescriptorSubtype = USB_CDC_TYPE_UNION,
                                     .bControlInterface = INTERFACE_NO_1,
                                     .bSubordinateInterface0 = INTERFACE_NO_1 + 1,
                                 }};

/*
 * This notification endpoint isn't implemented. According to CDC spec it's
 * optional, but its absence causes a NULL pointer dereference in the
 * Linux cdc_acm driver.
 */
static const struct usb_endpoint_descriptor comm_endp[] = {{
    .bLength = USB_DT_ENDPOINT_SIZE,
    .bDescriptorType = USB_DT_ENDPOINT,
    .bEndpointAddress = USB_CDC_ENDPOINT_IN,
    .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
    .wMaxPacketSize = 16,
    .bInterval = 255,
}};

static const struct usb_endpoint_descriptor data_endp[] = {{
                                                               .bLength = USB_DT_ENDPOINT_SIZE,
                                                               .bDescriptorType = USB_DT_ENDPOINT,
                                                               .bEndpointAddress = USB_DATA_ENDPOINT_OUT,
                                                               .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                               .wMaxPacketSize = USB_PACKET_SIZE,
                                                               .bInterval = 1,
                                                           },
                                                           {
                                                               .bLength = USB_DT_ENDPOINT_SIZE,
                                                               .bDescriptorType = USB_DT_ENDPOINT,
                                                               .bEndpointAddress = USB_DATA_ENDPOINT_IN,
                                                               .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                               .wMaxPacketSize = USB_PACKET_SIZE,
                                                               .bInterval = 1,
                                                           }};

static const struct usb_endpoint_descriptor msc_endp[] = {{
                                                              .bLength = USB_DT_ENDPOINT_SIZE,
                                                              .bDescriptorType = USB_DT_ENDPOINT,
                                                              .bEndpointAddress = USB_MSC_ENDPOINT_OUT,
                                                              .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                              .wMaxPacketSize = USB_PACKET_SIZE,
                                                              .bInterval = 1,
                                                          },
                                                          {
                                                              .bLength = USB_DT_ENDPOINT_SIZE,
                                                              .bDescriptorType = USB_DT_ENDPOINT,
                                                              .bEndpointAddress = USB_MSC_ENDPOINT_IN,
                                                              .bmAttributes = USB_ENDPOINT_ATTR_BULK,
                                                              .wMaxPacketSize = USB_PACKET_SIZE,
                                                              .bInterval = 1,
                                                          }};

static const struct usb_interface_descriptor comm_iface[] = {{
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = INTERFACE_NO_1,
    .bAlternateSetting = 0,
    .bNumEndpoints = 1,
    .bInterfaceClass = USB_CLASS_CDC,
    .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
    .bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
    .iInterface = 0,

    .endpoint = comm_endp,

    .extra = &cdcacm_functional_descriptors,
    .extralen = sizeof(cdcacm_functional_descriptors),
}};

static const struct usb_interface_descriptor data_iface[] = {{
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = INTERFACE_NO_1 + 1,
    .bAlternateSetting = 0,
    .bNumEndpoints = 2,
    .bInterfaceClass = USB_CLASS_DATA,
    .bInterfaceSubClass = 0,
    .bInterfaceProtocol = 0,
    .iInterface = 0,

    .endpoint = data_endp,
}};

static const struct usb_interface_descriptor msc_iface[] = {{
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = INTERFACE_NO_2,
    .bAlternateSetting = 0,
    .bNumEndpoints = 2,
    .bInterfaceClass = USB_CLASS_MSC,
    .bInterfaceSubClass = USB_MSC_SUBCLASS_SCSI,
    .bInterfaceProtocol = USB_MSC_PROTOCOL_BBB,
    .iInterface = 0,

    .endpoint = msc_endp,
}};

static const struct usb_interface ifaces[] = {
    {
        .num_altsetting = 1,
        .altsetting = comm_iface,
    },
    {
        .num_altsetting = 1,
        .altsetting = data_iface,
    },
    {
        .num_altsetting = 1,
        .altsetting = msc_iface,
    },
};

static const struct usb_config_descriptor config_descr = {
    .bLength = USB_DT_CONFIGURATION_SIZE,
    .bDescriptorType = USB_DT_CONFIGURATION,
    .wTotalLength = 0,
    .bNumInterfaces = 3,
    .bConfigurationValue = 1,
    .iConfiguration = 0,
    .bmAttributes = 0x80,
    .bMaxPower = 0x32,

    .interface = ifaces,
};

static const char *usb_strings[] = {
    "",
    "",
    "",
};

/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];

static enum usbd_request_return_codes
cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
                       void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) {
    (void)complete;
    (void)buf;
    (void)usbd_dev;

    switch (req->bRequest) {
        case USB_CDC_REQ_SET_CONTROL_LINE_STATE: {
            return USBD_REQ_HANDLED;
        }
        case USB_CDC_REQ_SET_LINE_CODING:
            if (*len < sizeof(struct usb_cdc_line_coding)) {
                return USBD_REQ_NOTSUPP;
            }
            return USBD_REQ_HANDLED;
    }
    return USBD_REQ_NOTSUPP;
}

// USB RX Callback - currently just send back what we received
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) {
    (void)ep;
    char buf[USB_PACKET_SIZE];
    if (ep == USB_DATA_ENDPOINT_OUT) {
        uint16_t len = usbd_ep_read_packet(usbd_dev, USB_DATA_ENDPOINT_OUT, buf, USB_PACKET_SIZE);
        for (size_t i = 0; i < len; i++) {
            if (usb_data_callback != NULL) {
                usb_data_callback(buf[i]);
            }
        }
    }
}

static void usb_cdc_set_config_callback(usbd_device *usbd_dev, uint16_t wValue) {
    (void)wValue;

    // We are being configured, usb must be connected.
    if (!usb_is_connected) {
        usb_is_connected = true;
    }

    usbd_ep_setup(usbd_dev, USB_DATA_ENDPOINT_OUT, USB_ENDPOINT_ATTR_BULK, USB_PACKET_SIZE, cdcacm_data_rx_cb);
    usbd_ep_setup(usbd_dev, USB_DATA_ENDPOINT_IN, USB_ENDPOINT_ATTR_BULK, USB_PACKET_SIZE, NULL);
    usbd_ep_setup(usbd_dev, USB_CDC_ENDPOINT_IN, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);

    usbd_register_control_callback(usbd_dev,
                                   USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
                                   USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
                                   cdcacm_control_request);
}

static void usb_suspend_callback() {
    // We are being suspended, disconnect.
    if (usb_is_connected) {
        usb_is_connected = false;
    }
}

int get_block_count(void) {
    uint16_t block_count = 0;
    (void)disk_ioctl(0, GET_SECTOR_COUNT, &block_count);
    return block_count;
}

int read_block(uint32_t lba, uint8_t *copy_to) {
    return disk_read(0, copy_to, lba, 1);
}

int write_block(uint32_t lba, const uint8_t *copy_from) {
    return disk_write(0, copy_from, lba, 1);
}

void usb_setup(void) {
    desig_get_unique_id_as_string(usb_serial_number, sizeof(usb_serial_number));

    usb_device = usbd_init(&otgfs_usb_driver,
                           &dev_descr,
                           &config_descr,
                           usb_strings,
                           SIZEOFARRAY(usb_strings),
                           usbd_control_buffer,
                           sizeof(usbd_control_buffer));

    usbd_register_set_config_callback(usb_device, usb_cdc_set_config_callback);
    usbd_register_suspend_callback(usb_device, usb_suspend_callback);

    usb_msc_init(usb_device,
                 USB_MSC_ENDPOINT_IN,
                 USB_PACKET_SIZE,
                 USB_MSC_ENDPOINT_OUT,
                 USB_PACKET_SIZE,
                 "VendorID",
                 "ProductID",
                 "0.00",
                 get_block_count(),
                 read_block,
                 write_block);
}

void otg_fs_isr(void) {
    if (usb_device) {
        usbd_poll(usb_device);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants