2022-05-03 09:25 AM
Hi everybody,
I would like to simulate a USB scanner device using libcomposite, so I modified the USB FIFO sizes in the DTS as follows:
usbotg_hs{
u-boot,dm-pre-reloc;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&usb_otg_hs_pins_mx>;
pinctrl-1 = <&usb_otg_hs_sleep_pins_mx>;
status = "okay";
/* USER CODE BEGIN usbotg_hs */
phys = <&usbphyc_port1 0>; /* 0: UTMI switch selects the OTG */
phy-names = "usb2-phy";
u-boot,force-b-session-valid;
u-boot,force-vbus-detection;
dr_mode = "peripheral";
g-rx-fifo-size = <256>;
g-np-tx-fifo-size = <256>;
g-tx-fifo-size = <128 128 64 16 16 16 16 16>;
/* USER CODE END usbotg_hs */
};
This allows me to open the device declaring USB packet sizes of 512 bytes instead of the maximum which would be allowed by stm32mp151.dtsi (64 bytes, the kernel panics declaring 512).
After the configuration of configfs I pull up my application device-side, which borrows from the kernel example in tools/usb/ffs-aio-example/simple/device_app/aio_simple.c, defining three endpoints (IN, OUT, IN) like here:
#pragma pack(1)
typedef struct {
struct usb_functionfs_descs_head_v2 header;
__le32 fs_count;
__le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_sink_command;
struct usb_endpoint_descriptor_no_audio bulk_source_command;
struct usb_endpoint_descriptor_no_audio bulk_sink_scanner;
} __attribute__ ((__packed__)) fs_descs, hs_descs;
} t_usb_device_descriptors;
#pragma pack()
#pragma pack(1)
typedef struct {
struct usb_functionfs_strings_head header;
struct {
__le16 code;
const char str1[sizeof(STR_INTERFACE)];
} __attribute__ ((__packed__)) lang0;
} t_scanner_string;
#pragma pack()
t_usb_device_descriptors descriptors = {
.header = {
.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
.flags = htole32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC),
.length = htole32(sizeof(descriptors)),
},
.fs_count = htole32(USB_NUM_ENDPOINTS + 1),
.fs_descs = {
.intf = {
.bLength = sizeof(descriptors.fs_descs.intf),
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = USB_NUM_ENDPOINTS,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.iInterface = 1,
},
.bulk_sink_command = {
.bLength = sizeof(descriptors.fs_descs.bulk_sink_command),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 1 | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.bulk_source_command = {
.bLength = sizeof(descriptors.fs_descs.bulk_source_command),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 2 | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.bulk_sink_scanner = {
.bLength = sizeof(descriptors.fs_descs.bulk_sink_scanner),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 3 | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
},
.hs_count = htole32(USB_NUM_ENDPOINTS + 1),
.hs_descs = {
.intf = {
.bLength = sizeof(descriptors.hs_descs.intf),
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = USB_NUM_ENDPOINTS,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.iInterface = 1,
},
.bulk_sink_command = {
.bLength = sizeof(descriptors.hs_descs.bulk_sink_command),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 1 | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = htole16(USB_MAX_PACKET_SIZE),
},
.bulk_source_command = {
.bLength = sizeof(descriptors.hs_descs.bulk_source_command),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 2 | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = htole16(USB_MAX_PACKET_SIZE),
},
.bulk_sink_scanner = {
.bLength = sizeof(descriptors.hs_descs.bulk_sink_scanner),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 3 | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = htole16(USB_MAX_PACKET_SIZE),
},
},
};
t_scanner_string strings = {
.header = {
.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
.length = htole32(sizeof(strings)),
.str_count = htole32(1),
.lang_count = htole32(1),
},
.lang0 = {
htole16(0x0409), /* en-us */
STR_INTERFACE,
},
};
The target is to define host IN and OUT endpoints for commands, a IN endpoint to read chunks of data in bulk mode for a scanner.
The configuration seems successful, meaning that via lsusb -vvv I see the device:
Bus 001 Device 017: ID XXXX:YYYY DeviceName
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 255 Vendor Specific Class
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0xXXXX
idProduct 0xYYYY
bcdDevice 1.00
iManufacturer 1 MANUFACTURER
iProduct 2 PRODUCT
iSerial 3 SERIAL
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0027
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 4 Config 1: commands
bmAttributes 0xc0
Self Powered
MaxPower 250mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 5 SOMESTRING
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
bcdUSB 2.00
bDeviceClass 255 Vendor Specific Class
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
bNumConfigurations 1
Device Status: 0x0000
(Bus Powered)
(some information edited out for privacy).
I can send commands and receive answers from/to endpoints 1 and 2 using libusb_bulk_transfer() (libusb 1.0) from the host. However, I cannot receive any data through the third IN endpoint. Device-side writes are successful to the FunctionFS mounted endpoint files though.
So my problem is: both libusb_bulk_transfer() and async transfers with libusb_handle_events_timeout_completed() fail to read anything from the third endpoint host-side. Trying to read 26MB from the former it returns LIBUSB_ERROR_NO_MEM, while the latter tends to crash with SIGSEGV. I am using version 1.0.26 of libusb.
Since I do not see any error message from the kernel (both on device and host side), I am currently suspecting some DTS configuration problem. The hardware works: if I configure a usb-net gadget I can transfer at sustained speed.
I also tried to use 64 bytes packets instead of 512 ones. Same behavior.
Any ideas that could point me in the right direction?
Thank you,
Antonio
Solved! Go to Solution.
2022-05-19 09:07 AM
Thank you Olivier.
I solved the problem fixing some of the code, which was otherwise structurally correct.
It was fine tuned as follows:
g-rx-fifo-size = <64>;
g-np-tx-fifo-size = <64>;
g-tx-fifo-size = <64 640 16 16 16 16 16 16>;
This allows to create for the second IN endpoint the room for five 512 bytes packets (640x4). The throughput is now nearing 40MB/s, which is about the practical limit for USB2.
Antonio
2022-05-17 06:16 AM
Hi @ATringali ,
An internal support case #BZ128324 has been open and is under investigation.
Keep you posted
Olivier
2022-05-19 09:07 AM
Thank you Olivier.
I solved the problem fixing some of the code, which was otherwise structurally correct.
It was fine tuned as follows:
g-rx-fifo-size = <64>;
g-np-tx-fifo-size = <64>;
g-tx-fifo-size = <64 640 16 16 16 16 16 16>;
This allows to create for the second IN endpoint the room for five 512 bytes packets (640x4). The throughput is now nearing 40MB/s, which is about the practical limit for USB2.
Antonio
2022-05-31 06:55 AM
Hi @ATringali
Thanks a lot to share this with the community !
Olivier