Today I was going through some stuff in my desk’s locker and ran into this nice toy — a big red electronic panic button. It has been there for years but I never had a chance to use it because a company making those does not have a driver for OS X, and of course I couldn’t find one for Linux either. But pushing a big red button is always fun and this time I have decided to try and make it work. So I brought it home with me into my “lab” to see what I can do.
After plugging it into a USB port, it immediately showed up in a list of USB devices (which you can see with lsusb tool). My Linux box has identified it as a Dream Cheeky Stress/Panic Button:
Bus 007 Device 010: ID 04f3:04a0 Elan Microelectronics Corp. Dream Cheeky Stress/Panic Button
Meanwhile, the kernel has also logged a message saying that it has found a new USB device. So the magic button seemed somewhat operational and I already knew two important things about it — a vendor and a product IDs. They were 0x04f3
and 0x04a0
respectively.
Writing a USB driver
The only little detail missing was actually a driver. I tried to Duck Duck Go it real quick but nothing showed up, and I decided to write it myself. How hard can it be, after all?
Reverse Engineering
In order to write a device driver, one must know what the device is doing. Obviously, I didn’t have any specification, neither I wanted to deal with Elan Microelectronics support department. So the only way to figure it out was reverse engineering. Luckily, Linux has usbmon — a facility in the kernel that is used to collect traces of I/O on the USB bus. After a quick pick at its documentation, I loaded the module using modprobe usbmon
command and the device showed up in /sys/kernel/debug/usb/devices
. Since it was attached to bus #7, I traced the I/O by reading the /sys/kernel/debug/usb/usbmon/7u
file and pushing my panic button a few times to see if anything shows up. And every time I hit the button, the device was sending the following data to the host:
1 2 3 4 5 |
|
Not only this proved that button itself works, it also uncovered a pattern — the device was sending 02000000
and 06000000
twice with 06001300
in between. So 06001300
seemed like a good indication of the button being pressed.
Linux Kernel USB Driver
The next step was to write a USB Device Driver for Linux. I dealt with PCI Express, DMA and Ethernet network drivers, but never wrote a single USB driver before. So I pulled my copy of the Linux Device Drivers book off the shelf and opened it on Chapter 13 “USB Drivers”. I have to say I got surprised — that rant is about 100 pages long. I would of course suck it up, enjoy the reading and proceed to hacking. If only it was something serious. And what I had was a simple USB button that did nothing but sending two 32-bit integers, 0x06 and 0x13, every time it gets hit. Going through the hundred pages just to read 64-bit of data off the USB cable, on Monday night, after a hard 10 hours working day… Ain’t nobody got time for that?
PyUSB
A bit disappointed, I tried to find an easier way and once against ducked for a few keywords like “USB”, “driver”, “read”, “HID” and so on…
Ask and it will be given to you; seek and you will find; knock and the door will be opened to you. — Matthew 7:7
Turned out, it is possible to write a USB driver in just a few lines of Python. Sir Micah Carrick have managed to make a driver for his credit card reader and wrote about his experience in this nice article. Following in his footsteps, I quickly glanced at PyUSB that he was using in his work, read a few other examples, and came up with a driver for my magic button. This is truly the shortest and simplest device driver that I have made in my life:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
So now I have my beautiful toy working!
UPDATE
Drew Fustini have hooked this up to the BeagleBone Next-Gen and made this video: