aboutsummaryrefslogblamecommitdiffstats
path: root/libgamepad_open_superdevice.c
blob: b6b975f7349f1ea3a3c2c5a8577bb9fa5ffd209b (plain) (tree)




















































































































































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