13

I'm working on an embedded Linux project where I will be developing a program that will run automatically on bootup and interact with the user via a character display and some sort of button array. If we go with a simple GPIO button array, I can easily write program that will look for keypresses on those GPIO lines. However, one of our thoughts was to use a USB number pad device instead for user input. My understanding is that those devices will present themselves to the OS as a USB keyboard. If go down this path, is there a way for my program to look for input on this USB keyboard from within Linux, keeping in mind that there is no virtual terminal or VGA display. When a USB keyboard is plugged in, is there an entity in '/dev' that appears that I can open a file descriptor for?

24

Devices most likely get a file in /dev/input/ named eventN where N is the various devices like mouse, keyboard, jack, power-buttons etc.

ls -l  /dev/input/by-{path,id}/

should give you a hint.

Also look at:

cat /proc/bus/input/devices

Where Sysfs value is path under /sys.

You can test by e.g.

cat /dev/input/event2 # if 2 is kbd.

To implement use ioctl and check devices + monitor.

EDIT 2:

OK. I'm expanding on this answer based on the assumption /dev/input/eventN is used.

One way could be:

  1. At startup loop all event files found in /dev/input/. Use ioctl() to request event bits:

    ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
    

    then check if EV_KEY-bit is set.

  2. IFF set then check for keys:

    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), &keybit);
    

    E.g. if number-keys are interesting, then check if bits for KEY_0 - KEY9 and KEY_KP0 to KEY_KP9.

  3. IFF keys found then start monitoring event file in thread.

  4. Back to 1.

This way you should get to monitor all devices that meet the wanted criteria. You can't only check for EV_KEY as e.g. power-button will have this bit set, but it obviously won't have KEY_A etc. set.

Have seen false positives for exotic keys, but for normal keys this should suffice. There is no direct harm in monitoring e.g. event file for power button or a jack, but you those won't emit events in question (aka. bad code).

More in detail below.


EDIT 1:

In regards to "Explain that last statement …". Going over in stackoverflow land here … but:

A quick and dirty sample in C. You'll have to implement various code to check that you actually get correct device, translate event type, code and value. Typically key-down, key-up, key-repeat, key-code, etc.

Haven't time, (and is too much here), to add the rest.

Check out linux/input.h, programs like dumpkeys, kernel code etc. for mapping codes. E.g. dumpkeys -l

Anyhow:

Run as e.g.:

# ./testprog /dev/input/event2

Code:

#include <stdio.h>

#include <string.h>     /* strerror() */
#include <errno.h>      /* errno */

#include <fcntl.h>      /* open() */
#include <unistd.h>     /* close() */
#include <sys/ioctl.h>  /* ioctl() */

#include <linux/input.h>    /* EVIOCGVERSION ++ */

#define EV_BUF_SIZE 16

int main(int argc, char *argv[])
{
    int fd, sz;
    unsigned i;

    /* A few examples of information to gather */
    unsigned version;
    unsigned short id[4];                   /* or use struct input_id */
    char name[256] = "N/A";

    struct input_event ev[EV_BUF_SIZE]; /* Read up to N events ata time */

    if (argc < 2) {
        fprintf(stderr,
            "Usage: %s /dev/input/eventN\n"
            "Where X = input device number\n",
            argv[0]
        );
        return EINVAL;
    }

    if ((fd = open(argv[1], O_RDONLY)) < 0) {
        fprintf(stderr,
            "ERR %d:\n"
            "Unable to open `%s'\n"
            "%s\n",
            errno, argv[1], strerror(errno)
        );
    }
    /* Error check here as well. */
    ioctl(fd, EVIOCGVERSION, &version);
    ioctl(fd, EVIOCGID, id); 
    ioctl(fd, EVIOCGNAME(sizeof(name)), name);

    fprintf(stderr,
        "Name      : %s\n"
        "Version   : %d.%d.%d\n"
        "ID        : Bus=%04x Vendor=%04x Product=%04x Version=%04x\n"
        "----------\n"
        ,
        name,

        version >> 16,
        (version >> 8) & 0xff,
        version & 0xff,

        id[ID_BUS],
        id[ID_VENDOR],
        id[ID_PRODUCT],
        id[ID_VERSION]
    );

    /* Loop. Read event file and parse result. */
    for (;;) {
        sz = read(fd, ev, sizeof(struct input_event) * EV_BUF_SIZE);

        if (sz < (int) sizeof(struct input_event)) {
            fprintf(stderr,
                "ERR %d:\n"
                "Reading of `%s' failed\n"
                "%s\n",
                errno, argv[1], strerror(errno)
            );
            goto fine;
        }

        /* Implement code to translate type, code and value */
        for (i = 0; i < sz / sizeof(struct input_event); ++i) {
            fprintf(stderr,
                "%ld.%06ld: "
                "type=%02x "
                "code=%02x "
                "value=%02x\n",
                ev[i].time.tv_sec,
                ev[i].time.tv_usec,
                ev[i].type,
                ev[i].code,
                ev[i].value
            );
        }
    }

fine:
    close(fd);

    return errno;
}

EDIT 2 (continued):

Note that if you look at /proc/bus/input/devices you have a letter at start of each line. Here B means bit-map. That is for example:

B: PROP=0
B: EV=120013
B: KEY=20000 200 20 0 0 0 0 500f 2100002 3803078 f900d401 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7

Each of those bits correspond to a property of the device. Which by bit-map means, 1 indicate a property is present, as defined in linux/input.h. :

B: PROP=0    => 0000 0000
B: EV=120013 => 0001 0010 0000 0000 0001 0011 (Event types sup. in this device.)
                   |   |               |   ||
                   |   |               |   |+-- EV_SYN (0x00)
                   |   |               |   +--- EV_KEY (0x01)
                   |   |               +------- EV_MSC (0x04)
                   |   +----------------------- EV_LED (0x11)
                   +--------------------------- EV_REP (0x14)
B: KEY=20... => OK, I'm not writing out this one as  it is a bit huge.

B: MSC=10    => 0001 0000
                   |
                   +------- MSC_SCAN
B: LED=7     => 0000 0111 , indicates what LED's are present
                      |||
                      ||+-- LED_NUML
                      |+--- LED_CAPSL
                      +---- LED_SCROLL

Have a look at /drivers/input/input.{h,c} in the kernel source tree. A lot of good code there. (E.g. the devices properties are produced by this function.)

Each of these property maps can be attained by ioctl. For example, if you want to check what LED properties are available say:

ioctl(fd, EVIOCGBIT(EV_LED, sizeof(ledbit)), &ledbit);

Look at definition of struct input_dev in input.h for how ledbit are defined.

To check status for LED's say:

ioctl(fd, EVIOCGLED(sizeof(ledbit)), &ledbit);

If bit 1 in ledbit are 1 then num-lock are lit. If bit 2 is 1 then caps lock is lit etc.

input.h has the various defines.


Notes when it comes to event monitoring:

Pseudo-code for monitoring could be something in the direction of:

WHILE TRUE
    READ input_event
    IF event->type == EV_SYN THEN
        IF event->code == SYN_DROPPED THEN
            Discard all events including next EV_SYN
        ELSE
            This marks EOF current event.
        FI
    ELSE IF event->type == EV_KEY THEN
        SWITCH ev->value
            CASE 0: Key Release    (act accordingly)
            CASE 1: Key Press      (act accordingly)
            CASE 2: Key Autorepeat (act accordingly)
        END SWITCH
    FI
END WHILE

Some related documents:

  1. Documentation/input/input.txt, esp. note section 5.
  2. Documentation/input/event-codes.txt, description of various events etc. Take note to what is mentioned under e.g. EV_SYN about SYN_DROPPED
  3. Documentation/input ... read up on the rest if you want.
2

You can do this easily by referencing /dev/input/by-id/usb-manufacturername_*serialnumber*. These appear as symbolic links which you can dereference using readlink -e to determine the associated block device. These links are however created by udev which might not be present in your embedded environment.

Or.. Look at the dmesg after connecting the USB device. It should give you the /dev node.

  • 1
    The entries in /dev/disk/by-id/ are imho created by udev - question is whether this is available in this partcular case (embedded platform). – peterph Oct 9 '13 at 21:26
  • @peterph: You are correct. If not using udev the first suggestion won't work. – Jeight Oct 9 '13 at 21:30
  • @Gilles: I see you edited the answer and change the path to input rather than disk. In that case I believe it would be input/by-path and not disk/by-id. I suspect either would work. – Jeight Oct 9 '13 at 22:40
  • 1
    No, by-id is correct. For example my USB keyboard is available as /dev/input/by-id/usb-_USB_Keyboard-event-kbd and /dev/input/by-path/pci-0000:00:1d.2-usb-0:2:1.0-event-kbd. – Gilles Oct 9 '13 at 22:44
  • @Jeight: Once I find the correct device node, do you know how I can access key presses? – KyleL Oct 9 '13 at 23:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.