Michael Hewner

Ruby USB

by Michael Hewner

In his talk at MountainWest RubyConf 2007, Michael Hewner discusses the theme of customizing USB devices using Ruby. He begins by connecting with the audience through humor related to Unix customization, highlighting the pride many programmers take in personalizing their environments. However, he warns against complacency in customization and suggests that there is a vast realm of possibilities beyond traditional software tweaks, particularly in the physical manipulation of hardware.

Hewner introduces the idea of controlling USB devices through Ruby, emphasizing that creating these devices can fulfill the creative impulses of hobbyist hackers. He elaborates on USB devices, explaining their self-describing properties, the advantages of Human Interface Devices (HID), and the simplified approach to employing Ruby to interface with these devices. Key points from his talk include:

- Customization of Devices: Embracing fun over utilitarian setups, like using a quirky USB interface to control media playback.

- USB Specifications: Insight into how devices signal what they are and their flexible configuration options.

- HID Communication Protocol: Understanding how HID devices transmit data and how Ruby can be utilized to interpret this data effectively.

- Practical Examples: He shares anecdotal evidence of his experimentation with a Toshiba LED Control Module remote control that he reverse-engineered for personal use.

- Technical Steps: Details about USB interactions in Ruby, including obtaining vendor and product IDs and managing device configurations.

- Challenges and Lessons Learned: Sharing insights into the development process of interfacing C++ with Ruby and the importance of unit testing.

Hewner concludes by encouraging the audience to explore DIY USB projects, emphasizing the joy and creativity that can be harnessed through technology. He invites collaboration on projects and expresses excitement for the future of integrating hardware with programming languages like Ruby, underscoring that the journey in technology should always revolve around fostering creativity and innovation among hackers and makers.

00:00:09.760 All right, so I'm Mike Hewner, and I'll talk to you about something that should be familiar to most of you.
00:00:15.040 You may need to permit me a little bit of Unix nerd humor here. If you look into any big Unix nerd's home directory and do something like 'ls .rc', you'll see that it's basically like a badge of honor. If you don't have 12 of these suckers in there, you are not cool.
00:00:25.680 If you examine one of these rc files, each line often has some sort of history to it—like the day they were supposed to be working on a project but instead spent eight hours customizing the perfect Zsh prompt.
00:00:31.679 You know, each one may come from someone else—like this one from an Australian at a conference. Hopefully, I’ll get a chance to trade some tips with you guys later on today. But all these files show how nerds often love customizing their stuff. You need everything tricked out, shiny, and polished. Just look how much work has gone into this thing! I can't even imagine the various processes and chemicals involved to get those wheels shiny, but one thing’s for sure: this sort of customization is a classic nerd thing.
00:01:07.600 However, I’ve felt recently that this kind of customization is a bit fraught with peril because, after a while, when you shine up the wheels and get everything working smoothly, it seems like you run out of things to customize.
00:01:18.399 Yes, you might have a few spam emails come in, which probably means you need to add a few lines to your procmail.rc, and maybe you could find a couple of Vim plug-ins that could revolutionize your life, but it's hard to envision what they might do.
00:01:29.920 Then, if you're an Emacs user, you've probably reached the point where you never leave Emacs for any reason. You might even have a command to use the restroom. Everything starts feeling pretty good and normal, but soon you realize you're done, and your computer seems to work great. But the question becomes: what do you do now? You might notice people hanging out at your house, possibly related to you. Maybe you could consider talking to them—apparently, religion is good. Ruby is probably something you know about to some extent.
00:02:03.600 Or you could take the approach of saying, 'Screw all that! I'm going to make something that essentially works and add a fishbowl to it.' So, in my opinion, you're not done. What you might not realize is that while you've done all these amazing customizations on the screen of your computer, there's still so much more you could be doing in the actual physical world.
00:02:29.280 Instead of having shiny, perfect devices, you could incorporate more bizarre gadgets, which might seem odd at first. But you could create something unique, like a USB device that lets you switch between desktops just by twiddling it around. That’s the concept I’m presenting here.
00:02:43.680 This isn't about USB for hardcore hardware enthusiasts who are building devices that normal people actually want. No, this is USB for hackers who want to take that mouse they have in their closet and turn it into something that plays a tune when you jiggle it around or something equally fun. This isn’t a project for work; it’s just for the fun of it.
00:03:05.920 The idea is that you can control USB devices using Ruby. I mean, Bluetooth would be even cooler, but I didn’t have time to look into that. USB devices are my focus because the main reason is that if your primary source of hardware comes from, say, the Best Buy bargain bin—you can pretty much bank on it being cheap and available.
00:03:29.920 Also, USB devices are self-describing. For instance, when you plug that mouse from the bargain bin into your system, it’s quite satisfying when your operating system recognizes it as a 'Japanese character mouse' or whatever, and you think, ‘Good, I think it’s going to happen for me.’ Clearly, there’s something in there that lets the device know what it is.
00:03:45.440 Those of you who remember back to the days before USB devices will recall the hassle of plugging in a serial mouse and then having to search for the right driver. You would plug it in, and it wouldn’t work properly, leading to countless uninstalls and reinstalls, only for it still to malfunction. Thankfully, that’s no longer the case with USB. These devices can convey a lot of information about themselves, and particularly Human Interface Devices (HID), like your USB printer or a thumb drive, can tell your computer exactly what they are.
00:04:14.560 Now, HID devices can often have a lot of strange features and are quite flexible—sometimes non-standard—and surprisingly, still functional. Moreover, while this next part doesn’t relate directly to Ruby, you can actually build your own USB devices. I’ll touch on that later, but I assure you it is simple enough that even someone like me can manage the soldering required.
00:04:47.680 The USB specification comes with various subspecifications. Just like any good spec, there are subspecs deep within subspecs. The main USB spec addresses how devices plug into hubs, whether they are powered or not, and introduces the concept of configurations. If anyone has a USB device that actually uses configurations, I want to hear from you because I have never found one that does, nor have I tested this aspect of Ruby USB.
00:05:14.599 Configurations can dictate a device's behavior based on whether it receives external power or not, and it can respond differently based on its powered status. Within a USB device definition, you can have multiple interfaces and more than one configuration. As examples, you can have a thumb drive and a salad shooter on a single USB device—where one conforms to a thumb drive specification and the other follows the salad shooter spec.
00:05:50.560 The most interesting aspect of USB that I’ll be discussing today pertains to HID. As I mentioned, it’s very flexible. If you want to know more, please check out my website at rubyusb.technofetish.net, where you’ll find all the links for this presentation along with all the documents I reference.
00:06:08.720 The goal for Human Interface Devices (HID) has always puzzled me due to its design choices at the specification level. HID devices send data in somewhat unconventional formats, but beforehand, they provide a description of the data they will send. You’d think any device could benefit from that finessed feature, but it strangely appears to be a quirk of the HID spec. It allows for interesting details regarding the format in which the data will be sent, which is beneficial.
00:06:35.760 This feature is significant because when you connect your interface device to your computer and then request data from it with Ruby, it first looks at that handy little document detailing everything about the data sent, allowing Ruby to interpret it accurately. For instance, you can analyze the seven different numbers the device sent, what they mean, and how they correspond to certain functions.
00:07:06.720 Let’s look at how you used to reverse-engineer some obscure device by staring at a random stream of binary data on your screen. You'd see a string of zeros and wonder what was being pressed at that exact moment. The disconnect was often baffling.
00:07:21.440 Imagine an HID's data stream on the far left indicating left mouse button activity: that magic number denotes the left mouse button you recognize, which is vital for clicking icons. Then there are specifics like the chaff release button for joysticks, which, believe it or not, is included in the HID spec. It's great to know that all manufacturers conform to this standard, even if I doubt many people utilize them.
00:07:52.880 However, the specs can get quite complicated. For instance, I can indicate groups of numbers and clarify their meanings through annotations—though, unfortunately, they don’t seem accessible through any known programmer interface—I’ve searched and never discovered how to access those annotations. Often you find little more than overwhelming voids of zeros.
00:08:27.680 Going deeper into USB specs, you might find what’s coming back, such as unexpected units or surprising ranges of values being shared without the specs indicating their significance. We can even mention the presence of negative numbers that would pose a challenge to decipher during reverse engineering, but having a spec helps mitigate this frustration.
00:08:43.120 Let’s address how to handle USB devices via Ruby. Below, you can see an example of a mouse descriptor’s beginning, expressed in hexadecimal code. It is essential to understand various terms I'd like to touch upon if you ever decide to hack on USB devices.
00:09:10.560 In terms of usage, there’s something called usage page—the one that indicates what types of devices people might want to look for—for instance, the left mouse button usage might have a distinct designation as part of this. Meanwhile, the keyboard key usage belongs to a similar categorization.
00:09:36.720 The USB spec introduces collections for grouping multiple functions and defines logical minimums and maximums—useful for range specifications. Additionally, the report count and report size give you insight into how the data is structured in a given context. You may find this detail-oriented, but Ruby USB helps to figure all of this out for you, preventing the need to struggle with complex codes.
00:10:06.960 So, now let’s get into some more technical details. When you want to work with these devices, there are a few necessary steps you must take. Obviously, you have to load your libraries carefully—it can be somewhat tricky, especially when you also need elevated permissions due to Linux’s USB device ownership criteria.
00:10:34.080 You may need to run prompts with pseudo commands to ensure proper permission and access to the devices. Once you manage that, you can get a list of connected USB devices using Ruby—generating outputs with descriptions of each device connected. For instance, I had an optical mouse and a keyboard connected, alongside a USB hub.
00:10:58.560 Following that, you can use vendor and product IDs to uniquely identify each connected device. Working off those parameters, you can retrieve information like the device's name and manufacturer. This is where it gets interesting; you’re also able to check if it’s a HID by evaluating the device’s configurations.
00:11:24.560 The magic inquiry begins when I grab my optical mouse, which Microsoft calls a basic optical mouse, and proceed to get the first interface. This is mainly for brevity since the reality is you would typically inspect all interfaces of a device to locate the specific HID interface it implements.
00:11:49.679 In practice, most devices typically only implement one interface; however, theoretically speaking, devices can certainly be multifaceted. We haven’t encountered performance issues as a general rule because system-level APIs and specifications govern how an HID functions. But remember, it’s crucial to detach the device from the kernel before proceeding with any interesting actions.
00:12:12.480 Once you detach the device from the kernel, keep in mind that it won’t automatically reattach, leading to situations where your mouse or trackpad may stop functioning. You would need to unplug and replug to refresh that connection.
00:12:38.960 After detaching, you can assess the device’s functionality by querying all input usages associated with it. You can effortlessly get a list of functionalities—each of which corresponds to different buttons or features present on the device. You can imagine picking a keyboard and realizing that it supports an extensive array of keys.
00:13:07.680 Let’s not forget about potential interruptions. It’s imperative to initiate listening for data from your USB device so that when it begins receiving input, you’ll have access to that exciting binary string data returned, showing you parameters like button activity—what’s on, off, coordinates, and whatnot.
00:13:32.799 I’ll demonstrate with my latest USB project, a surprisingly ghetto remote control I discovered at a thrift store, labeled as a Toshiba LED Control Module. The purpose of this device remains unclear to me, but with some creative problem-solving, I was able to reverse-engineer its communication protocol in about an hour and integrate that with VLC to function as a remote.
00:14:03.119 It’s important to note that the following code segment shows the USB aspects, where I locate the device, get its endpoint, detach it from the kernel, and listen for HID interrupts while successfully retrieving button codes to operate commands just as I intended.
00:14:36.799 Using VLC—a video player ideal for Linux and very Unix-like—I enjoy watching anime while easily controlling the volume and playback through the custom remote, showing just how effectively such integrations can work.
00:15:06.240 Addressing the operational layers of my setup helps clarify how it functions. I can describe it as a three-layer system. It begins with Lib USB, a cross-platform USB library designed primarily for Unix-like systems, including Mac OS X.
00:15:27.440 However, keep one thing in mind: Windows versions may be a bit quirky! When interfacing with C code, you usually think of reliable, solid foundational layers, but Ruby plus Lib USB results in some erratic behavior due to low-level operations.
00:15:52.480 This leads us to an aspect of the interface which could sometimes reveal unpredictable timeouts—an important detail since Ruby users expect a stable interface. I’ve primarily tested this on Linux systems, which creates complications if someone were to attempt this on different environments.
00:16:14.399 In reviewing the interface, you’ll naturally want to see how it accommodates querying a USB device for its characteristics by filling up its C structs to provide data and, ideally, navigate the complexity more smoothly.
00:16:46.799 I’ll share one significant downside of developing this; I wrote my HID parser in C++ thinking I’d create something reusable, but I learned it became an enormous pain while interfacing with Ruby.
00:17:07.679 Had I to do it again, I’d build it purely in Ruby for a more refined interface accessing USB’s report descriptor objects. These describe the different potential data types—a critical feature but managing devices in C++ created complexities beyond my expectations.
00:17:31.199 Among the lessons learned—always implement unit tests! This should be standard practice for any tech and ensures that as you add functionality, the core features remain intact. When you’re implementing without ample resources, you’ll inevitably encounter challenges.
00:17:55.280 If you want to implement unique setups without strict USB nuances, I’d recommend exploring the EVDev framework for Ruby, which is Linux-based and conveys exceptional performance. However, it lacks compatibility with other operating systems.
00:18:25.440 Lastly, I really value collecting data from USB devices and using that information in a constructive manner, whether that’s to manipulate settings or enable their features as you see fit. We still have a lot of advancements left ahead in terms of functionality.
00:18:54.800 While I haven’t worked extensively with Ruby threads in multi-threaded programs—my experience has shown listening for interrupts can leverage threads safely—there are bound to be bugs unless extensively tested.
00:19:13.440 It’s all not just theory; it's got practical applications you've likely used every single day. I even explored a quirky idea once of gluing two keyboards together—one in insert mode and one not.
00:19:34.880 I was curious about the complexity of input but ended up with two keyboards dominating my desk space. Despite its goofiness, it was perfectly functional—and you can always experiment with configurations.
00:19:56.240 What I strive for next is efficiency, perhaps developing customized devices or programming new ones with flexible features, especially if they can easily integrate with Ruby. Creating DIY USB devices is thrilling, and who knows what innovative functionalities we could see emerge!
00:20:30.000 If anyone feels inclined to help me brainstorm or work on a USB chainsaw project, I’m up for it! I’m always keen on using AVR programming to create fantastic USB devices that work with Unix.
00:20:59.680 Though I faced some hiccups during soldering my USB device, I still see potential in future integrations with new hardware. Please anticipate even more exciting things to come as I continue to develop and share projects relating to Ruby and USB interfacing.
00:21:25.200 Thank you for your time, and feel free to ask any questions about what we’ve discussed today or any projects you might have in mind. I'm happy to provide suggestions or insights if you have ideas you want to run through!
00:21:57.120 I realized that utilizing these systems isn’t solely about creating fun projects; it’s about embracing creativity and keeping aspirations alive through technology—it’s what drives the community of hackers and makers forward.