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
roduct 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,