Close

Trouble with the arrows

A project log for USB Crank-Stick

Crank-based game controller

pixjuanPixJuan 08/06/2020 at 08:250 Comments

So, I got my CrankStick working with Sabotage on my emulator, but I want to use it with other games.
A game controller is exepected to behave like a usb joypad(on my TODO list) or to behave like a keyboard and send arrows key codes + Alt/Ctrl.
I'm already sending 'D' and 'F' key codes, so it should be easy to send 🡸 and 🡺, right?
But what 8 bit values should I send to say 'left arrow' ? I just have to RTFM to know how it works
On the Arduino Keyboard lib page, it mentions that   "Not every possible ASCII character, particularly the non-printing ones, can be sent with the Keyboard library" so I just followed a link and found this :

  KEY_UP_ARROW 0xDA1
  KEY_DOWN_ARROW 0xD9
  KEY_LEFT_ARROW 0xD8
  KEY_RIGHT_ARROW 0xD7 

Easy, I just have to use that code,no ? Well... it doesn't seem to work.
So what should you do when reading the documentation doesn't work? Look at the code!
I looked at Keyboard.cpp in the Arduino IDE directory

size_t Keyboard_::press(uint8_t k) {
    uint8_t i;
    if (k >= 136) {            // it's a non-printing key (not a modifier)
        k = k - 136;
        ...
        _keyReport.keys[i] = k;
        ...
        sendReport(&_keyReport);

Sending 0xDA to Keyboard.press() will change it to 0xDA - 136 = 0x52 before sending it to the low level function,
which according to USB HID standard is "up arrow". That should work, but it doesn't...

I also wanted to send 'Enter'(0x28 in USB keycode) and noticed in was in _asciimap, so I send the corresponding value and it worked.
I tried to modify the _asciimap variable to send arrow keycodes with unmapped characters but it still didn't work!
Ok Fine, I just wrote some obscenity in Keyboard.cpp to see if it was compiled at all... It turned out this file wasn't compiled at all in my case. Ok, fair enough that's not something you're supposed to play with and anyway it wasn't a proper solution
to modify this file.

DuckDuckGo didn't provide me any obvious solution, so I used the strings command on most of the binary files in my Arduino directory, still no luck I couldn't find ReleaseAll, which was the most unique function name of the lib. I found the 'release' keyword in usb_keyboard.c but though it was for the usb host support.
So what to do when you can't find the information neither in the documentation nor in the source code? Disassemble it ;-)
On Linux, Arduino projects are built in the /tmp/arduinoXXX dir, They are built as elf files and then converted to bin/hex
to be sent through the serial port. So I loaded my elf file in Ghidra and noticed that the real function called in my code was usb_keyboard_press_keycode() which is in fact present usb_keyboard.c

// Input can be:
//     32 - 127     ASCII direct (U+0020 to U+007F) <-- uses layout
//    128 - 0xC1FF  Unicode direct (U+0080 to U+C1FF) <-- uses layout
// 0xC200 - 0xDFFF  Unicode UTF8 packed (U+0080 to U+07FF) <-- uses layout
// 0xE000 - 0xE0FF  Modifier key (bitmap, 8 keys, shift/ctrl/alt/gui)
// 0xE200 - 0xE2FF  System key (HID usage code, within usage page 1)
// 0xE400 - 0xE7FF  Media/Consumer key (HID usage code, within usage page 12)
// 0xF000 - 0xFFFF  Normal key (HID usage code, within usage page 7)

void usb_keyboard_press_keycode(uint16_t n)

Great! I have a documentation explaining me clearly what to send to the function so I just send KEY_LEFT (defined as  80  | 0xF000) it should work...Sill not working!

Looking at the compiled code I noticed that the parameter was stored in r0 and truncated to 8 bits before being used!

        000007b8 c8 b2           uxtb       r0,r1
        000007ba 23 60           str        r3,[r4,#0x0]=>encoder0Pos 
        000007bc 2a 60           str        r2,[r5,#0x0]=>keyJustPressed
        000007be 01 f0 6d ff     bl         usb_keyboard_press_keycode     

 
So Finally I directly called usb_keyboard_press_keycode() with the same keycode and it just worked!

After all this, I searched again on the web and found a page with some useful information but it was not obvious I had to use "the micro manager way" to accomplish what I was trying to do.

I struggled a lot but it's ok, because it reminded me just how powerful Ghidra is, and it is yet another incentive to try something else than the classic Arduino IDE, probably PlatormIO, as it was mentionned in a previous Hackaday post.

Discussions