C/C++ How to get the usb subsystem path for a USB libudev hidraw device? - c++

I'm using libudev in C/C++ with the hidraw subsystem to enumerate and communicate with custom HID devices - working well. My devices are assigned specific usb plugs and they are "hot swappable" - I need to know which plug is connected to each hidraw device. Is there a correspondence between the usb and the hidraw subsystems and how to get the usb path which details the interface route (like: /dev/bus/usb/002/001 and not the hidraw path) for each device from its hidraw device pointer?

i think with hidraw device pointer you mean the device nodes like /dev/hidraw0 or similar
Hidraw uses a dynamic major number, meaning that udev should be relied
on to create hidraw device nodes. Udev will typically create the
device nodes directly under /dev (eg: /dev/hidraw0). As this location
is distribution- and udev rule-dependent, applications should use
libudev to locate hidraw devices attached to the system. There is a
tutorial on libudev with a working example at:
http://www.signal11.us/oss/udev/
linux has two species of device nodes, one created by device drivers i.e. /dev/sdb for a mass storage device and raw device nodes like /dev/bus/usb/BBB/DDD where BBB is the bus number and DDD is the device number, that are created by the kernel directly :
USB Device Issues
USB devices usually have two kinds of device nodes associated with
them.
The first kind is created by device-specific drivers (e.g.,
usb_storage/sd_mod or usblp) in the kernel. For example, a USB mass
storage device would be /dev/sdb, and a USB printer would be
/dev/usb/lp0. These device nodes exist only when the device-specific
driver is loaded.
The second kind of device nodes (/dev/bus/usb/BBB/DDD, where BBB is
the bus number and DDD is the device number) are created even if the
device doesn't have a kernel driver. By using these "raw" USB device
nodes, an application can exchange arbitrary USB packets with the
device, i.e., bypass the possibly-existing kernel driver.
source : http://www.linuxfromscratch.org/blfs/view/7.10/postlfs/devices.html
you want to establish a link between the kernel module device node ( i.e. /dev/hidraw0 ) and the corresponding raw device node ( i.e /dev/bus/usb/BBB/DDD )
you can get the bus address (BBB and DDD ) from the device node using sudo udevadm info -a -p $(sudo udevadm info -q path -n /dev/hidraw0) ( ATTRS{busnum}=="BBB" and ATTRS{devnum}=="DDD" in the output ) however this is a bit ugly
in Find bus number and device number with device file symlink is code using libudev to get bus number BBB and device number DDD for a specific device node in /dev/ i.e. /dev/hidraw0 it uses udev_device_get_sysattr_value(dev, "devnum")); to get DDD in /dev/bus/usb/BBB/DDD and udev_device_get_sysattr_value(dev, busnum")); to get BBB
you can also get BBB and DDD from sysfs ( /sys/devices/ ... ) :
/sys/devices/pci0000:00/0000:00:12.2/usb2/2-5/2-5.4$ ls 2-5.4:1.0
bDeviceSubClass configuration idProduct remove authorized
bmAttributes descriptors idVendor serial
avoid_reset_quirk bMaxPacketSize0 dev manufacturer
speed bcdDevice bMaxPower devnum
maxchild subsystem bConfigurationValue bNumConfigurations devpath
power uevent bDeviceClass bNumInterfaces driver
product urbnum bDeviceProtocol busnum
ep_00
quirks version
source : http://www.signal11.us/oss/udev/
to get the sysfs path of your device ( the /sys/devices/pci0000:00/0000:00:12.2/usb2/2-5/2-5.4 above ) use sudo udevadm info -q path -n /dev/hidraw0
( https://unix.stackexchange.com/questions/344784/how-to-map-sys-bus-usb-devices-to-dev-video )

Hope it's helpful but IIRC, you cannot reliably (with port# being same between insert events) enumerate individual ports on hubs that are connected downstream from a main USB controller point. I think that was one of the reasons we started seeing lots and lots of USB controllers on MB's after USB came out; because when chaining everything off downstream hubs, besides negatively affecting bandwidth, also caused problems with persistent software numbering issues.
I believe when a device is plugged into a USB port directly connected to a USB controller, you can reliably get the same exact port# it's connected to. But when doing that from a downstream multi-port USB hub connected upstream to a USB port from a USB controller, the actual port# on the USB hub does not get passed upstream or even if it does, it is not a predictable port# between insertions/power-resets in the same hub port.

Related

Creating a driver to translate HID-USB requests into HCI packets

So this may seem a little odd, but I'm rather limited by the hardware I have on hand.
I've got a device that consists of a TI CC2640R2 wired to a Silicon Labs CP2110 UART to USB-HID bridge. The basic premise of the device is to send Bluetooth HCI commands from the TI chip to the CP2110, which translates the UART data to an HID record, which can be read by application code on the host machine, and vice versa. What I'd like to do is to wire this device into the predefined HCI stack in Windows 10, so the desktop can use the device as a standard Bluetooth endpoint.
Normally, this wouldn't be a problem, as the device's USB chip would enumerate it as a Bluetooth device, which would cause the PnP manager in Windows to attach it to the system's Bluetooth stack. The HID device, however, complicates matters.
In order to make this work properly, I'd need to intercept HID records from the target device, unpack them, and forward them to the HCI stack so Windows Bluetooth would know what to do. I'd have to do the exact opposite going the other way - take an HCI command, package it into the appropriate HID record, and send the record to the target device.
I've managed to get the source code for the KMDF example of a Bluetooth Serial HCI Bus Driver, but I don't know the proper way to get the target device from the HID class driver, or if I'd need to do something with an HID minidriver to bridge the two driver stacks.
Is it possible to retrieve a pointer to an HID device from kernel mode, and if so, how would I go about doing that?
If not, and an HID minidriver is required to intercept and relay commands, what would be the best method of combining the HCI driver and the HID minidriver to allow the two to interoperate cleanly?

Detecting USB Storage connection using WINAPI

I'm basically trying to detect a simple USB detection and retrieve certain information from it.
Doing so, for a regular USB FLASH drive is quite simple, as most of the MSDN samples show.
HDEVNOTIFY hDevNotify;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
hDevNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
and later on, wait for a WM_DEVICECHANGE message with DBT_DEVICEARRIVAL
i actually get 2 sequential messages:
dbcc_name of the first message :
\\?\USB#VID_0781&PID_5597#4C530001210518100555#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
dbcc_name of the second message:
\\?\USBSTOR#Disk&Ven_SanDisk&Prod_Cruzer_Glide_3.0&Rev_1.00#4C530001210518100555&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
From this one, i can actually retrieve the friendaly name and so on.
Now, my problem occurs when i try to do the same with phone connection.
When i plug my phone using a regular USB cable, i get
\\?\USB#VID_18D1&PID_4EE2#00c5c6f0839a25d4#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
SetupDiGetDeviceRegistryProperty with SPDRP_DEVICEDESC returns USB Composite Device
And when i grant file transfer storage permissions on my mobile, i get a second message with:
\\?\USB#VID_18D1&PID_4EE2#00c5c6f0839a25d4#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
without a friendly name, using the
SetupDiGetDeviceRegistryProperty(hDevInfo, &spDevInfoData,
SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, sizeof(buf), &nSize)
function.
So couple of questions here:
How does windows recognize this as a mobile device, and even shows its name in the notifications bar (Nexus 5x, Select to choose what happens with this device)?
Lets say i would like to copy certain files from or to it. how do i get device handle or device path for WriteFile / CreateFile
How come GetLogicalDrivesStrings retrieves a new drive("c:\\\0d:\\\0") for a regular USB flash drive but doesn't do the same for mobile connection?
Look at the dialog on your phone. You probably picked an option named "MTP". This is a file sharing protocol. On the other hand, the USB stick uses a block storage protocol. That's why you get the USBSTOR announcement.
Block storage allows for dumb devices, and requires intelligence on the host (Windows). It's just a few million 512-byte sectors. Turning that into files is the responsibility of the FAT (or NTFS) driver.
Since block storage uses the FAT driver, it gets a drive letter just like internal hard disks, and can be accessed via CopyFile. Since MTP devices aren't managed by the file system, they don't have drive letters.
Why do mobile phones use MTP then? The big advantage is that MTP allows the phone to be much more in control. It can decide on a file by file basis whether to show it to the USB host.
(Disclosure: you can do that with FAT as well, but my former employer may still have a patent on that. Search for "Data storage access device patent TomTom" if this matters to you)
when a smartphone is connected it appears at first as a SCSI device. then USB protocol supports several attributes (beside vendorID and productID) from that an operating system can get informations like the device name (NEXUS 5x,...) for this especially the attribute fields manufacturer and product
so windows does not 'know' it is a smartphone
the following is the output of udevadm info -a -p /sys/class/scsi_host/... on linux:
looking at device '/devices/pci0000:00/0000:00:12.2/usb1/1-3/1-3:1.0/host42/scsi_host/host42':
KERNEL=="host42"
SUBSYSTEM=="scsi_host"
DRIVER==""
ATTR{unique_id}=="0"
ATTR{host_busy}=="0"
ATTR{cmd_per_lun}=="1"
ATTR{can_queue}=="1"
ATTR{sg_tablesize}=="65535"
ATTR{sg_prot_tablesize}=="0"
ATTR{unchecked_isa_dma}=="0"
ATTR{proc_name}=="usb-storage"
ATTR{state}=="running"
ATTR{supported_mode}=="Initiator"
ATTR{active_mode}=="Initiator"
ATTR{prot_capabilities}=="0"
ATTR{prot_guard_type}=="0"
looking at parent device '/devices/pci0000:00/0000:00:12.2/usb1/1-3/1-3:1.0/host42':
KERNELS=="host42"
SUBSYSTEMS=="scsi"
DRIVERS==""
looking at parent device '/devices/pci0000:00/0000:00:12.2/usb1/1-3/1-3:1.0':
KERNELS=="1-3:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="usb-storage"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bNumEndpoints}=="02"
ATTRS{bInterfaceClass}=="08"
ATTRS{bInterfaceSubClass}=="06"
ATTRS{bInterfaceProtocol}=="50"
ATTRS{supports_autosuspend}=="1"
ATTRS{interface}=="Mass Storage"
looking at parent device '/devices/pci0000:00/0000:00:12.2/usb1/1-3':
KERNELS=="1-3"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="c0"
ATTRS{bMaxPower}=="500mA"
ATTRS{urbnum}=="195"
ATTRS{idVendor}=="feed"
ATTRS{idProduct}=="2002"
ATTRS{bcdDevice}=="ffff"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="480"
ATTRS{busnum}=="1"
ATTRS{devnum}=="34"
ATTRS{devpath}=="3"
ATTRS{version}==" 2.00"
ATTRS{maxchild}=="0"
ATTRS{quirks}=="0x0"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{authorized}=="1"
ATTRS{manufacturer}=="MediaTek"
ATTRS{product}=="X5"
ATTRS{serial}=="0123456789ABCDEF"
then it depends what function you activate on your smartphone. if you want to activate tethering windows will get notified by the android device and load the RNDIS driver. the notification mechanism is a rest of the USB connection
if you want to write or read files from the androids mass storage you have to activate the mass storage option and windows will get notified and 'mount' the android as a file system
so an android has implemented multiple interfaces / USB device classes in its USB structure and the user has to choose which interface to activate (tethering -> RNDIS, storage -> USB mass storage class,...)
this is also the reason why no new drive c:\\\0d:\\\0 appears when you connect the android to windows. At first the android appears as a SCSI device with no specified function (SCSI Target)
For communication with the android in SCSI mode see http://www.stackoverflow.com/questions/3316284/sending-a-specific-scsi-command-to-a-scsi-device-in-windows
if you want to read or write the phones (internal, SD card) activate the mass storage option and you can use the normal USB mass storage interface (like USB memory stick)
for getting the dbcc_name maybe see http://www.stackoverflow.com/questions/2208722/how-to-get-friendly-device-name-from-dev-broadcast-deviceinterface-and-device-in

How to set a specific device ID on OpenNIGrabber?

how can I set a specific device ID to the constructor of the OpenNIGrabber object?
The tutorial example with OpenNIGrabber("#1") or OpenNIGrabber("#2") works well but I need to select a specific device somehow.
Is it possible to choose the device connected to a specific USB port?
I'm using some Xtion PRO.
I'm on ubuntu 13.04 64bit.
You can use bus#address ID where bus number and USB port address (device) can be looked up with lsusb command on Linux. This type of device ID works only on non-Windows systems as you can see in PCL sources (https://github.com/PointCloudLibrary/pcl/blob/master/io/src/openni_grabber.cpp#L352-L361, method pcl::OpenNIGrabber::setupDevice, lines 352-361).
Also you can use ASUS Xtion Pro's serial number as an ID.
More in PCL documentation: http://docs.pointclouds.org/1.7.2/a00897.html#a5753a422ff92067c9065797697d69244
Example
quepas#ubuntu:~$ lsusb
Bus 001 Device 002: ID 1d27:0601 ASUS
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Then:
OpenNIGrabber("1#2")

How to find what device is connected to USB port (HID or Disk drive) in C++

I have managed to enumerate all the connected USB ports using SetupDi calls. Sample code is available at this link.
Please could any one tell me how can I find what device is connected to USB port like whether it is any HID device or any Disk drive?
Thanks.
I would recommend looking at the USBView sample in the WDK. If you are unfamiliar with this, simply run it - this tool walks the entire USB tree on the system and prints out information and descriptor listings for each device.
In your case, I'd start at the RefreshTree() function in this sample, you can then follow the code to see how it enumerates the host controllers, hubs and finally devices. For each device that you find you can look at the bInterfaceClass in the interface descriptors to find out what types of interfaces it is advertising (in your case 0x03 for HID Interface Class and 0x08 for Mass Storage Class).
The easiest way to get the source to this sample is to install the 7.1.0 WDK which is currently available here:
http://www.microsoft.com/en-us/download/details.aspx?id=11800

Is there a way to save a USB device so that re-enumeration can be prevented after replugging?

First of all, we are working with a 3rd party software that ties itself to a USB device. When this USB device is disconnected and reconnected, the software cannot communicate with the device will stop working until the program is restarted. i.e. It only detects the USB device on startup.
Windows is able to see the device, but goes through the full detection/driver installation procedure every time it is reconnected, even if it is reconnected to the same USB port.
The difficulty here is that we have no way of modifying the third party software to poll for the appropriate USB device after the device is unplugged.
As such, we would like to ask if anyone has knowledge on how to go about writing a c++ program to save a USB state/register, prevent Windows from re-enumerating the USB port upon re-connection, and restoring the saved state/register. If so, we would appreciate some guidance in this endeavor. Naturally, we are open to other approaches to solving this issue.
You can't do this at application level. USB is managed by drivers. Furthermore, while the details of USB devices are managed by specific drivers, the basics (such as enumeration) are handled by the standard Windows USB driver. That's logical: Windows has to enumerate the device first to determine its Vendor ID and Product ID, which then determines the specific driver to load.
As for the full reinstallation on every reinsertion, that suggests a violation of the USB spec by the device or the Windows API by the driver. My first guess would be that the device doesn't have a proper serial number.