Author Topic: HID in USB HS  (Read 3559 times)

0 Members and 1 Guest are viewing this topic.

Online SiliconWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 14464
  • Country: fr
HID in USB HS
« on: January 19, 2022, 01:43:58 am »
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?
 

Offline thm_w

  • Super Contributor
  • ***
  • Posts: 6359
  • Country: ca
  • Non-expert
Re: HID in USB HS
« Reply #1 on: January 19, 2022, 02:08:52 am »
Profile -> Modify profile -> Look and Layout ->  Don't show users' signatures
 

Online SiliconWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 14464
  • Country: fr
Re: HID in USB HS
« Reply #2 on: January 19, 2022, 02:14:44 am »
Yes, I knew for FS.
Thanks for the pointer.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: HID in USB HS
« Reply #3 on: January 19, 2022, 02:33:16 am »
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.
Alex
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11248
  • Country: us
    • Personal site
Re: HID in USB HS
« Reply #4 on: January 19, 2022, 03:36:28 am »
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.
Alex
 
The following users thanked this post: hans, SiliconWizard, Nominal Animal

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #5 on: January 19, 2022, 03:12:52 pm »
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/#msg3353344
https://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)
 

Offline josip

  • Regular Contributor
  • *
  • Posts: 151
  • Country: hr
Re: HID in USB HS
« Reply #6 on: January 20, 2022, 10:56:26 am »
Perhaps the OP is considering HID class to avoid driver installation hassles on Windows.

Win 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.
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #7 on: January 20, 2022, 11:21:34 am »
Yeh, but COM ports suck.
 
The following users thanked this post: Renate

Offline josip

  • Regular Contributor
  • *
  • Posts: 151
  • Country: hr
Re: HID in USB HS
« Reply #8 on: January 20, 2022, 11:32:10 am »
Yeh, but COM ports suck.

I 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.  :-// 
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #9 on: January 20, 2022, 12:33:43 pm »
I 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.  :-//

300KByte/s?
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #10 on: January 20, 2022, 12:35:41 pm »
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.
« Last Edit: January 20, 2022, 01:27:43 pm by voltsandjolts »
 

Offline tru

  • Regular Contributor
  • *
  • Posts: 107
  • Country: gb
Re: HID in USB HS
« Reply #11 on: January 20, 2022, 01:22:42 pm »
(6) With a WCID USB device you get FS or HS. CDC is FS only.
I 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.
« Last Edit: January 20, 2022, 01:28:05 pm by tru »
 
The following users thanked this post: voltsandjolts

Offline josip

  • Regular Contributor
  • *
  • Posts: 151
  • Country: hr
Re: HID in USB HS
« Reply #12 on: January 20, 2022, 01:45:04 pm »
300KByte/s?

Yes, 300 KByte/s.

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.

(7) Creating commercial products in 2022 which use COM ports looks weird and unprofessional IMHO.

So, in summary, for USB devices onnecting to Windows, WCID is just as easy as CDC but offers more features.

In 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
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #13 on: January 20, 2022, 02:06:31 pm »
CDC device(s) can be found automatically from PC side by PID:VID without problems on any OS.
I'm interested - how do you find serial port from a given VID:PID on Windows and on Linux?

Quote
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...
I 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.
 

Offline josip

  • Regular Contributor
  • *
  • Posts: 151
  • Country: hr
Re: HID in USB HS
« Reply #14 on: January 20, 2022, 04:46:20 pm »
I'm interested - how do you find serial port from a given VID:PID on Windows and on Linux?

TI 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.
« Last Edit: January 20, 2022, 04:59:36 pm by josip »
 

Offline mazurov

  • Frequent Contributor
  • **
  • Posts: 524
  • Country: us
Re: HID in USB HS
« Reply #15 on: January 20, 2022, 06:14:04 pm »
I'm interested - how do you find serial port from a given VID:PID on Windows and on Linux?
On Linux you can grep /var/log/syslog:
Code: [Select]
[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
With sufficient thrust, pigs fly just fine - RFC1925
 

Online SiliconWizardTopic starter

  • Super Contributor
  • ***
  • Posts: 14464
  • Country: fr
Re: HID in USB HS
« Reply #16 on: January 20, 2022, 06:58:47 pm »
Generally bulk is the fastest practical endpoint type.

Yes 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. =)
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #17 on: January 20, 2022, 07:22:25 pm »
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.

Thanks.
That leads me to UsbCdcIoChannel.cpp and the createCdcPortList() function:

Code: [Select]
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[BUFFER_SIZE] = {0};
BOOL result = ::SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, deviceId, BUFFER_SIZE, nullptr);

//not TI and/or not CDC
if (result && string(deviceId).find(cdcIdStream.str()) != string::npos)
{
DWORD propertyType = 0;
BYTE property[BUFFER_SIZE] = {0};

::SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, &propertyType, property, BUFFER_SIZE, nullptr);

stringstream sstr;
for (int k = 0; k < BUFFER_SIZE && property[k] != 0; ++k)
{
sstr << property[k];
}

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[0] && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 ))|| (name[0] && (sstr.str().compare(0, 19, "MSP-FET430UIF - CDC") == 0 )))
{
PortInfo portInfo(name, string("\\\\.\\")+name, PortInfo::CDC, retrieveSerialFromId(deviceId));
if (name[0] && (sstr.str().compare(0, 19, "MSP Debug Interface") == 0 ))
{
portInfo.useFlowControl = false;
portInfo.useCrc = false;
}
else if (name[0] && (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[MAXNAMELEN];
SInt32 interfaceNumber;
char path[MAXPATHLEN];
SInt32 vID = 0;
SInt32 pID = 0;

// Get the name of the modem's callout device
bsdPathAsCFString = 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 available
io_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;
}
}
#else
stringstream 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 sysfs modalias trick to get hardware information on the tty device.
Similar to the way this bash script finds ttyUSB devices with VID:PID=19D2:0016
Code: [Select]
for i in $(find -L /sys/bus/usb/devices/ -maxdepth 2 -name "ttyUSB*"); do
        egrep -i "v19d2p0016" $i/../modalias >/dev/null && echo "/dev/${i##*/}"
done



All of the above can be avoided by simply enumerating as a plain usb device and use libusb which handles crossplatform details for you.
« Last Edit: January 21, 2022, 11:08:55 am by voltsandjolts »
 

Offline voltsandjolts

  • Supporter
  • ****
  • Posts: 2298
  • Country: gb
Re: HID in USB HS
« Reply #18 on: January 20, 2022, 07:36:43 pm »
To others: thanks for participating
You're welcome.

Quote
but the question was not about selecting the most appropriate class given hypothetical requirements that I never stated, but only about HID. =)
The 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.
« Last Edit: January 21, 2022, 10:46:01 am by voltsandjolts »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf