diff --git a/README.md b/README.md index 1f8428d..35aa41b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ around that: | | getting the input | MCU bridge needs | |---|---|---| | **Linux** | `snd-usb-audio` quirk ([quirk/](quirk/)) | `snd-virmidi` | -| **Windows** | Digidesign/Avid's own driver | a loopback pair | +| **Windows** | Digidesign/Avid's own driver, **or the dongle** | a loopback pair | | **macOS** | claim the USB interface directly (libusb) | nothing | macOS is the odd one out in both columns. CoreMIDI has no quirk mechanism, so @@ -190,9 +190,19 @@ unzip anywhere, no vcredist or DLLs needed): cd build && cpack ``` -The Command|8 needs Digidesign/Avid's own driver on Windows to expose its MIDI -input and output (`Command|8`, plus `MIDIIN2/3` for the rear MIDI jacks). If -another app (a DAW) holds the port, close it first: WinMM ports are exclusive. +On Windows the Command|8 needs either Digidesign/Avid's own driver to expose its +MIDI input and output (`Command|8`, plus `MIDIIN2/3` for the rear MIDI jacks), +or the [command8-dongle](https://github.com/alphonsom/command8-dongle), which +makes the surface enumerate as an ordinary class-compliant USB-MIDI device with +no driver at all. Either way the ports look the same to this engine. + +**If another app holds the port, close it first: WinMM ports are exclusive.** +In particular, set the Command|8's input *and* output to disabled in a DAW's +MIDI device list before starting `command8-reaper` or `command8-mackie` -- +otherwise the engine cannot open the device and simply fails to start. The DAW +talks to the engine (over OSC, or over the MCU loopback pair), never to the +surface directly: the Command|8 speaks a proprietary protocol, so a DAW sending +it generic MIDI just makes the faders twitch and leaves the display Offline. ### Mackie bridge on Windows diff --git a/quirk/UPSTREAM.md b/quirk/UPSTREAM.md new file mode 100644 index 0000000..833ba19 --- /dev/null +++ b/quirk/UPSTREAM.md @@ -0,0 +1,111 @@ +# Sending the Command|8 quirk upstream + +The patch in this directory is a plain diff. The kernel wants a git commit with +a Signed-off-by, sent inline as email. This is the whole procedure. + +## 1. Get the right tree + +Sound patches go through Takashi Iwai's tree, not mainline directly: + +```sh +git clone https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git +cd sound +git checkout for-next +``` + +`for-next` is the branch new material is based on. Basing on `master` invites a +"please rebase" reply. + +## 2. Apply the change and commit + +```sh +patch -p1 < /path/to/0001-ALSA-usb-audio-add-Digidesign-Command8-MIDI-quirk.patch +git add sound/usb/quirks-table.h +git commit -s # -s adds Signed-off-by from your git identity +``` + +`Signed-off-by` is a legal statement (the Developer's Certificate of Origin), so +`user.name` must be your real name — a handle will be rejected. + +Suggested commit message, using what we actually measured: + +``` +ALSA: usb-audio: Add quirk for Digidesign Command|8 + +The Digidesign Command|8 control surface (0dba:8000) exposes a +MIDIStreaming interface whose class-specific bulk-IN endpoint descriptor +declares bNumEmbMIDIJack 3 but carries only two jack IDs. bLength 6 is +correct for the two that are present, so the count is the field in error, +not the length. The interface also declares only two Embedded MIDI OUT +jacks, and the MS header's wTotalLength (98) disagrees with the +descriptors actually present (82). + +As a result snd-usb-audio binds the device and creates a card, but only +output ports: + + $ amidi -l + Dir Device Name + O hw:2,0,0 Command8 MIDI 1 + O hw:2,0,1 Command8 MIDI 2 + O hw:2,0,2 Command8 MIDI 3 + +With no input port the surface's faders, encoders and buttons are +unreadable, which makes the device useless as a control surface. + +The declared jack topology does not describe the hardware either: both +Embedded MIDI OUT jacks are sourced from external (DIN) input jacks, yet +the surface's own data is observed arriving on cable 0. In practice the +device presents three inputs (the surface plus two DIN) and three +outputs. Rather than trying to repair individual fields, ignore the +descriptors and force fixed endpoints with 3 in and 3 out cables on the +MIDIStreaming interface; the bulk endpoints 0x01/0x81 are auto-detected. + +Tested on a Command|8 with firmware 02.01.02. + +Signed-off-by: Your Name +``` + +Adjust the firmware version if `tools/probe_command8.sh` reports a different +one, and drop the "Tested on" line only if it is not true. + +## 3. Check it before sending + +```sh +./scripts/checkpatch.pl --strict -g HEAD +``` + +Fix anything it reports. Warnings about long lines in quoted output are usually +tolerated, but style errors in the code are not. + +## 4. Find the recipients + +Do not guess the addresses — ask the tree: + +```sh +git format-patch -1 +./scripts/get_maintainer.pl 0001-*.patch +``` + +That will list Takashi Iwai (sound maintainer), the sound mailing list and +`linux-kernel@vger.kernel.org`. Send to the maintainers, CC the lists. + +## 5. Send it + +```sh +git send-email --to= --cc= --cc= 0001-*.patch +``` + +It must be plain-text and inline. Attachments and HTML mail are silently +dropped by the lists. If `git send-email` is not configured, `b4 send` is the +modern alternative and handles most of the setup for you. + +## What to expect + +Quirk-table additions are routine and usually applied quickly. The one question +a reviewer may reasonably ask is why 3 in-cables when only two Embedded MIDI OUT +jacks are declared. The answer is in the commit message and the code comment: +the descriptors are internally inconsistent and contradict observed behaviour, +so they are not a usable basis for anything — hence the fixed-endpoint quirk. + +Reply in-thread, plain text, no top-posting. If asked for changes, send a v2 +with a `---`-delimited changelog below the commit message. diff --git a/src/surface.hpp b/src/surface.hpp index 47b6402..15ec21b 100644 --- a/src/surface.hpp +++ b/src/surface.hpp @@ -21,11 +21,15 @@ namespace command8 { // "Command|8" (the later ports show up as "MIDIIN2/3 (Command|8)"); the bar // also keeps it from matching the "Command8 MCU" loopback endpoints. // The value is unused wherever UsbSurface is the backend, which is everywhere -// libusb is available: it matches on VID/PID instead. No class driver on any -// platform successfully claims the MIDIStreaming interface -- on a stock Linux -// kernel snd-usb-audio binds neither interface, and the macOS and Windows class -// drivers reject it outright -- so there is no port to name and nothing to -// detach. On macOS there is no alternative backend at all. +// libusb is available: it matches on VID/PID instead. +// +// Platforms differ in how they fail. On Linux snd-usb-audio does bind both +// interfaces and creates a card, but only output ports -- "Command8 MIDI 1..3" +// all show direction O, with no input -- which is what the quirk exists to fix +// and what UsbSurface sidesteps. It therefore has to detach the kernel driver, +// which libusb_set_auto_detach_kernel_driver handles (verified: it reattaches +// on release). macOS and Windows reject the MIDIStreaming interface outright +// and expose nothing at all; on macOS there is no alternative backend. #if defined(_WIN32) inline constexpr const char* kDefaultPortMatch = "Command|8"; #elif defined(__APPLE__) diff --git a/src/usb/usb_surface.cpp b/src/usb/usb_surface.cpp index 7189da0..b401b19 100644 --- a/src/usb/usb_surface.cpp +++ b/src/usb/usb_surface.cpp @@ -158,11 +158,12 @@ bool UsbSurface::open(const std::string& port_match) { return false; } - // Belt and braces. In practice no class driver claims this interface on any - // platform -- a stock Linux kernel binds neither interface because the - // descriptors fail to parse -- so there is normally nothing to detach. This - // covers the case where the quirk-patched snd-usb-audio did bind it, and is - // a no-op (NOT_SUPPORTED) on macOS and Windows. + // Required on Linux, where snd-usb-audio does claim this interface -- with + // or without the quirk. It creates output-only ports from the malformed + // descriptors, so the interface is genuinely taken and must be detached; + // libusb reattaches the kernel driver when we release it. A no-op + // (NOT_SUPPORTED) on macOS and Windows, where no class driver accepts the + // device at all. libusb_set_auto_detach_kernel_driver(dev_, 1); const int r = libusb_claim_interface(dev_, C8_USB_INTERFACE);