Reverse Engineering Force Feedback for the Logitech Rumblepad 2 by Edgar Simo with help from the great Anssi Hannula Changelog 2008-Apr-3 Minor Tweaks 2008-Mar-21 Second Draft 2008-Mar-20 First draft 0. Basic Knowledge The standard force feedback devices tend to have two inputs: strong rumble and weak rumble. Normally the values are between 0 and 255 (0x00 and 0xff in hex). The different rumble modes are done all in software by the underlying OS. Being that simple, they aren't too hard to reverse engineer. 1. Getting Device Data 1.1 Finding the Device First step is finding out where the device is. You'll want to use lsusb: Sample Output: Bus 010 Device 001: ID 0000:0000 Bus 009 Device 001: ID 0000:0000 Bus 008 Device 021: ID 054c:0243 Sony Corp. Bus 008 Device 002: ID 0bda:8187 Realtek Semiconductor Corp. Bus 008 Device 001: ID 0000:0000 Bus 007 Device 002: ID 03f0:8604 Hewlett-Packard Deskjet 5440 Bus 007 Device 001: ID 0000:0000 Bus 005 Device 001: ID 0000:0000 Bus 003 Device 004: ID 05f3:0007 PI Engineering, Inc. Kinesis Advantage PRO MPC/USB Keyboard Bus 003 Device 003: ID 05f3:0081 PI Engineering, Inc. Kinesis Integrated Hub Bus 003 Device 001: ID 0000:0000 Bus 006 Device 002: ID 046d:0840 Logitech, Inc. QuickCam Express Bus 006 Device 001: ID 0000:0000 Bus 004 Device 004: ID 1a7c:0068 Bus 004 Device 002: ID 056a:0011 Wacom Co., Ltd Graphire 2 Bus 004 Device 001: ID 0000:0000 Bus 002 Device 001: ID 0000:0000 Bus 001 Device 017: ID 046d:c218 Logitech, Inc. Logitech RumblePad 2 USB Bus 001 Device 001: ID 0000:0000 From here you'll need to isolate the device: Bus 001 Device 017: ID 046d:c218 Logitech, Inc. Logitech RumblePad 2 USB 1.2 Descriptors To get the descriptors you need to know what event the device created in /dev/input. You can see this in dmesg. To figure out what you need to unbind run: basename $(readlink -f /sys/class/input/input#/device) input# should be the device that you get in dmesg like for example: [ 8357.783371] input: Logitech Logitech RumblePad 2 USB as /devices/pci0000:00/0000:00:1a.0/usb1/1-2/1-2:1.0/input/input8 Would mean it's /sys/class/input/input8/device. Run the command it'll return the value which you can then send to /sys/bus/usb/drivers/usbhid/unbind. For example if it returned '4-2:1.0' you'd run: echo -n '4-2:1.0' > /sys/bus/usb/drivers/usbhid/unbind This will unbind usbhid from the device allowing you to do raw usb commands, and in this case let lsusb find out about the descriptors. Now run lsusb -v to get the descriptor: You can see my output in the attached log. You'll be looking for something like: Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 Item(Global): Physical Maximum, data= [ 0xff 0x00 ] 255 Item(Global): Report Size, data= [ 0x08 ] 8 Item(Global): Report Count, data= [ 0x07 ] 7 Item(Local ): Usage, data= [ 0x02 ] 2 (null) Item(Main ): Output, data= [ 0x02 ] 2 Data Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Main ): End Collection, data=none Which means we have 7 blocks (report count) of 8 bits (0x00-0xff). The usage being 0x02 is nonsense since it returns (null). The output just tells us a bit about the data, which we don't really need to know. 1.3 HID Debug An alternative would be to compile the hid module with CONFIG_HID_DEBUG and then run: echo 1 > /sys/module/hid/parameters/debug replug the device and turn off debugging (it's very spammy) echo 0 > /sys/module/hid/parameters/debug You can reduce the spam if you use the hid-debug-noverbose.diff patch attached. Now you'll have to read the debug log and grab the beginning of the gamepad chunk which should be something like (it'll repeat, just grab what's before the first repitition). See the attached file rumblepad.debug for an example. It'll tell you the same stuff the descriptors tell you but more elegantly. What we saw above would be: [12523.892590] OUTPUT[OUTPUT] [12523.892593] Field(0) [12523.892595] Usage(7) [12523.892596] ff00.0002 [12523.892600] ff00.0002 [12523.892603] ff00.0002 [12523.892606] ff00.0002 [12523.892609] ff00.0002 [12523.892613] ff00.0002 [12523.892616] ff00.0002 [12523.892619] Logical Minimum(0) [12523.892621] Logical Maximum(255) [12523.892623] Physical Minimum(0) [12523.892625] Physical Maximum(255) [12523.892627] Report Size(8) [12523.892628] Report Count(7) [12523.892630] Report Offset(0) [12523.892632] Flags( Variable Absolute ) And would be read more or less the same way. 1.4 USB Snooping If you have access to a computer with windows, you should try getting the force feedback working on it there. Once you have it running, you'll have to install USBSnoop. Get it at http://www.pcausa.com/Utilities/UsbSnoop/default.htm . Now you'll need to be able to trigger force feedback when you want to. So head off to http://www.fs-force.com/support.htm and get the forcetest.exe. This will allow you to test only force feedback. Install UsbSnoop on your device, replug it, and start testing force feedback with the test app. Do that a bit and then save the log. Now you can see how they do it. The log will tell you about the URB the device used. You'll have to isolate in the log where the events happen. Some devices fill the logs with crap so you can't make much sense and it's hard to hunt. In our case it's pretty easy and we can figure out a few. Here is how you would do it: First isolate a URB (USB Request Block): [22402 ms] >>> URB 14 going down >>> -- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: PipeHandle = 84e6117c [endpoint 0x00000001] TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK) TransferBufferLength = 00000007 TransferBuffer = f7c4df61 TransferBufferMDL = 00000000 00000000: f3 00 00 00 00 00 00 UrbLink = 00000000 The important thing here is the buffer length and buffer itself. We see that the buffer matches the unknown descriptor we saw (length 7) and we see the data is 00000000: f3 00 00 00 00 00 00. We'll see how to send that to the device later on. Now we have to figure out different commands it sends related to force feedback. Here are a few examples: 00000000: f3 00 00 00 00 00 00 00000000: 51 00 12 00 00 00 00 00000000: 51 00 24 00 00 00 00 00000000: 51 00 44 00 00 00 00 00000000: 51 00 52 00 00 00 00 ... All are report 0, and it seems like there's multiple commands being sent. 2. Testing the device 2.1 Test setup Now you'll need to test the device, make sure you have /dev/hidraw support in your kernel (CONFIG_HIDRAW). This will create a /dev/hidraw device to which you'll be able to send data very easily to test the device. With all the information gather you can see that the Rumblepad 2 uses you can see it uses the same HID device to create rumble events, so we can actually use /dev/hidraw, some other devices could use different approaches forcing you to test with raw usb commands like with python-usb or libusb. First we have to find out what raw device our gamepad is using. We'll check dmesg: [ 5828.642065] input,hidraw3: USB HID v1.10 Joystick [Logitech Logitech RumblePad 2 USB] on usb-0000:00:1a.0-2 We see that we are using /dev/hidraw3. Now we can start sending commands to it. Syntax is: echo -en "0\x00\x00\x00\x00\x00\x00\x00" > /dev/hidraw3 The first number is the report number, the rest is the data. Since we think our device is using a buffer length of 7, we send 7 values afterwards. 2.2 Actually testing We'll need to use the events we saw in UsbSnoop and try to make sense of them, seeing what works and doesn't. so we'll try first: echo -en "0\xf3\x00\x00\x00\x00\x00\x00" > /dev/hidraw3 echo -en "0\x51\x00\xff\x00\x00\x00\x00" > /dev/hidraw3 and see if we get anything. Then we'll try variants of it with the first block constant like: echo -en "0\xf3\xff\x00\x00\x00\x00\x00" > /dev/hidraw3 echo -en "0\xf3\x00\xff\x00\x00\x00\x00" > /dev/hidraw3 ... echo -en "0\xf3\x00\x00\x00\x00\x00\xff" > /dev/hidraw3 and same with \x51 echo -en "0\x51\xff\x00\x00\x00\x00\x00" > /dev/hidraw3 echo -en "0\x51\x00\xff\x00\x00\x00\x00" > /dev/hidraw3 ... echo -en "0\x51\x00\x00\x00\x00\x00\xff" > /dev/hidraw3 You have to feel the device and see when it starts shaking. Now you'll have to try to isolate when and why it shakes. In our case we had success with many of the \x51 variants. 2.3 More in depth testing You'll also have to try combinations and such. I recommend you write a script, although you can do it manually too. After spending a while you should be able to figure out more or less how it works. In our case we were able to find out: "0\xf3\x00\x00\x00\x00\x00\xff" turns off rumble "0\x51\x00\xYY\x00\x00\x00\x00" is the weak rumble with values of YY from 0x00 to 0xff "0\x51\x00\x00\x00\xYY\x00\x00" is the strong rumble with values of YY from 0x00 to 0xff And we have it all figured out. 3. Adding the suport to linux TODO For now just send it to the force feedback kernel maintainers :). You can see the example driver Anssi made as hid-logitech-rumblepad2.diff.