/*
 * Copyright (c) 2019-2020, yzrh <yzrh@noema.org>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "gpio.h"

#ifdef __FreeBSD__

int
gpio_config(int pin, int direction, int interrupt)
{
	int ret;
	int fd = open("/dev/gpioc0", O_RDWR);

	if (fd == -1) {
		fprintf(stderr, "GPIO configuration failure\n");
		return 1;
	}

	struct gpio_pin pin_req = {
		.gp_pin = pin,
		.gp_caps = interrupt,
		.gp_flags = direction
	};

	ret = ioctl(fd, GPIOSETCONFIG, &pin_req) == 0 ? 0 : 1;
	close(fd);
	return ret;
}

int
gpio_write(int pin, int value)
{
	int ret;
	int fd = open("/dev/gpioc0", O_RDWR);

	if (fd == -1) {
		fprintf(stderr, "GPIO output failure\n");
		return 1;
	}

	struct gpio_req req = {
		.gp_pin = pin,
		.gp_value = value
	};

	ret = ioctl(fd, GPIOSET, &req) == 0 ? 0 : 1;
	close(fd);
	return ret;
}

int
gpio_read(int pin)
{
	int fd = open("/dev/gpioc0", O_RDWR);

	if (fd == -1) {
		fprintf(stderr, "GPIO input failure\n");
		return 1;
	}

	struct gpio_req req = {
		.gp_pin = pin
	};

	ioctl(fd, GPIOGET, &req);

	close(fd);
	return req.gp_value;
}

#elif __linux__

int
gpio_export(int pin)
{
	int export = open("/sys/class/gpio/export", O_RDWR);

	if (export == -1) {
		fprintf(stderr, "GPIO export failure\n");
		return 1;
	}

	char buf[3];
	int buf_size = snprintf(buf, 3, "%d", pin);
	write(export, buf, buf_size);

	close(export);
	return 0;
}

int
gpio_unexport(int pin)
{
	int unexport = open("/sys/class/gpio/unexport", O_RDWR);

	if (unexport == -1) {
		fprintf(stderr, "GPIO unexport failure\n");
		return 1;
	}

	char buf[3];
	int buf_size = snprintf(buf, 3, "%d", pin);
	write(unexport, buf, buf_size);

	close(unexport);
	return 0;
}

int
gpio_config(int pin, int direction, int interrupt)
{
	char path[33];
	snprintf(path, 33, "/sys/class/gpio/gpio%d/direction", pin);

	int config = open(path, O_RDWR);

	if (config == -1) {
		fprintf(stderr, "GPIO configuration failure\n");
		return 1;
	}

	char buf[8];
	int buf_size = snprintf(buf, 8, "%s", direction == 0 ? "in" : "out");
	write(config, buf, buf_size);

	close(config);

	snprintf(path, 33, "/sys/class/gpio/gpio%d/edge", pin);

	config = open(path, O_RDWR);

	if (config == -1) {
		fprintf(stderr, "GPIO configuration failure\n");
		return 1;
	}

	buf_size = snprintf(buf, 8, "%s",
		interrupt == 0 ? "falling" : "rising");
	write(config, buf, buf_size);

	close(config);
	return 0;
}

int
gpio_write(int pin, int value)
{
	char path[29];
	snprintf(path, 29, "/sys/class/gpio/gpio%d/value", pin);

	int config = open(path, O_RDWR);

	if (config == -1) {
		fprintf(stderr, "GPIO output failure\n");
		return 1;
	}

	char buf[2];
	int buf_size = snprintf(buf, 2, "%d", value);
	write(config, buf, buf_size);

	close(config);
	return 0;
}

int
gpio_read(int pin)
{
	char path[29];
	snprintf(path, 29, "/sys/class/gpio/gpio%d/value", pin);

	int config = open(path, O_RDONLY);

	if (config == -1) {
		fprintf(stderr, "GPIO input failure\n");
		return 1;
	}

	char buf[2];
	read(config, buf, 2);

	close(config);
	return atoi(buf);
}

#endif /* __FreeBSD__ */
