How does one handle input from a character device/gamepad directly in Linux systems?
Asked Answered
C

2

7

I am developing a program in C that uses a USB SNES controller for input on a RPM based distribution. Is there a library anyone knows of that makes this easier to interact with, or some tool (joydev?) that allows to properly read input from the device? I do not need an entire game engine; it is only for input from the character device.

If there's a library that already does this for me, that would be excellent (I can just see what the library does on my own) and this can be closed with just a link; otherwise, if I have to do this myself, I have a few specific questions:

The questions:

  • Is there an already existing C library that handles all of my USB device interactions to a gamepad for me? I'll gladly learn a new library. (My google-fu has failed me here, I apologize if this is excessively obvious)
  • What is the appropriate way to ensure you open the proper character device every time, as the event* name changes between sessions/initialization?
  • What is the appropriate method to handle input from these devices from my application? Just determine what each button press amounts to value-wise and perform actions based on that input when we poll over the character device?

In brief pseduo-C, something like this?

struct gamepad {
    int fd;
};

void get_input(struct gamepad *gamepad)
{
    char *buf;
    read(gamepad->fd, buf, 48);
    switch(buf)
    {
        /* insert cases about handling differing buttons */
    }
}

How the device presents itself:

I can clearly see the device registers properly as far as I can see:

$ dmesg | tail
[198564.517068] usb 1-1: USB disconnect, device number 17
[198566.154324] usb 1-1: new low-speed USB device number 18 using xhci_hcd
[198566.323309] usb 1-1: New USB device found, idVendor=12bd, idProduct=d015
[198566.323312] usb 1-1: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[198566.323313] usb 1-1: Product: 2Axes 11Keys Game  Pad
[198566.323792] usb 1-1: ep 0x81 - rounding interval to 64 microframes, ep desc says 80 microframes
[198566.328692] input: 2Axes 11Keys Game  Pad as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/input/input20
[198566.329036] hid-generic 0003:12BD:D015.0006: input,hidraw2: USB HID v1.10 Gamepad [2Axes 11Keys Game  Pad] on usb-0000:00:14.0-1/input0

And if I read from the device, I can see that it receives an interrupt and input from the device just be using hexdump:

$ ls -lattr /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick 
lrwxrwxrwx. 1 root root 10 Jan 20 15:56 /dev/input/by-id/usb-12bd_2Axes_11Keys_Game_Pad-event-joystick -> ../event17

When I press a key (not releasing) it appears to work as expected although I cannot decipher what is being returned from the buffer without context as of yet:

$ hexdump /dev/input/event17 
0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000

On release of a button, you receive similar output:

0000030 f53c 569f 0000 0000 8be3 0007 0000 0000
0000040 0003 0001 007f 0000 f53c 569f 0000 0000
0000050 8be3 0007 0000 0000 0000 0000 0000 0000

That is the 'up' button being pressed and released above.

The research:

In an effort to determine how other libraries do this, I thought about doing an strace of pygame in python and seeing what devices it opens as well as how it reads input, but I'm still learning through how to use it. I also saw some vague mentions concerning joydev, but, again, haven't learned how to use them. I'm doing that currently and will post results if I learn anything of value.

Beyond that, watching button presses via ASCII and hexdump, I note that they have a similar input based on button, but there appears to be what I figure is a count of interrupts for the USB bus at the end of the output above (0xf53a to 0xf53c). This always appears to increment as well, and, for my purposes, can probably be discarded.

There is also the possibility I'm just not mounting the device correctly because I'm missing some module or package (thinking, again, about joydev and what it is supposed to do). I have not worked often with USB at all so this type of device handling is new to me.

Searching around for a while, I didn't see anything that showed exactly what I was looking for, but I gladly accept redirects to other questions/topics for reading.

Thank you in advance!

Cruller answered 20/1, 2016 at 21:44 Comment(2)
Most games/frameworks (including pygame) would end up using libsdl.Tallowy
Once upon a time, about 15 years ago, I wrote a quick little Python library for interpreting raw evdev events. No idea what happened to it, or if it ever became public (was done in the context of job-related QA automation), but... it's really not hard at all; once you have the format, the struct module does a fine job of decoding them.Actinomycin
P
5

USB input devices in Linux are usually handled by the HID driver (Human Interface Device), which in turn are converted into input devices.

You can read them as raw USB devices, but that is not usually a very good idea, because it is a very low level protocol.

You can read the /dev/input/* devices, if you have the proper permissions. Usually they are readable only by root. If you do not want to read the raw bytes, there are some libraries, such as libinput, that do that job for you.

But if your game works in XWindows (most likely), then you should manage the XInput devices. You can do that with raw X calls, but you are probably better using some library such as SDL. Actually SDL is what pygame uses under the hood, so I'd try that first.

About how to identify the proper device, every input device has a name, and some even a serial number (you see those as the symlink under /dev/input/by-id These are usually enough to identify the device, instead of the input number.

If you want to read the raw input devices, let me explain a bout about your hexdumps. You are reading the input* devices, so you are getting values of type struct input_event (see /usr/include/linux/input.h):

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

In your first dump, for example:

0000000 f53a 569f 0000 0000 ac6c 000c 0000 0000
0000010 0003 0000 007f 0000 f53a 569f 0000 0000
0000020 ac6c 000c 0000 0000 0000 0000 0000 0000

There are actually two input_events. The first one is:

f53a 569f 0000 0000 ac6c 000c 0000 0000 0003 0000 007f 0000

The first 64 bytes is the timestamp. Then, 0003 (EV_ABS) means a movement of an absolute axis, 0000 (ABS_X) is the axis index and 0000007f is the position of that axis. Absolute axes are sometimes used to represent throttles, joysticks and the like (sometimes keypads are sent as a joystick instead of 4 buttons), and you get the position on first read to know where the control is, even if you do not move it.

The second event is:

f53a 569f 0000 0000 ac6c 000c 0000 0000 0000 0000 0000 0000

The first 64 bytes is the timestamp (same as above. Then 0000 (EV_SYN) means a synchronization event. With EV_SYN, the other fields are unused. EV_SYN is used to group together the different values of a single event, such as the horizontal and vertical axes of a mouse or a joystick.

Your other dump is similar but for AXIS_Y.

My guess is that the keypad is being treated as a digital joystick, two axes ABS_X and ABS_Y, being 0x7F the middle point (range from 0x00 to 0xFF). The messages you are getting are simply the middle point, that is no keypad button pressed. Maybe your hexdump output is being buffered?

Paulson answered 20/1, 2016 at 22:13 Comment(0)
H
1

It is an USB Hid device, so it is handled by the hid driver - see in your debug output "USB HID v1.10 Gamepad" There are various tutorials and examples out how to do it.

As starting point, you could install joystick over "apt-get install joystick" and have a look on the source code.

Also libhidapi is a good choice.

Hakon answered 20/1, 2016 at 21:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.