/* 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; }