/* See LICENSE file for copyright and license details. */
#include "common.h"
int
libgamepad_open_superdevice(struct libgamepad_superdevice *devicep, const char *syspath)
{
struct libgamepad_subdevice *subdev;
int dirfd = -1, dirfd2, dirfd3;
DIR *dir = NULL, *dir2 = NULL;
struct dirent *f, *f2;
enum libgamepad_type type;
size_t first = 0, len;
void *new;
int saved_errno;
devicep->ndevices = 0;
devicep->devices = NULL;
devicep->nleds = 0;
devicep->leds = NULL;
devicep->npower_supplies = 0;
devicep->power_supplies = NULL;
devicep->syspath = strdup(syspath);
if (!devicep->syspath)
return -1;
dirfd = open(syspath, O_DIRECTORY);
if (dirfd < 0)
goto fail;
dirfd2 = openat(dirfd, "input", O_DIRECTORY);
if (dirfd2 < 0)
goto fail;
dir = fdopendir(dirfd2);
if (!dir)
goto fail;
while (errno = 0, (f = readdir(dir))) {
if (!strncmp(f->d_name, "input", 5) && isdigit(f->d_name[5])) {
dirfd3 = openat(dirfd2, f->d_name, O_DIRECTORY);
if (dirfd3 < 0)
goto fail;
dir2 = fdopendir(dirfd3);
if (!dir2)
goto fail;
type = 0;
while (errno = 0, (f2 = readdir(dir2))) {
if (!strncmp(f2->d_name, "event", 5) && isdigit(f2->d_name[5])) {
new = realloc(devicep->devices, (devicep->ndevices + 1) * sizeof(*devicep->devices));
if (!new)
goto fail;
devicep->devices = new;
len = sizeof("/dev/input/") + strlen(f2->d_name);
subdev = malloc(offsetof(struct libgamepad_subdevice, path) + len);
if (!subdev)
goto fail;
stpcpy(stpcpy(subdev->path, "/dev/input/"), f2->d_name);
devicep->devices[devicep->ndevices++] = subdev;
} else if (!strncmp(f2->d_name, "js", 2) && isdigit(f2->d_name[2])) {
type |= LIBGAMEPAD_GAMEPAD;
} else if (!strncmp(f2->d_name, "mouse", 5) && isdigit(f2->d_name[5])) {
type |= LIBGAMEPAD_MOUSE;
}
}
while (first < devicep->ndevices)
devicep->devices[first++]->type = type;
closedir(dir2);
dir2 = NULL;
}
}
if (errno)
goto fail;
closedir(dir);
dir = NULL;
dirfd2 = openat(dirfd, "leds", O_DIRECTORY);
if (dirfd2 >= 0) {
dir = fdopendir(dirfd2);
if (!dir)
goto fail;
len = strlen(syspath) + sizeof("/leds/");
while (errno = 0, (f = readdir(dir))) {
if (f->d_name[0] != '.') {
new = realloc(devicep->leds, (devicep->nleds + 1) * sizeof(*devicep->leds));
if (!new)
goto fail;
devicep->leds = new;
devicep->leds[devicep->nleds] = malloc(len + strlen(f->d_name));
if (!devicep->leds[devicep->nleds])
goto fail;
stpcpy(stpcpy(stpcpy(devicep->leds[devicep->nleds], syspath), "/leds/"), f->d_name);
devicep->nleds += 1;
}
}
if (errno)
goto fail;
closedir(dir);
dir = NULL;
}
dirfd2 = openat(dirfd, "power_supply", O_DIRECTORY);
if (dirfd2 >= 0) {
dir = fdopendir(dirfd2);
if (!dir)
goto fail;
len = strlen(syspath) + sizeof("/power_supply/");
while (errno = 0, (f = readdir(dir))) {
if (f->d_name[0] != '.') {
new = realloc(devicep->power_supplies,
(devicep->npower_supplies + 1) * sizeof(*devicep->power_supplies));
if (!new)
goto fail;
devicep->power_supplies = new;
devicep->power_supplies[devicep->npower_supplies] = malloc(len + strlen(f->d_name));
if (!devicep->power_supplies[devicep->npower_supplies])
goto fail;
stpcpy(stpcpy(stpcpy(devicep->power_supplies[devicep->npower_supplies], syspath),
"/power_supply/"), f->d_name);
devicep->npower_supplies += 1;
}
}
if (errno)
goto fail;
closedir(dir);
dir = NULL;
}
close(dirfd);
return 0;
fail:
saved_errno = errno;
if (dir2)
closedir(dir2);
if (dir)
closedir(dir);
if (dirfd >= 0)
close(dirfd);
libgamepad_close_superdevice(devicep);
devicep->syspath = NULL;
devicep->ndevices = 0;
devicep->devices = NULL;
devicep->nleds = 0;
devicep->leds = NULL;
devicep->npower_supplies = 0;
devicep->power_supplies = NULL;
errno = saved_errno;
return -1;
}