snd-usb-audio: exposing a vendor HID control channel as mixer controls (Topping M62, 152a:875c)
Mikhail Gavrilov <[email protected]> Wed, 12 Aug 2026 22:10:34 +0500
| Newsgroups | org.kernel.vger.linux-input,org.kernel.vger.linux-sound |
|---|---|
| Message-ID | <[email protected]> |
Hello, I would like to add ALSA mixer controls for the analogue input gain of a USB audio interface whose control channel is a vendor-specific HID interface, and I would like to agree on the shape before writing code, because it crosses into drivers/hid. The device is a Topping Professional M62, USB 152a:875c. In its multichannel modes it presents 10 playback and 16 capture channels on interfaces 1 and 2, a DFU interface, and interface 4 of class 3 (HID) with a vendor-defined usage page, one 16-byte Input report and one 16-byte Output report, and no report IDs. The problem: the microphone preamplifier gain, 0..88 dB per the specification, is not reachable through ALSA. The card does expose a 'Mic Capture Volume', but a gain ladder measured in silence shows that control to be a digital trim after the converter. The recorded noise floor is flat at about -172 dBFS at the bottom of its range, far below any converter's own noise floor, so what is being measured there is the sample word running out of bits; above that the floor rises with unity slope, i.e. one fixed analogue noise being divided down. The analogue stage is reachable only over the HID interface, which is what the vendor's own application uses. I have the protocol. It was reverse engineered from captures of the vendor application's traffic, the same way sound/usb/mixer_scarlett2.c describes in its header. Frames are 15 bytes: 22 33 | 20 01 01 | target | property | s32 big endian | CRC | 66 77 with CRC-16/MODBUS over bytes 2..10, stored big endian. Rebuilding every frame of a capture from the decoded fields reproduces all 2619 of them byte for byte. Inbound reports are that frame plus one pad byte. The device stays silent until the host sends a subscription frame, after which it reports every state change including front-panel button presses, and it answers a "report your state" frame with a full dump. The analogue gain of each microphone input is a single property carrying whole decibels, 0..88, so a plain TLV_DB_SCALE fits it. The constraint, and my question. This device accepts nothing on the control pipe: SET_REPORT and GET_REPORT both stall with EPIPE, for report types Output, Input and Feature alike. So the pattern used by snd_soundblaster_e1_switch_update() in sound/usb/mixer_quirks.c, which sends HID_REQ_SET_REPORT through snd_usb_ctl_msg(), is not available here. The only usable transport is the interrupt endpoints of interface 4, which usbhid binds. Would it be acceptable for a mixer quirk in sound/usb to own that interface? Concretely: an entry in hid_ignore_list so that usbhid stays away, the quirk claiming interface 4, an interrupt IN URB whose completion handler parses the vendor frame, updates cached values and calls snd_ctl_notify(), and usb_interrupt_msg() in the put callbacks. The notification half looks like what snd_usb_mixer_status_create() already does for the audio control interface's status endpoint. Or would you prefer a different layout for this? A first patch would be deliberately minimal: two controls for the analogue gain of the two microphone inputs, with a dB TLV, and nothing else. The line-level inputs and the outputs use index scales with a piecewise taper, which I have measured but would rather submit separately. For context, an ALSA UCM configuration for the same card is already proposed as alsa-project/alsa-ucm-conf#826. That is what would designate the new control as the capture volume, so that userspace moves the hardware gain instead of the digital trim. I can post the full protocol notes and the captures if that would be useful. -- Thanks, Mikhail Gavrilov