Hi, the saga continues. To start, I need to ask if everything has to be set up before trying to bind the UDC. From my reading the answer is *no* but the endpoints and strings need to be written to ep0. I get to the point the descriptors are taken with no error, as well as the strings. The usb0 device shows up for the ncm function on device. However attempting to bind the UDC fails with an IO error, and the host doesn't see a new network device. I would expect to see my device and potentially be able to enumerate the extra endpoints but be unable to actually do anything with them, potentially crashing the kernel on the device if I tried to send data. I'd also like to know: if I half configure or misconfigure the gadget driver can I do a soft reset? Can I do a harder reset by unloading the driver? Do I need to reboot? I can't actually tell, for now it seems like I can continue trying to configure the device but now that I am writing endpoint descriptors I think I need some kind of reset if I mess up. I apologize for the frequent messages, but I don't have a ton of expertise in this area of the kernel. As I stated earlier I'm trying to migrate from a microcontroller to something that isn't a huge waste of time to program. ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <linux/usb/functionfs.h> #include <linux/usb/ch9.h> #include <usbg/usbg.h> // TODO: Use the "scheme" format for libusbgx. // Dependency for systemd is sys-kernel-config.mount (usually?). #define VENDOR 0x1d6b #define PRODUCT 0x0104 #if __BYTE_ORDER == __LITTLE_ENDIAN #define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) #else #define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) #define cpu_to_le32(x) \ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) #endif static const struct { struct usb_functionfs_descs_head_v2 header; __le32 fs_count; __le32 hs_count; //__le32 ss_count; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio sink; struct usb_endpoint_descriptor_no_audio source; } __attribute__((packed)) fs_descs, hs_descs; } __attribute__((packed)) descriptors = { .header = { .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), .length = cpu_to_le32(sizeof descriptors), .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC), }, .fs_count = cpu_to_le32(3), // In the C example these are in other order. .hs_count = cpu_to_le32(3), //.ss_count = cpu_to_le32(0), // Should this be present if no SS? .fs_descs = { .intf = { .bLength = sizeof descriptors.fs_descs.intf, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .iInterface = 1 }, .sink = { .bLength = sizeof descriptors.fs_descs.sink, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .source = { .bLength = sizeof descriptors.fs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, } }, .hs_descs = { .intf = { .bLength = sizeof descriptors.hs_descs.intf, // Ex. uses fs_descs. .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .iInterface = 1, }, .sink = { .bLength = sizeof descriptors.hs_descs.sink, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le32(512), }, .source = { .bLength = sizeof descriptors.hs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le32(512), .bInterval = 1, // NAK every 1 uframe. }, }, }; #define STR_INTERFACE_ "Source/Sink" static const struct { struct usb_functionfs_strings_head header; struct { __le16 code; const char str1[sizeof STR_INTERFACE_]; } __attribute__((packed)) lang0; } __attribute__((packed)) strings = { .header = { .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), .length = cpu_to_le32(sizeof strings), .str_count = cpu_to_le32(1), .lang_count = cpu_to_le32(1), }, .lang0 = { cpu_to_le16(0x0409), // en-US. STR_INTERFACE_, }, }; #define STR_INTERFACE strings.lang0.str1 int usbg_setup( usbg_state **s, usbg_gadget **g, usbg_config **c, usbg_function **f_ffs0, usbg_function **f_ncm0 ); int main(int argc, char *argv[]) { usbg_state *s; usbg_gadget *g; usbg_config *c; usbg_function *f_ffs0, *f_ncm0; if (int rc = usbg_setup(&s, &g, &c, &f_ffs0, &f_ncm0); rc != 0) { puts("gadget exists."); } else { puts("gadget created."); } int ffs_fd = open("/sys/kernel/config/usb_gadget/g1/functions", O_RDONLY); if (int rc = fchdir(ffs_fd); rc != 0) { fprintf(stderr, "unable to chdir to ffs directory: %s\n", strerror(errno)); } system("mount -t functionfs dev0 ./ffs.dev0"); int ffs_fd_ep0 = open("./ffs.dev0/ep0", O_RDWR); if (ffs_fd_ep0 < 0) { fprintf(stderr, "unable to create ep0: %d : %s\n", errno, strerror(errno)); } puts("ep0 opened."); if (int rc = write(ffs_fd_ep0, &descriptors, sizeof(descriptors)); rc < 0) { fprintf(stderr, "unable to write descriptors to ep0: %s\n", strerror(errno)); } if (int rc = write(ffs_fd_ep0, &strings, sizeof(strings)); rc < 0) { fprintf(stderr, "unable to write strings to ep0: %s\n", strerror(errno)); } while (true) sleep(1); close(ffs_fd); } int usbg_setup( usbg_state **s, usbg_gadget **g, usbg_config **c, usbg_function **f_ffs0, usbg_function **f_ncm0 ) { struct usbg_gadget_attrs g_attrs = { .bcdUSB = 0x0200, .bDeviceClass = USB_CLASS_PER_INTERFACE, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = 64, .idVendor = VENDOR, .idProduct = PRODUCT, .bcdDevice = 0x0000 }; struct usbg_gadget_strs g_strs = { .manufacturer = "Foo Inc.", .product = "Bar Gadget", .serial = "0000000000" }; struct usbg_config_strs c_strs = { .configuration = "Default" }; if (usbg_error rc = (usbg_error)usbg_init("/sys/kernel/config", s); rc != USBG_SUCCESS) { goto error_exit; } if (usbg_error rc = (usbg_error)usbg_create_gadget(*s, "g1", &g_attrs, &g_strs, g); rc != USBG_SUCCESS) { goto error_exit; } if (usbg_error rc = (usbg_error)usbg_create_function(*g, USBG_F_NCM, "dev0", NULL, f_ncm0); rc != USBG_SUCCESS) { goto error_exit; } if (usbg_error rc = (usbg_error)usbg_create_function(*g, USBG_F_FFS, "dev0", NULL, f_ffs0); rc != USBG_SUCCESS) { //fprintf(stderr, "Error: %s : %s\n", usbg_error_name(rc), // usbg_strerror(rc)); goto error_exit; } if (usbg_error rc = (usbg_error)usbg_create_config(*g, 1, "c", NULL, &c_strs, c); rc != USBG_SUCCESS) { goto error_exit; } if (usbg_error rc = (usbg_error)usbg_add_config_function(*c, "ncm.dev0", *f_ncm0); rc != USBG_SUCCESS) { goto error_exit; } if (usbg_error rc = (usbg_error)usbg_add_config_function(*c, "ffs.dev0", *f_ffs0); rc != USBG_SUCCESS) { goto error_exit; } return 0; error_exit: return -2; } ```