HID in USB HS - Page 1
Forum section: electronics / general

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 19, 2022, 01:43:58 am
content: Quick question - not completely sure about the answer: what's the max data throughput one can get with HID in USB High-Speed mode? Is it anywhere close to the max rated throughput? Are there any known limitations, either from the standard, or from actual OS implementations?

author: thm_w
author_rank: Super Contributor
timestamp: January 19, 2022, 02:08:52 am
content: 6MB according to this guy:https://www.eevblog.com/forum/microcontrollers/usb-hid-enumeration/msg1154756/#msg115475664kb/s for FS is theoretical max

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 19, 2022, 02:14:44 am
content: Yes, I knew for FS.Thanks for the pointer.

author: ataradov
author_rank: Super Contributor
timestamp: January 19, 2022, 02:33:16 am
content: Table 5-8. "High-speed Interrupt Transaction Limits" in the spec answers that question. The maximum number there is 53248000 bytes/second. The real OSes are not scheduling interrupt transfers anywhere close to this.

author: ataradov
author_rank: Super Contributor
timestamp: January 19, 2022, 03:36:28 am
content: Ok, so I think I figured what they mean by that table. They mean the bandwidth occupied by all the endpoints at once.For a single regular (non high bandwidth) endpoint you are still limited to 1 transfer per microframe. For a 512 byte endpoint this gives you 512 * 8000 = 4 MBytes/second. I'm too lazy to setup a dedicated experiment, but this is the configuration used by Atmel-ICE, and I just tried to flood it with fast DAP commands and got this exact throughput. For a 1024 byte endpoint you should get 8 MByte/sec, but this is where standard HID APIs start to struggle, especially sending data to the device. Receiving should work, since the bandwidth is allocated anyway. I bet direct libusb style access would be fast enough to handle it.And absolute theoretical maximum for one (high bandwidth) endpoint is 1024 * 3 * 8000 = 24.576 MBytes/second. I think I tried that at some point, but the issue you start to run into is that OS fails to enumerate the device if it is not connected to the root hub directly, since it is hard to allocate guaranteed bandwidth with multiple things connected to the hub.Generally bulk is the fastest practical endpoint type.

author: voltsandjolts
author_rank: Unknown
timestamp: January 19, 2022, 03:12:52 pm
content: Perhaps the OP is considering HID class to avoid driver installation hassles on Windows.If that is the case, there is another option: use WCID to get plug 'n' play FS/HS USB vendor class.https://www.eevblog.com/forum/microcontrollers/minimal-usb-cdc-for-pic32/msg3353344/#msg3353344https://www.eevblog.com/forum/microcontrollers/minimal-usb-cdc-for-pic32/msg3560340/#msg3560340(From the KiCAD thread I see the OP might be using Win7 and I'm unsure of WCID status there - it may have been added later through win updates)

author: josip
author_rank: Regular Contributor
timestamp: January 20, 2022, 10:56:26 am
content: Quote from: voltsandjolts on January 19, 2022, 03:12:52 pmWin 10 (not older) or any other OS (I tried) will automatically enumerate CDC device without asking for driver installation, same as with HID. And FS CDC can go up to 1 MByte/s.

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 11:21:34 am
content: Yeh, but COM ports suck.

author: josip
author_rank: Regular Contributor
timestamp: January 20, 2022, 11:32:10 am
content: Quote from: voltsandjolts on January 20, 2022, 11:21:34 amI needed 300 MByte/s multiple transfers at once between micro and PC, wrote FS USB micro stack from zero, and didn't have any problems with CDC on any OS till today.

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 12:33:43 pm
content: Quote from: josip on January 20, 2022, 11:32:10 am300KByte/s?

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 12:35:41 pm
content: I prefer not to build USB devices which enumerate as COM ports because:(1) It can be a hassle for PC software to automatically find which COM port is used by your device, in the worst case opening each port and trying it. I think there is some option to find the descriptor strings / friendly name supplied by your device which would help but is not straightforward. Whereas for a USB device, you can just open by VID:PID.(2) Using WCID is just as easy as CDC from a firmware point of view and also from host point of view i.e. plug 'n' play for both.(3) With a WCID USB device you get a sensible device description in Windows Device Manager (string of your choice, e.g. you can include your company name, device name, sn).(4) With a WCID USB device you get as many endpoints as you want which helps with separation of concerns in firmware and software. CDC is one data endpoint.(5) With a WCID USB device you can use all transfer types (control, bulk, int, iso) and I find vendor commands on EP0 useful. CDC is bulk only.(6) With a WCID USB device you get FS or HS.CDC is FS only.Edit: Apparently CDC is also FS or HS.(7) Creating commercial products in 2022 which use COM ports looks weird and unprofessional IMHO.So, in summary, for USB devices connecting to Windows, WCID is just as easy as CDC but offers more features.

author: tru
author_rank: Regular Contributor
timestamp: January 20, 2022, 01:22:42 pm
content: Quote from: voltsandjolts on January 20, 2022, 12:35:41 pmI don't think CDC is FS only. I've enumerated CDC (ACM) in HS on a FPGA (DE10-Nano). I've also tested it under Windows and there is no rate limiting on the baud rate, i.e. I can actually transfer at around 30MB/s. I think the FS and transfer rate limit is imposed by the microcontroller and or library.

author: josip
author_rank: Regular Contributor
timestamp: January 20, 2022, 01:45:04 pm
content: Quote from: voltsandjolts on January 20, 2022, 12:33:43 pmYes, 300 KByte/s.Quote from: voltsandjolts on January 20, 2022, 12:35:41 pmIn the same time, during work on micro FS USB stack I was testing also WinUSB on Windows, but I didn't found any advance over CDC, and/or reason to use it. Yes connection / disconnection and some other things are more elegant, but this was not important in my case. I have written working micro / PC side for WinUSB.CDC device(s) can be found automatically from PC side by PID:VID without problems on any OS. I am not interesting in serial port handshake, just using it for USB data transfer, and device that I am using can give me with CDC, 15 COM (data) ports, working on any OS plug & play, without asking for any drivers...https://www.nxp.com/docs/en/application-note/AN12597.pdf

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 02:06:31 pm
content: Quote from: josip on January 20, 2022, 01:45:04 pmI'm interested - how do you find serial port from a given VID:PID on Windows and on Linux?QuoteI would say 15 COM ports from one USB device is perhaps functional but not elegant.With a plain USB device you would just have 15 endpoints, with one entry in Device Manager and less clutter in firmware and host code.

author: josip
author_rank: Regular Contributor
timestamp: January 20, 2022, 04:46:20 pm
content: Quote from: voltsandjolts on January 20, 2022, 02:06:31 pmTI MSPDS (https://www.ti.com/tool/MSPDS-> MSPDS-OPEN-SOURCE) is C open source with PC side and FET firmware. PC Side (CCS or MSP430Flasher or something else) is using msp430.dll (is C open source) for communication with FET's. Inside dll source you can find scan / auto detection right COM port by PID / VID that is related to connected FET on Windows, Linux and mac OS. This will be visible on Win CLI like this...D:\msp>msp430flasher -e ERASE_ALL -w firmware.txt -v* -----/|-------------------------------------------------------------------- ** / |__ ** /_ / MSP Flasher v1.3.20 ** | / ** -----|/-------------------------------------------------------------------- *** Evaluating triggers...done* Checking for available FET debuggers:* Found USB FET @ COM7 <- Selected* Initializing interface @ COM7...done...on Linux like this......* Evaluating triggers...done* Checking for available FET debuggers:* Found USB FET @ ttyACM2 <- Selected* Initializing interface @ ttyACM2...done...This (CDC auto detection based on almost any data from USB device descriptor) can be found also on other places, but here is collected for all OS's on one place.

author: mazurov
author_rank: Frequent Contributor
timestamp: January 20, 2022, 06:14:04 pm
content: Quote from: voltsandjolts on January 20, 2022, 02:06:31 pmOn Linux you can grep /var/log/syslog:Code:[85081.902191] usb 1-3.4.4: new full-speed USB device number 18 using xhci_hcd[85082.021049] usb 1-3.4.4: New USB device found, idVendor=0403, idProduct=6001[85082.021055] usb 1-3.4.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3[85082.021060] usb 1-3.4.4: Product: FT232R USB UART[85082.021063] usb 1-3.4.4: Manufacturer: FTDI[85082.021067] usb 1-3.4.4: SerialNumber: A10KL4Z9[85082.024636] ftdi_sio 1-3.4.4:1.0: FTDI USB Serial Device converter detected[85082.024735] usb 1-3.4.4: Detected FT232RL[85082.025171] usb 1-3.4.4: FTDI USB Serial Device converter now attached to ttyUSB1

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 20, 2022, 06:58:47 pm
content: Quote from: ataradov on January 19, 2022, 03:36:28 amYes of course, and I've used bulk mode extensively (and almost exclusively) so far.The question was not about finding the fastest way of transfering data over USB, but about the max actual data throughput using HID. Which you answered, thanks about that.To others: thanks for participating, but the question was not about selecting the most appropriate class given hypothetical requirements that I never stated, but only about HID. =)

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 07:22:25 pm
content: Quote from: josip on January 20, 2022, 04:46:20 pmThanks.That leads me to UsbCdcIoChannel.cpp and the createCdcPortList() function:Code:void UsbCdcIoChannel::createCdcPortList(const uint16_t vendorId, const uint16_t productId, PortMap& portList){#if defined(_WIN32) || defined(_WIN64)stringstream cdcIdStream;cdcIdStream << hex << setfill('0') << "USB\\VID_" << setw(4) << vendorId << "&PID_" << setw(4) << productId;const int BUFFER_SIZE = 128;HDEVINFO hDevInfo = ::SetupDiGetClassDevs(nullptr, "USB", 0, DIGCF_PRESENT | DIGCF_ALLCLASSES );SP_DEVINFO_DATA devInfoData;devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);for (int i = 0; ::SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i ){char deviceId = {0};BOOL result = ::SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, deviceId, BUFFER_SIZE, nullptr);//not TI and/or not CDCif (result && string(deviceId).find(cdcIdStream.str()) != string::npos){DWORD propertyType = 0;BYTE property = {0};::SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, &propertyType, property, BUFFER_SIZE, nullptr);stringstream sstr;for (int k = 0; k < BUFFER_SIZE && property != 0; ++k){sstr << property;}const size_t idBegin = sstr.str().find_last_of('(') + 1;const size_t idEnd = sstr.str().find_last_of(')');assert(idEnd > idBegin);const string name = sstr.str().substr(idBegin, idEnd - idBegin);if ((name && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 ))|| (name && (sstr.str().compare(0, 19, "MSP-FET430UIF - CDC") == 0 ))){PortInfo portInfo(name, string("\\\\.\\")+name, PortInfo::CDC, retrieveSerialFromId(deviceId));if (name && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 )){portInfo.useFlowControl = false;portInfo.useCrc = false;}else if (name && (sstr.str().compare(0, 19, "MSP-FET430UIF - CDC") == 0 )){portInfo.useFlowControl = true;portInfo.useCrc = true;}//if (open){portInfo.status = UsbCdcIoChannel(portInfo).getStatus();}portList[portInfo.name] = portInfo;}}}::SetupDiDestroyDeviceInfoList(hDevInfo);//free resources#elif defined(__APPLE__)CFMutableDictionaryRef matchingDict;kern_return_t kernResult;io_iterator_t iterator;matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);CFDictionarySetValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iterator);if (kernResult != KERN_SUCCESS) {return;}for (;;){io_service_t device = IOIteratorNext(iterator);if (device == 0){if (!IOIteratorIsValid(iterator)){/** Apple documentation advises resetting the iterator if* it should become invalid during iteration.*/IOIteratorReset(iterator);device = IOIteratorNext(iterator);if (device == 0){break;}}else{break;}}CFTypeRef bsdPathAsCFString = nullptr;CFTypeRef vendorIdAsCFNumber = nullptr;CFTypeRef productIdAsCFNumber = nullptr;CFTypeRef ttyDeviceAsCFString = nullptr;CFTypeRef interfaceNumberAsCFNumber = nullptr;char ttyDevice;SInt32 interfaceNumber;char path;SInt32 vID = 0;SInt32 pID = 0;// Get the name of the modem's callout devicebsdPathAsCFString = IORegistryEntryCreateCFProperty(device, CFSTR(kIOCalloutDeviceKey),kCFAllocatorDefault, 0);ttyDeviceAsCFString = IORegistryEntryCreateCFProperty(device, CFSTR(kIOTTYDeviceKey),kCFAllocatorDefault, 0);io_name_t name;IORegistryEntryGetName(device, name);// wander up the hierarchy until we find the level that can give us the// vendor/product IDs and the product name, if availableio_registry_entry_t parent;kern_return_t kernResult = IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent);IOObjectRelease(device);while ( kernResult == KERN_SUCCESS && ( !vendorIdAsCFNumber || !productIdAsCFNumber || !interfaceNumberAsCFNumber) ){if (!vendorIdAsCFNumber){vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,kIOServicePlane,CFSTR(kUSBVendorID),kCFAllocatorDefault, 0);}if (!productIdAsCFNumber){productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,kIOServicePlane,CFSTR(kUSBProductID),kCFAllocatorDefault, 0);}if (!interfaceNumberAsCFNumber){interfaceNumberAsCFNumber = IORegistryEntrySearchCFProperty(parent,kIOServicePlane,CFSTR(kUSBInterfaceNumber),kCFAllocatorDefault, 0);}io_registry_entry_t oldparent = parent;kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);IOObjectRelease(oldparent);}if (interfaceNumberAsCFNumber){CFNumberGetValue((CFNumberRef)interfaceNumberAsCFNumber, kCFNumberSInt32Type, &interfaceNumber);CFRelease(interfaceNumberAsCFNumber);if (interfaceNumber != 1){continue;}}if (ttyDeviceAsCFString){CFStringGetCString((CFStringRef)ttyDeviceAsCFString, ttyDevice, PATH_MAX, kCFStringEncodingUTF8);CFRelease(ttyDeviceAsCFString);}if (bsdPathAsCFString){CFStringGetCString((CFStringRef)bsdPathAsCFString, path, PATH_MAX, kCFStringEncodingUTF8);CFRelease(bsdPathAsCFString);}if (vendorIdAsCFNumber){CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID);CFRelease(vendorIdAsCFNumber);}if (productIdAsCFNumber){CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID);CFRelease(productIdAsCFNumber);}if ((vID == vendorId) && (pID == productId)){PortInfo portInfo(ttyDevice, path, PortInfo::CDC);if (productId == 0x0010){portInfo.useFlowControl = true;portInfo.useCrc = true;}portInfo.status = UsbCdcIoChannel(portInfo).getStatus();portList[portInfo.name] = portInfo;}}#elsestringstream cdcIdStream;cdcIdStream << hex << setfill('0') << "usb:v" << setw(4) << vendorId << "p" << setw(4) << productId;path p("/sys/class/tty/");if (exists(p) && is_directory(p)){const directory_iterator end;for (directory_iterator it(p); it != end; ++it){string dir = it->path().string();if (dir.find("ttyACM") != string::npos){string modalias;int interfaceNumber = -1;std::ifstream modAliasStream((it->path()/"device/modalias").string().c_str());modAliasStream >> modalias;std::ifstream ifNumStream((it->path()/"device/bInterfaceNumber").string().c_str());ifNumStream >> interfaceNumber;if (modalias.find(cdcIdStream.str()) == 0 && interfaceNumber == 0){const string filename = it->path().filename().string();const string portPath = string("/dev/") + filename;PortInfo portInfo(filename, portPath, PortInfo::CDC);if (productId == 0x0010){portInfo.useFlowControl = true;portInfo.useCrc = true;}//if (open){portInfo.status = UsbCdcIoChannel(portInfo).getStatus();}portList[portInfo.name] = portInfo;}}}}#endif}So, on Windows they are using SetupDiEnumDeviceInfo() api call.On Linux they are using sysfsmodaliastrick to get hardware information on the tty device.Similar to the way this bash script finds ttyUSB devices with VID:PID=19D2:0016Code:for i in $(find -L /sys/bus/usb/devices/ -maxdepth 2 -name "ttyUSB*"); doegrep -i "v19d2p0016" $i/../modalias >/dev/null && echo "/dev/${i##*/}"doneAll of the above can be avoided by simply enumerating as a plain usb device and uselibusbwhich handles crossplatform details for you.

author: voltsandjolts
author_rank: Unknown
timestamp: January 20, 2022, 07:36:43 pm
content: Quote from: SiliconWizard on January 20, 2022, 06:58:47 pmYou're welcome.QuoteThe question was unusual in that HID is intended for low bandwith and there is little reason to use it for anything else, given the other options available. Hence the slight OT which has forever ruined your beautiful thread. Anyway, you seem adament you're on the right path, so bon voyage to you.