Rabbithole that is USB HID in Wine

MissMissM (she/her)
7 min readApr 26, 2021
Image from WiKi Is the glass half empty or half full?

As you know I was a bit frustrated with the proprietary closed source workflow imposed on me compared to the industry standard to get the onboard audio recordings off those wireless mics.

Not only that — BUT remember the bullet time ?

Scaling the workflows..

Like in Matrix they had tons of DSLR cameras surrounding the acting to do just that and it was essential to have precise fine shutter control and not to say efficient ingest off those cameras and certainly someone going to each camera and running some windows app ingesting these photos one by one was out of question…

Now we could say one wants to do some crazy innovative SFX similar to that the GO II can come as nice value proposition considering you get two on board recording units as well as the stereo receiver for them but problem with that is the latency among other 2.4 GHz gotchas not to mention that you need to handle inputs from tons of receivers at the same time so that leaves the ingest over USB.

For ingest already I figured I could attempt to stuff this problem into a container with questionmark around how to pass the connected USB device(s) presented as Human Interface Devices (HID) in various OSes typically at user space into those containers whilst previously I’ve associated all sorts of networking virtual and physical connectivity with those.

In the future I really want to see the USB or HID raw inputs/outputs encapsulated into standardised network protocols/APIs so instead of everyone building their separate IoT APIs and centralised public cloud deployments with questionable data security (say easily with S3)/privacy we could just “plug” our devices in standardised manner to where we want, how we want and when we want…. leaving us all in control with our physical assets

It’s just yet another format?

I also figured I could just reverse-engineer the Oggs but what fun would would it be?

Without some Docker magic and having the ability to run the ingest from multiple devices at the same time cutting all that mad clicking out?

If the mountains don’t come to me..

I could of course have combined but I ran out of time so I just started throwing a generic “over the top” solution to it that I could teach easily to clicki-bunti if they change stuff.

Same time I could ensure the devices are clean for fresh recordings after the files have been successfully ingested as the thing requires you trashbin existing recordings out thru the proprietary app which a regular human may forget.

Benefits? Maybe later…

This would also allow other workflow magic to automatically process to create sound report and such to send straight to offline with additional processing e.g. process the metadata and provide splitting the clips at scene/take and maybe even synchronization to non-linear (NLE) editing offlining.

And even on bullet time on Matrix I bet they had real time previews off their take straight after only enabled by the automated ingest at scale.

Plus I was really interested to see how USB HID has evolved in Wine past year and if it’s ready for the prime time.. gotta learn sometimes!

Association between the physical and the virtual worlds

To parallelize we need a way to associate physical world things within our virtual worlds and even with a single transmitter (TX) device it would be nice if we can pass the device with fixed identifier to the container instance without giving it privileges.

You could certainly put a trigger-scheduler-queue type of ad-hoc containerization where you spawn a container for each new detected connected device but here I am just keeping things single assigned per container.

On OSX you could even script your way with diskutil monitor but you’ll need to handle this logic somewhere which sees all the USB devices attachments and detachments.

Linux udev to keep things neat

For me I want to keep things neat so I want to properly identify each of the RØDE Wireless GO II TX units that store the recordings and talk via USB in a controlled manner so the containers (per device) which run parallel are not mixing up the physical assets they are associated with.

On a Linux host (say a Raspberry PI) for easier identifying the USB can be done by writing udev rules and an example from StackExchange.

Typically we do this with all sorts of physical/virtual network interfaces when we have complex communication requirements where the typical docker NAT doesn’t cut it anymore.

For desktop environment you could alreasy see some example hidraw entries there from physical hardware keys/tokens such as Yubikey that implements U2F/FIDO for browsers and such as organisations such as Google have done the smart thing to reduce phishing.

Find the device popup in dmesg:

usb 1–1: new high-speed USB device number 8 using xhci_hcd
usb 1–1: New USB device found, idVendor=19f7, idProduct=001e, bcdDevice= 1.19
usb 1–1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1–1: Product: Wireless GO II TX
usb 1–1: Manufacturer: RØDE Microphones
usb 1–1: SerialNumber: XXXXX
hid-generic 0003:19F7:001E.0007: hiddev1,hidraw4: USB HID v1.11 Device [RØDE Microphones Wireless GO II TX] on usb-0000:00:14.0–1/input0

Or lookup from usb-devices or lsusb (-v for more detail):

$ sudo lsusb|grep RODE
Bus 001 Device 008: ID 19f7:001e RODE Microphones Wireless GO II TX

Looking at the attributes…

To create a udev rule so we can id and expose this device properly to the associated container without giving it inappropriate privileges.

We can look at the attributes we use to match

udevadm info --attribute-walk /dev/bus/usb/001/008

Good candidates are typically the idProduct and idVendor

looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-1':
KERNEL=="1-1"
SUBSYSTEM=="usb"
DRIVER=="usb"
ATTR{product}=="Wireless GO II TX"
ATTR{version}==" 2.00"
ATTR{idProduct}=="001e"
ATTR{idVendor}=="19f7"

And then write the rules

We need to give the hidraw entities read/write permission in order for these to be usable over WiNe as well as trigger actions e.g. docker run upon device attachment and destroy upon detachment.

Additionally one needs to address the race conditions and make sure containers are not overlapping with appropriate cleanup and monitoring.

Sadly as is the case with network devices we cannot rename or even symlink (Docker doesn’t work with symlink — devices either) the device names to ID them so we have to trigger attachments/detachments and pass the hidrawX here along.

The rules for hidraw I would write from attach/detach trigger:

ACTION=="add", KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="19f7", ATTRS{idProduct}=="001e", USER="docker", GROUP="docker", TAG+="uaccess", RUN+="run something with %n"ACTION=="remove", KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="19f7", ATTRS{idProduct}=="001e", RUN+="clean up %n"

To apply and re-trigger the rules

sudo udevadm control --reload-rules

sudo udevadm trigger

And to test the rules to some degree

sudo udevadm test usb-device path

Create a WINE prefix somewhere

In container we need to be on the edge with Wine and compile it ourselves since typical packaged linux distribution has not included the crucial bits that deal with the USB which have started to appear to Wine from only last year.

I am not covering the problem that is multi-arch / WoW64 / bits here but that’s a long topic by itself considering all this information is rapidly evolving and confused.

Don’t expect perfection but don’t expect RAWness either..

Not everything will run smoothly for every app which I come back later as-in how this thing turned into rabbithole looking at how Windows enumerates USB HID thru SetupAPI and registry and what I need to do for the central app USB HID enumerator to detect the device(s) correctly under Wine.

Just also know that Wine is being used by Valve/Steam for tons of games as part of their white-label Proton and it has had a lot of funding to create great things to make Windows games work in OSX and Linux (or SteamOS) so it’s pretty mature platform for some things :)

I also tried to see if I could test out darling which is attempting to do similar with the OSX than Wine is already doing with the Windows but it isn’t much mature yet considering it doesn’t run much GUI yet and a lot of things are still to be developed.

Whilst running winecfg make sure to set up the environment as “Windows 10” as Windows 7 seems to hit installer detection thing about incompatible environment with some versions of Windows.

To create a wine prefix which default should be 64 bit:

WINEPREFIX=~/rode/central/WINE winecfg

Run the central app installation

You need to install the application into the wine prefix as below.

WINEPREFIX=~/rode/central/WINE wine msiexec /i ~’/Downloads/RODE\ Central.msi’

I installed to C:\Program Files\RODE\

Which simply installed just the central app .exe into

$ md5sum ~/rode/central/WINE/drive_c/Program\ Files/RODE/RODE\ Central.exe
5a4796d2d6165a74d71f07840c0a1ee4 /home/foobar/rode/central/WINE/drive_c/Program Files/RODE/RODE Central.exe

It was all going fine until I hit the brick wall with Wine

I was already crafting the Dockerfile that would run it all from my existing stuff and then more which wasn’t too big of a loss since I had the most bits already I can re-use.

Only to hit a brick wall where the Central App refused to acknowledge the plugged transmitter over USB despite this showing on my USB hid enumerator running over Wine I made myself — there is also pywinusb pure python Windows USB user space snippet or say hclient.

For this I need to continue looking at perhaps what is not implemented at setupapi and more closely how the central app enumerates the USB devices in the Windows environment…

To be continued…

If someone has Windows domain expertise around USB HID / SetupAPI pls or the associated register stuff get in touch I wanna fix it all :)

--

--

MissMissM (she/her)

just some collection of stardust in the wider universe