LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Get /dev/input/eventX from VID:PID (C/C++) (http://www.linuxquestions.org/questions/programming-9/get-dev-input-eventx-from-vid-pid-c-c-906842/)

Litch84 10-06-2011 10:07 PM

Get /dev/input/eventX from VID:PID (C/C++)
 
I have a known USB HID device XYZ, which has a known VID:PID (It's an RFID Scanner that acts like a keyboard).

I need to know (once it's plugged in) how to get the /dev/input/eventX path by just knowing it's VID:PID...

(Solution in C/C++)

Any ideas?
Does the linux event subsystem have an enumeration function?

Nominal Animal 10-08-2011 06:49 AM

Each event interface is listed in /sys/class/input/ as a directory. USB devices will have the vendor ID in hexadecimal in /sys/class/input/eventX/device/id/vendor and product ID in /sys/class/input/eventX/device/id/product. You can rely on /sys being mounted, since otherwise a lot of programs (and even some GNU C library functions) may stop working.

The implementation depends on your approach, but here is a thread-safe example in C99:
Code:

#define  _POSIX_C_SOURCE 200808L
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

static char *strmerge(const char *const s1, const char *const s2)
{
        const size_t        n1 = s1 ? strlen(s1) : 0;
        const size_t        n2 = s2 ? strlen(s2) : 0;
        char                *s;

        if (!n1 && !n2) {
                errno = EINVAL;
                return NULL;
        }

        s = malloc(n1 + n2 + 1);
        if (!s) {
                errno = ENOMEM;
                return NULL;
        }

        if (n1)        memcpy(s, s1, n1);
        if (n2)        memcpy(s + n1, s2, n2);
        s[n1 + n2] = 0;

        return s;
}

static int read_hex(const char *const filename)
{
        FILE                *in;
        unsigned int        value;

        in = fopen(filename, "rb");
        if (!in)
                return -1;

        if (fscanf(in, "%x", &value) == 1) {
                fclose(in);
                return (int)value;
        }

        fclose(in);
        return -1;
}

static int vendor_id(const char *const event)
{
        if (event && *event) {
                char        filename[256];
                int        result;

                result = snprintf(filename, sizeof(filename), "/sys/class/input/%s/device/id/vendor", event);
                if (result < 1 || result >= sizeof(filename))
                        return -1;

                return read_hex(filename);
        }
        return -1;
}

static int product_id(const char *const event)
{
        if (event && *event) {
                char        filename[256];
                int        result;

                result = snprintf(filename, sizeof(filename), "/sys/class/input/%s/device/id/product", event);
                if (result < 1 || result >= sizeof(filename))
                        return -1;

                return read_hex(filename);
        }
        return -1;
}

char *find_event(const int vendor, const int product)
{
        DIR                *dir;
        struct dirent        *cache, *entry;
        char                *name;
        long                maxlen;
        int                result;

        maxlen = pathconf("/sys/class/input/", _PC_NAME_MAX);
        if (maxlen == -1L)
                return NULL;

        dir = opendir("/sys/class/input");
        if (!dir)
                return NULL;

        cache = malloc(offsetof(struct dirent, d_name) + maxlen + 1);
        if (!cache) {
                closedir(dir);
                errno = ENOMEM;
                return NULL;
        }

        while (1) {

                entry = NULL;
                result = readdir_r(dir, cache, &entry);
                if (result) {
                        free(cache);
                        closedir(dir);
                        errno = result;
                        return NULL;
                }

                if (!entry) {
                        free(cache);
                        closedir(dir);
                        errno = ENOENT;
                        return NULL;
                }

                if (vendor_id(entry->d_name) == vendor &&
                    product_id(entry->d_name) == product) {
                        name = strmerge("/dev/input/", entry->d_name);
                        free(cache);
                        closedir(dir);
                        if (name)
                                return name;
                        errno = ENOMEM;
                        return NULL;
                }

        }
}

int main(int argc, char *argv[])
{
        int                arg, status;
        unsigned int        vendor, product;
        char                *event;
        char                dummy;

        if (argc < 2) {
                fprintf(stderr, "\nUsage: %s vendor:product\n\n", argv[0]);
                return 0;
        }

        status = 0;

        for (arg = 1; arg < argc; arg++)
                if (sscanf(argv[arg], "%x:%x %c", &vendor, &product, &dummy) == 2) {
                        event = find_event((int)vendor, (int)product);
                        if (event) {
                                fprintf(stdout, "%s (%s)\n", event, argv[arg]);
                                fflush(stdout);
                                free(event);
                        } else {
                                fprintf(stderr, "%s not found.\n", argv[arg]);
                                fflush(stderr);
                                status |= 2;
                        }
                } else {
                        fprintf(stderr, "%s is not a valid vendor:product.\n", argv[arg]);
                        fflush(stderr);
                        status |= 1;
                }

        return status;
}

The function find_event() will return the path to the event device, if it finds a matching vendor:product input device. Note the careful error handling, and use of re-entrant readdir_r() (for threaded programs). I supplied the main() too for testing, so you can compile and run it as a standalone program first. I am sure this could be written in a much cleaner manner, and I would not be surprised if something like this is already available in a library, but this should do for an example.

Hope you find it useful,


All times are GMT -5. The time now is 07:29 PM.