/******************************************************************************
版权所有：
文件名称：	gpio.c
文件版本：	01.01
创建作者:	zhaoyang
创建日期：	2023-12-14
功能说明：	gpio驱动程序。
其它说明：
修改记录：

[new]-->applay
B_DO_ADJ	--> GPIOD_17	-->  gpio113 预置返校
B_DO_0		--> GPIOD_03	-->  gpio99 预置
B_DO_1		--> GPIOD_19	-->  gpio115
B_DO_2		--> GPIOD_02	-->  gpio98
B_DO_3		--> GPIOD_09	-->  gpio105
B_DO_4		--> GPIOD_18	-->  gpio114
B_DO_5		--> GPIOD_16	-->  gpio112
B_DO_6		--> GPIOD_07	-->  gpio103
B_DO_7		--> GPIOD_08	-->  gpio104
B_DO_8		--> GPIOD_06	-->  gpio102

M1 开入	    --> GPIOI_11	-->  gpio267
M3 采样     --> GPIOI_10	-->  gpio266
M4 开出     --> GPIOA_02	-->  gpio2

T536 start ↓↓↓↓↓↓
RUN_LED			-->  PB7  	-->  gpio39
ERR_LED			-->  PF4	-->  gpio164
ESAM_PWR		-->  PL7	-->  gpio159
LINE_LOSS_SET	-->  PM5	-->  gpio389
T536 end   ↑↑↑↑↑↑
*/

/*------------------------------- 头文件 --------------------------------------
 */
#include "bspconfig.h"
#include "gpio.h"
#include "head.h"

#include <rtdm/gpio.h>
/*------------------------------- 宏定义 --------------------------------------
 */
#if CFG_BSP_DEBUG

#define _GPIO_DEBUG

#endif

#define GPIO_LOW 0

#define GPIO_INPUT  0
#define GPIO_OUTPUT 1

#define NEW_GOIO_VERSION    1

/*------------------------------ 全局变量 -------------------------------------
 */
typedef enum
{
    SYS_GPIO = 0,
    RTDM_GPIO = 1,
} GPIO_TYPE;

unsigned short kc_kout0_stu;
extern uint32_t g_yx_buf[2]; // 预留64个遥信

/*------------------------------ 函数声明 -------------------------------------
 */
extern unsigned int change_di_ch(unsigned int di);

#if (0 == NEW_GOIO_VERSION)
int gpio_val_fd[DO_NUM];
int gpio_dev_type[DO_NUM] = {
    SYS_GPIO,
}; // 0:系统GPIO, 1:RTDM GPIO

/* 开出gpio */
const int gpio_val[DO_NUM] = {115, 98, 105, 114, 112, 103, 104, 102, 99, 113};

#else

struct t_goio_cfg
{
    int l_pin_num;  // 引脚号
    int l_pin_fd;   // 句柄
    int l_pin_type; // 类型 0:系统GPIO, 1:RTDM GPIO
    int l_pin_dir;  // 方向 GPIO_INPUT 0 输入, GPIO_OUTPUT 1 输出
};

static struct t_goio_cfg mst_gpio_cfg[GPIO_NUM_TOTAL] = 
{
    {115, -1, -1, GPIO_OUTPUT},
    {98, -1, -1, GPIO_OUTPUT},
    {105, -1, -1, GPIO_OUTPUT},
    {114, -1, -1, GPIO_OUTPUT},
    {112, -1, -1, GPIO_OUTPUT},
    {103, -1, -1, GPIO_OUTPUT},
    {104, -1, -1, GPIO_OUTPUT},
    {102, -1, -1, GPIO_OUTPUT},
    {99, -1, -1, GPIO_OUTPUT},
    {113, -1, -1, GPIO_INPUT},
    {267, -1, -1, GPIO_INPUT},
    {266, -1, -1, GPIO_INPUT},
    {2, -1, -1, GPIO_INPUT},
};
#endif

// static int gpio_watchdog_val = 0;
static int gpio_run_led_val = 0;

/*------------------------------ 外部函数 -------------------------------------
 */

int sysfs_gpio_unexport(int gpio)
{
    char path[64];
    int fd;

    snprintf(path, sizeof(path), "/sys/class/gpio/unexport");
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        perror("Failed to open unexport");
        return -1;
    }

    char buf[16];
    snprintf(buf, sizeof(buf), "%d", gpio);
    if (write(fd, buf, strlen(buf)) < 0)
    {
        perror("Failed to set GPIO");
        close(fd);
        return -1;
    }
    close(fd);
    return 0;
}

int sysfs_gpio_export(int gpio)
{
    char path[64];
    int fd;

    // 检查是否已导出
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio);
    if (access(path, F_OK) == 0)
    {
        sysfs_gpio_unexport(gpio); //	bugfix: 避免重复导出
        msleep(50);                // 短暂等待让内核处理
    }

    snprintf(path, sizeof(path), "/sys/class/gpio/export");
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        perror("Failed to open export");
        return -1;
    }

    char buf[16];
    snprintf(buf, sizeof(buf), "%d", gpio);
    if (write(fd, buf, strlen(buf)) < 0)
    {
        perror("Failed to export GPIO");
        close(fd);
        return -2;
    }

    close(fd);
    return 0;
}

int sysfs_gpio_set_direction(int gpio, int is_output)
{
    char path[64];
    int fd;

    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        perror("Failed to open direction");
        return -1;
    }

    if (write(fd, is_output ? "out" : "in", is_output ? 3 : 2) < 0)
    {
        perror("Failed to set direction");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

int sysfs_gpio_set_edge_detect(int gpio, int edge)
{
    char path[64];
    int fd;

    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", gpio);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        perror("Failed to open edge");
        close(fd);
        return -1;
    }

    if (write(fd, edge, strlen(edge)) < 0)
    {
        perror("Failed to set edge");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

int sysfs_gpio_set_value(int gpio, int value)
{
    char path[64];
    int fd = -1;

    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        perror("Failed to open value");
        return -1;
    }

    if (write(fd, value ? "1" : "0", 1) < 0)
    {
        perror("Failed to set value");
        close(fd);
        return -1;
    }

    return fd;
}

int sysfs_gpio_get_value(int gpio)
{
    char path[64];
    int fd = -1;
    int value = 0;

    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        perror("Failed to open value");
        return -1;
    }

    if (read(fd, value, 1) < 0)
    {
        perror("Failed to get value");
        close(fd);
        return -1;
    }

    return fd;
}

int sysfs_gpio_init(int gpio, int direction)
{
    int ret;
    ret = sysfs_gpio_export(gpio);
    if (ret < 0)
    {
        dp_err_n_c("sysfs_gpio_export error ret = %d!", ret);
        return -1;
    }
    ret = sysfs_gpio_set_direction(gpio, direction);
    if (ret < 0)
    {
        dp_err_n_c("sysfs_gpio_set_direction error");
    }

    if (GPIO_INPUT == direction)
    {
        return sysfs_gpio_get_value(gpio);
    }
    else
    {
        return sysfs_gpio_set_value(gpio, GPIO_LOW); // 初始化IO为低电平
    }
}

int is_rtdm_device(int fd)
{
#if (0 == NEW_GOIO_VERSION)
    if (gpio_dev_type[fd] > SYS_GPIO)
        return 1;
    else if (gpio_dev_type[fd] == SYS_GPIO)
        return 0;
    else
        return -1;
#else
    if (mst_gpio_cfg[fd].l_pin_type > SYS_GPIO)
        return 1;
    else if (mst_gpio_cfg[fd].l_pin_type == SYS_GPIO)
        return 0;
    else
        return -1;
#endif
}

int rt_write(int fd, char *buf, int len)
{
    int value;

    if (len <= 0 || buf == NULL)
    {
        return -1;
    }
    if (is_rtdm_device(fd) == 1)
    {
        value = atoi(buf);
        if (value != 0 && value != 1)
        {
            dp_err_n_c("value :%d ERR, Please enter '0' or '1'", value);
            return -1;
        }
        return write(fd, &value, sizeof(value));
    }
    else
    {
        return write(fd, buf, len);
    }
}

int rt_read(int _l_fd, int *_l_state, int len)
{
    int ret = 0, l_val = -1;

    if (is_rtdm_device(_l_fd) == 1)
    {
        ret = read(_l_fd, &l_val, sizeof(l_val));
        if (ret < 0 || ret != sizeof(l_val))
        {
            dp_err_n_c("Failed to read from RTDM device, ret=%d", ret);
            return -1;
        }
        *_l_state = l_val;
    }
    else
    {
        ret = read(_l_fd, &l_val, len);
        dp_err_n_c("ret=%d", ret);
        if (ret < 0)
        {
            dp_err_n_c("Failed to read from regular device");
            return -1;
        }
        *_l_state = l_val;
    }
    return ret;
}

int rtdm_gpio_init(int gpio, int direction)
{
    char path[64];
    int fd = -1, ret;
    int value = 1;
    snprintf(path, sizeof(path), "/dev/rtdm/3604000.pinctrl/gpio%d", gpio);
    if (GPIO_INPUT == direction)
    {
        fd = open(path, O_RDWR | O_NONBLOCK);
    }
    else
    {
        fd = open(path, O_RDWR);
    }
    if (fd < 0)
    {
        dp_err_n_c("gpio [%d] open error", gpio);
        perror("open");
        return -1;
    }

    if (GPIO_INPUT == direction)
    {
        ret = ioctl(fd, GPIO_RTIOC_DIR_IN, &value);
    }
    else
    {
        ret = ioctl(fd, GPIO_RTIOC_DIR_OUT, &value);
    }
    if (ret < 0)
    {
        dp_err_n_c("gpio [%d] ioctl error", gpio);
        perror("gpio ioctl err");
        close(fd);
        return -1;
    }

    return fd;
}

int gpio_init(void)
{
    int i;
#if (0 == NEW_GOIO_VERSION)

    for (i = 0; i < DO_NUM; i++)
    {
        gpio_val_fd[i] = rtdm_gpio_init(gpio_val[i], GPIO_OUTPUT);
        if (gpio_val_fd[i] < 0)
        {
            gpio_val_fd[i] = sysfs_gpio_init(gpio_val[i], GPIO_OUTPUT);
            dp_info_n_c("gpio_val_fd[%02d] = %d", i, gpio_val_fd[i]);
            if (gpio_val_fd[i] < 0)
            {
                dp_err_n_c("gpio_val_fd[%d] error", i);
                gpio_val_fd[i] = -1;
                gpio_dev_type[i] = -1;
                return -1;
            }
        }
        else
        {
            gpio_dev_type[i] = gpio_val_fd[i]; // 保存RTDM设备类型句柄,这里有风险，fd句柄可能为0
        }
    }

    msleep(10); // 延时10ms

    kc_kout0_stu = 0;

    for (i = DO_OUT1; i < DO_NUM; i++)
    {
        rt_write(gpio_val_fd[i], "1", 2);
    }

    return 0;

#else
    
    for (i = 0; i < GPIO_NUM_TOTAL; i++)
    {
        mst_gpio_cfg[i].l_pin_fd = rtdm_gpio_init(mst_gpio_cfg[i].l_pin_num, mst_gpio_cfg[i].l_pin_dir);
        if (mst_gpio_cfg[i].l_pin_fd < 0)
        {
            mst_gpio_cfg[i].l_pin_fd = sysfs_gpio_init(mst_gpio_cfg[i].l_pin_num, mst_gpio_cfg[i].l_pin_dir);
            if (mst_gpio_cfg[i].l_pin_fd < 0)
            {
                dp_err_n_c("mst_gpio_cfg[%d].l_pin_fd error", i);
                mst_gpio_cfg[i].l_pin_fd = -1;
                mst_gpio_cfg[i].l_pin_type = -1;
                return -1;
            }
        }
        else
        {
            mst_gpio_cfg[i].l_pin_type = mst_gpio_cfg[i].l_pin_fd; // 保存RTDM设备类型句柄,这里有风险，fd句柄可能为0
        }
    }

    msleep(10); // 延时10ms

    kc_kout0_stu = 0;

    for (i = 0; i < (DO_NUM - 1); i++)
    {
        rt_write(mst_gpio_cfg[i].l_pin_fd, "1", 2);
    }

    return 0;
#endif
}

int gpio_exit(void)
{
    int i;

    for (i = 0; i < DO_NUM; i++)
    {
#if (0 == NEW_GOIO_VERSION)
        if (gpio_val_fd[i] >= 0)
        {
            close(gpio_val_fd[i]);
            gpio_val_fd[i] = -1;
        }
        sysfs_gpio_unexport(gpio_val[i]);
#else
        if (mst_gpio_cfg[i].l_pin_fd >= 0)
        {
            close(mst_gpio_cfg[i].l_pin_fd);
            mst_gpio_cfg[i].l_pin_fd = -1;
        }
        sysfs_gpio_unexport(mst_gpio_cfg[i].l_pin_num);
#endif
    }

    return 0;
}

/******************************************************************************
函数名称：	gpio_get_di
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	得到开入的状态
参数说明：	无
返回值：  	返回开入的状态，每个bit代表一个开入量。
修改记录：
*/
unsigned int gpio_get_di(void)
{
    unsigned int dw = 0;

    dw = g_yx_buf[0]; // 这里保存的是共享内存的yx值，156us更新一次

    return change_di_ch(~dw); // 取反---因为没有短接时，值为1. 所以要取反为0才行
}

/******************************************************************************
函数名称：	gpio_di_fj
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	读取开出返校遥信值,控制板遥信为YX17~YX18
            YX17:选择, YX18:执行

参数说明：	 无
返回值：  返回遥信值
修改记录：
*/
#ifdef DO_KOUT_CHECK
unsigned short gpio_di_fj(unsigned char index) //(void)
#else
unsigned short gpio_di_fj(void)
#endif
{
    unsigned int di = 0;
    unsigned short ret = 0;

    di = gpio_get_di();
#ifdef DO_KOUT_CHECK
    ret = (unsigned short)((di >> index) & 0x0001);
#else
    ret = (unsigned short)((di >> 23) & 0x0001);
#endif
    return ret;
}

/**
 * @brief          读取gpio的状态
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20260311
 * @param[in/out]  {uint8_t} _uc_idx 下标
 * @return         * int
 * @retval         none
 * 
 * @warning        none
 * @note           none
 */
int gpioio_get_gpio_status(uint8_t _uc_idx)
{
    int ret, l_io_state = 0;

    if (_uc_idx < DO_IN_PRESET || _uc_idx > GPIO_INT_M4)
        return -1;

    ret = rt_read(mst_gpio_cfg[_uc_idx].l_pin_fd, &l_io_state, sizeof(l_io_state));
    if (ret < 0)
    {
        dp_err_n_c("l_io_state: %d\r\n", l_io_state);
        return -2;
    }
    return l_io_state;
}

/******************************************************************************
函数名称：	gpio_kout_do
函数版本：	01.01
创建作者：
创建日期：	2014-11-28
函数说明：	开出

参数说明：	on: 1开出, 0回收
            kout: 那一路
返回值：  无
修改记录：
*/
int gpio_kout_do(int on, unsigned int kout)
{
    int l_ret = -1;
    if (kout > DO_OUT_PRESET)
        return;

#if (0 == NEW_GOIO_VERSION)
    if (on)
    {
        l_ret = rt_write(gpio_val_fd[kout + DO_OUT0], "0", 2);
        // dp_info_n_c("kout = %d, on = %d, l_ret = %d", kout, on, l_ret);
    }
    else
    {
        l_ret = rt_write(gpio_val_fd[kout + DO_OUT0], "1", 2);
        // dp_info_n_c("kout = %d, on = %d, l_ret = %d", kout, on, l_ret);
    }
#else
    if (on)
    {
        l_ret = rt_write(mst_gpio_cfg[kout + DO_OUT0].l_pin_fd, "0", 2);
        // dp_info_n_c("kout = %d, on = %d, l_ret = %d", kout, on, l_ret);
    }
    else
    {
        l_ret = rt_write(mst_gpio_cfg[kout + DO_OUT0].l_pin_fd, "1", 2);
        // dp_info_n_c("kout = %d, on = %d, l_ret = %d", kout, on, l_ret);
    }
#endif
    return l_ret;
}

/******************************************************************************
函数名称：	gpio_get_wirelessin
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-09-16
函数说明：	得到无线遥控的状态。
参数说明：	无
返回值：  	返回开入的状态，每个bit代表一个开入量。
修改记录：
*/
unsigned int gpio_get_wirelessin(void)
{
    register unsigned int dw = 0;

    return dw;
}

unsigned int gpio_get_version(void)
{
    register unsigned int dw = 0;

    return dw;
}

unsigned int gpio_get_addr(void)
{
    register unsigned int dw = 0;

    return dw;
}

void _led_run_err(void)
{
    // 上电就亮错误灯，有错误就继续亮没有就熄灭
    static uint8 err_led = 2;

    if (rt_err_count())
    {
        // 错误指示灯常亮且运行熄灭
        if (!(err_led & 0x01))
        {
            err_led |= 1;
            // write(gpio_val_fd_other[ERR_LED], "0", 2);
            // write(gpio_val_fd_other[RUN_LED], "1", 2);
        }
        return;
    }

    if (err_led)
    {
        err_led = 0;
        // write(gpio_val_fd_other[ERR_LED], "1", 2);
    }

    // 运行指示灯,每隔1s改变状态一次
    if (!gpio_run_led_val)
    {
        gpio_run_led_val = 1;
        // write(gpio_val_fd_other[RUN_LED], "0", 2);
    }
    else
    {
        gpio_run_led_val = 0;
        // write(gpio_val_fd_other[RUN_LED], "1", 2);
    }
}

void esam_power_ctrl(int on)
{
    // if (on)
    //     write(gpio_val_fd_other[ESAM_PWR], "1", 2);
    // else
    //     write(gpio_val_fd_other[ESAM_PWR], "0", 2);
}

/*------------------------------ 内部函数 -------------------------------------
 */

/*------------------------------ 测试函数 -------------------------------------
 */

#if (0)
int test_read_pin(void)
{
    int ret, l_io_state = 0;
    dp_info_n_c("test_read_pin!");

    ret = rt_read(mst_gpio_cfg[GPIO_INT_M1].l_pin_fd, &l_io_state, sizeof(l_io_state));
    dp_info_n_c("ret: %d", ret);
    if (ret > 0)
    {
        dp_info_n_c("l_io_state: %d\r\n", l_io_state);
    }
    else
    {
        dp_err_n_c("l_io_state: %d\r\n", l_io_state);
    }

    ret = rt_read(mst_gpio_cfg[GPIO_INT_M3].l_pin_fd, &l_io_state, sizeof(l_io_state));
    dp_info_n_c("ret: %d", ret);
    if (ret > 0)
    {
        dp_info_n_c("l_io_state: %d\r\n", l_io_state);
    }
    else
    {
        dp_err_n_c("l_io_state: %d\r\n", l_io_state);
    }

    ret = rt_read(mst_gpio_cfg[GPIO_INT_M4].l_pin_fd, &l_io_state, sizeof(l_io_state));
    dp_info_n_c("ret: %d", ret);
    if (ret > 0)
    {
        dp_info_n_c("l_io_state: %d\r\n", l_io_state);
    }
    else
    {
        dp_err_n_c("l_io_state: %d\r\n", l_io_state);
    }
}
#endif
