47 comments

  • yellowapple a day ago ago

    Perfect timing. I'm expecting to get my hands on a MOTU MIDI Express XT from my local Guitar Center within the next couple days (I paid for it when it arrived there a couple weeks ago, but they have a mandatory waiting period on used equipment to make sure it ain't stolen), which unfortunately uses some weird proprietary protocol instead of class-compliant MIDI-over-USB — so any use over USB from my PCs (nearly all of which are running Linux, OpenBSD, Haiku, or something other than Windows or macOS) is a no-go. This is okay for my immediate use cases (I just need it to route between some synth modules and controllers, without necessarily needing the PC to do any processing in-between), but it'd be cool to get the PC side of things working, too.

    There's an existing out-of-tree Linux driver¹ that looks promising, but AFAICT it only does the bare minimum of exposing the MIDI ports for use with e.g. JACK, and it's also unclear how stable it is and whether it really does support the XT (the README says the kernel panic got fixed, but there are open issues about it; the README says the XT's supported, but there are open issues about that, too). I'd like to be able to create new routing presets and stuff like what the proprietary companion app can do, and I'd also like to be able to use the thing without needing to shove extra drivers into my kernel, and I'd also like to be able to use the thing on my OpenBSD and Haiku boxen, so I've been perusing LibUSB docs since a userspace USB driver that then presents the relevant MIDI ports and some tooling to reroute the MIDI ports as desired seems like something useful. This article happens to be exactly what I've been looking for w.r.t. a starting point for such a userspace driver.

    ----

    ¹: https://github.com/vampirefrog/motu

    • ryanmcbride 6 hours ago ago

      I could be misremembering but I believe the guitar center waiting period isn't _just_ to make sure it's not stolen (I kind of doubt they're actually doing anything like an investigation) but also because legally their used equipment side of business has to operate like a pawn shop, in that they aren't allowed to sell something they've "bought" until after the window for the pawner to buy it back expires.

    • grawlinson a day ago ago

      I packaged that driver on the AUR, since I’ve got the same device. I can’t get the binary blob to work (admittedly, I haven’t tried very hard) and it’s not high on my list of priorities so I’m okay using it as a dumb MIDI tool.

    • colechristensen 4 hours ago ago

      I have had significant success using Claude to reverse engineer hardware

      • Surac 3 hours ago ago

        Yes Claude is impressive in HW-Development. It knows all those magic numbers and how chips work. i even use it to do development for Power delivery Systems.

        • sitzkrieg 11 minutes ago ago

          hope nothing goes wrong with it

  • kevmo314 a day ago ago

    If you are interested in doing this in golang I wrote a library to avoid needing cgo: https://github.com/kevmo314/go-usb

    I use this to access UVC devices correspondingly without cgo: https://github.com/kevmo314/go-uvc

  • 2bird3 18 hours ago ago

    Nice article, happen to have been working on a usbip system for my Macbook M3 using similar tech.

    Worth noting that, this approach is limitted on newer macOS systems: can't build libusb "userspace USB drivers" for devices that are supported by macOS. Without manually disabling some Security features, can't override drivers for system-recognized USB devices.

    • mycall 15 hours ago ago

      Overriding drivers can be mitigated as it is just one layer.

  • Neywiny a day ago ago

    But this kinda expects that your USB driver is the application code too, no? This is less of a driver and more of a library + program. If I have, say, a USB to Ethernet device, how do I hook this into the ethernet adapter subsystem?

    • pjc50 a day ago ago

      Things which are relatively standard tend to get good generic support: Ethernet devices will generally be USB/CDC/ECM or RNDIS, for example. That may Just Work (tm) if it has the right descriptors.

      The userland approach is much more useful for weird or custom devices. In particular, on Windows you can do one of these user space "drivers" without having to bother with driver signing, and if you use libusb it will be portable too.

      (I maintain a small USB DFU based tool for work)

      • Neywiny a day ago ago

        DFU - great example. If you have a USB device that has a DFU class that needs a custom driver, can dfu-util and the like hook into these userspace drivers? Or do you also need to maintain the application part?

        • pjc50 a day ago ago

          Dfu-util is one of those "user space drivers", so if you have a nonstandard protocol you'd have to add it directly to dfu-util. There's no intermediate API.

          It's not easy to set up a fake or "remapped" USB device on most OS as far as I'm aware, if you were trying to write an adapter program that modified USB packets.

        • WerWolv a day ago ago

          dfu-util actually also just uses libusb under the hood! Any class or device that doesn't have a driver baked into the OS can be implemented like this. And if you'd need the DFU functionality in a different application, you may be able to just simply link parts of the dfu-util tool into your application

    • WerWolv a day ago ago

      On Linux you could create a tun/tap device from your application and translate data sent over that to requests sent to the ethernet adapter.

      Of course, when you're doing these things in userspace you either need some way of communicating with the Kernel or for the other subsystems to be in userspace as well.

      • Neywiny a day ago ago

        Not to be too facetious but a great place for communicating with the kernel where there are a ton of other driver subsystems is... the kernel.

        Possibly a good addition to the article would be parallel development of an lkm. I guess it wouldn't have that windows interop but I would also be interested to see how this driver would be implemented on Windows. If it's idk 10x as many lines in the kernel vs userspace, that's a great benefit to the userspace approach.

        • pjc50 a day ago ago

          Driver signing is a killer issue on Windows; if you put your machine into dev/unsigned mode you get an ugly banner that can't be turned off.

          Much easier to design the device to avoid that. E.g. by abusing USB-HID. The desktop USB missile launcher toy is USB HID, for example.

          • kam 18 hours ago ago

            No need to pretend to be HID. Windows has WinUSB for userspace USB drivers that don't need special signing.

            • formerly_proven 11 hours ago ago

              iirc sending HID feature reports doesn’t need admin rights on windows

        • WerWolv a day ago ago

          Arguably all these other subsystems shouldn't be in the Kernel either but that's a different topic :)

          There are quite a few benefits to doing these things in userspace over the Kernel, not really necessarily just because of the code size:

          - The code is much easier to write and debug, you just write code like you always would.

          - Bugs don't have the possibility to taking down your entire system or introduce vulnerabilities

          - Especially on Windows, everyone can do this without requiring an impossible to get driver signing certificate

        • dist-epoch a day ago ago

          In HFT user-space networking drivers have a long history - there is too much latency induced by switching from kernel to user space to handle networking.

          > OpenOnload: A user-space network stack that intercepts socket calls to bypass the kernel network stack, accelerating standard socket operations for faster networking.

          > Netmap: A framework providing a simple API for high-speed packet I/O in user space, bypassing much of the kernel overhead for efficient packet forwarding and filtering.

          https://dysnix.com/blog/high-frequency-trading-infrastructur...

  • matheusmoreira 18 hours ago ago

    I wish I could have read this article years ago. Reverse engineering my laptop's features would have been so much easier. My keyboard LED program is still among my favorite projects.

  • Surac 3 hours ago ago

    i had to do this inside a FPGA. In the end it is just a very complicated Serial device. Was a Nightmare to support HDI Devices

  • kabir_daki a day ago ago

    Really useful introduction! Working with low-level hardware APIs is challenging but rewarding. The abstraction layers in modern OS make it easier but understanding what's underneath is invaluable.

  • tosti a day ago ago

    The C++ looks messed up. I have yet to come across a keyboard that can type an arrow.

    • quietbritishjim a day ago ago

      It's just a programming font ligature. If you copy and paste it you'll see the actual characters e.g.

         auto main() -> int {
      
      (It's also modern C++ trailing return type.)
      • Ballas 8 hours ago ago

        Why would anybody think more words more better?

            int main() {
        • TheSoftwareGuy 5 hours ago ago

          The trailing return type pattern was added to the standard, IIRC, to make it easier for templated functions to have return types that depend on the types of the arguments, such as in this example:

              template <typename A, typename B>
              auto multiply(A a, B b) -> decltype(a * b) {
                  return a * b;
              }
          
          Its easier for the compiler to parse everything if `decltype(a * b) occurs _after_ the definition of `a` and `b`. Once this pattern was added and people started using it for that purpose, people also started using the pattern for all functions for consistency.
      • voidUpdate 11 hours ago ago

        I enjoy that because I have my browser monospace font set to be one that also has those ligatures, your comment isn't enlightening at all (I set it up that way though, so it's not a problem for me :P )

    • Something1234 a day ago ago

      Some developers like ligature based fonts. They combine 2 characters into one glyph

      • tosti a day ago ago

        Thank you and the others who were kind enough to explain this. I've avoided such fonts like the plague. Didn't know it did arrows like that.

    • yellowapple a day ago ago

      Any keyboard can type “→” if you set up a compose key :)

    • bheadmaster a day ago ago

      It's just "->" - the ligature font just renders it as a unitary arrow

  • infogulch 4 hours ago ago

    How about webusb?

    • WerWolv 3 hours ago ago

      WebUSB works in pretty much the same way. You'll have the same set of functions that libusb exposes there too.

      Sadly it's not supported at all on anything but Chrome right now

  • brcmthrowaway 19 hours ago ago

    Dumb question.. do USB devices support DMA? Is it done through the host? Or does the USB device always push/pull data to host memory?

    • kam 18 hours ago ago

      USB devices cannot directly address host memory like PCIe or FireWire, but the XHCI controller does DMA to/from host memory, and most USB device controllers have some kind of DMA between USB and the device's RAM.

    • pjc50 11 hours ago ago

      All transfers are initiated by the host, including ones that look like they're client-first; there is no DMA, which would be a massive security pain.

  • varispeed 21 hours ago ago

    Ages ago when I was trying to create a simple USB device, I found that there is very much zero information how to do it - e.g. how to correctly write descriptors and so on. The typical advice was: find similar device to what you want to make, copy its descriptors and adapt to your own device using trial and error.

    Sounds like USB is a wonderful standard. Am I wrong?

    • WerWolv 12 hours ago ago

      Descriptors also were kind of a mystery for me until I realized that they're just a binary structure with a fixed format that the host reads and interprets.

      The device descriptor is easy enough to get right as it doesn't have too many fields and every USB class just defines in the specification which Class and SubClass it uses for its interface descriptor as well as which endpoints that interface needs to have. And that's, for the most part, all you need for the host to recognize your device

    • 15155 12 hours ago ago

      USB is nice, but electrically some parts of USB 1/2 are kind of complicated (not true differential signaling.)

    • pjc50 11 hours ago ago

      Eh, there's very little tutorial content, but as far as big corporate standards go it's fairly reasonable. There is a downside to "too much choice", in that you have to read a lot to find the most relevant pre-defined type of device to what you're doing.

  • analog31 a day ago ago

    >>> Say you’re being handed a USB device and told to write a driver for it.

    Hand it back, with a request to prove that it can't be done with one of the devices that the OS's already recognize as virtual COM ports.

  • brcmthrowaway 19 hours ago ago

    Does the adb tool use libusb or a kernel driver?

    • WerWolv 15 hours ago ago

      ADB uses libusb or WinUSB directly from userspace too yeah. On Windows you still need a .inf driver though because the WinUSB driver isn't getting loaded by default for Android devices because they're lacking the MS OS Descriptor

      • nottorp 11 hours ago ago

        In 99% of cases you don't need a kernel driver.

        I don't think anyone uses the "native" usb way on either windows or mac os, when it's so much easier to go through libusb.

        I've personally done a custom DFU updater for Mac OS some time ago by using libusb. It worked just fine(tm). I don't want to try and evaluate how long it would have taken to do it with the mac api.