/**
 * @file           ch423s.c
 * @brief
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 *
 * @copyright Copyright (c) 2025 by OLE, All Rights Reserved.
 *
 * @par 修改日志:
 * <table>
 * <tr><th>Date        <th>Version         <th>Author          <th>Description
 * <tr><td>20251224    <td>2.0             <td>test            <td>内容
 * </table>
 */
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include "head.h"
#include "ch423s.h"

#if defined IO_CHIP_CH423S

/*  设置系统参数命令 */
#define CH423_SYS_CMD 0x48 // 设置系统参数命令，默认方式
#define BIT_SLEEP 0x30     // 低功耗睡眠控制 0正常工作 1睡眠模式
#define BIT_INTENS 0x20    // 动态显示驱动亮度控制
#define BIT_OD_OE 0x10     // 输出引脚 OC15～OC0 开漏输出使能 0推挽输出低电平和高电平 1开漏输出低电平和不输出
#define BIT_X_INT 0x08     // 使能输入电平变化中断 0禁止输入电平变化中断 为1并且DEC_H为0允许从OC15引脚输出电平变化中断
#define BIT_DEC_H 0x04     // 控制开漏输出引脚高8位的片选译码 0通用输出 1分时显示扫描计数器译码后
#define BIT_DEC_L 0x02     // 控制开漏输出引脚低8位的片选译码 0通用输出 1分时显示扫描计数器译码后
#define BIT_IO_OE 0x01     // 控制双向输入输出引脚的三态输出 0禁止输出 1允许输出

/*  设置低8位开漏输出命令 */
#define CH423_OC_L_CMD 0x44 // 设置低8位开漏输出命令，默认方式
#define BIT_OC0_L_DAT 0x01  // OC0为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC1_L_DAT 0x02  // OC1为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC2_L_DAT 0x04  // OC2为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC3_L_DAT 0x08  // OC3为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC4_L_DAT 0x10  // OC4为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC5_L_DAT 0x20  // OC5为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC6_L_DAT 0x40  // OC6为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC7_L_DAT 0x80  // OC7为0则使引脚输出低电平，为1则引脚不输出

/*  设置高8位开漏输出命令 */
#define CH423_OC_H_CMD 0x46 // 设置低8位开漏输出命令，默认方式
#define BIT_OC8_L_DAT 0x01  // OC8为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC9_L_DAT 0x02  // OC9为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC10_L_DAT 0x04 // OC10为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC11_L_DAT 0x08 // OC11为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC12_L_DAT 0x10 // OC12为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC13_L_DAT 0x20 // OC13为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC14_L_DAT 0x40 // OC14为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC15_L_DAT 0x80 // OC15为0则使引脚输出低电平，为1则引脚不输出

/* 设置双向输入输出命令 */
#define CH423_SET_IO_CMD 0x60 // 设置双向输入输出命令，默认方式
#define BIT_IO0_DAT 0x01      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO0为0输出低电平，为1输出高电平
#define BIT_IO1_DAT 0x02      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO1为0输出低电平，为1输出高电平
#define BIT_IO2_DAT 0x04      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO2为0输出低电平，为1输出高电平
#define BIT_IO3_DAT 0x08      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO3为0输出低电平，为1输出高电平
#define BIT_IO4_DAT 0x10      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO4为0输出低电平，为1输出高电平
#define BIT_IO5_DAT 0x20      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO5为0输出低电平，为1输出高电平
#define BIT_IO6_DAT 0x40      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO6为0输出低电平，为1输出高电平
#define BIT_IO7_DAT 0x80      // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO7为0输出低电平，为1输出高电平


/**< led配置结构体 */
struct t_config_led
{
    uint8_t uc_port;
    uint8_t uc_pin;
    uint8_t uc_en;      /**< 使能标志 */
    uint8_t uc_mode;    /**< 模式 0灭 1亮 2闪烁 */
    uint8_t uc_state;   /**< 目前状态 */
    uint32_t ul_time;   /**< 翻转时间 */
    uint32_t ul_count;  /**< 翻转计时 */
};

static struct t_config_led mst_led_cfg[LED_MAX_NUM] =
    {
        {CH423_OC_L_CMD, 0, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 1, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 2, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 3, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 4, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 5, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 6, 0, 0, 0, 0},
        {CH423_OC_L_CMD, 7, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 8, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 9, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 10, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 11, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 12, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 13, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 14, 0, 0, 0, 0},
        {CH423_OC_H_CMD, 15, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 16, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 17, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 18, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 19, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 20, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 21, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 22, 0, 0, 0, 0},
        {CH423_SET_IO_CMD, 23, 0, 0, 0, 0},
};

#define I2C_DEV "/dev/i2c-5"
static int sl_ch423s_fd = -1, sl_ch423s_init = -1;
static uint32_t sul_io_status = 0xffffff;
static uint8_t msuc_test_flag = 0;

/**
 * @brief
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @param[in/out]  {uint8_t} _uc_cmd 命令
 * @param[in/out]  {uint8_t} _uc_state 状态
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           注意这个原件没有固定的iic地址 抓数据发现 命令 + 数据 就可以操作
 */
static int ch423_write_data(uint8_t _uc_cmd, uint8_t _uc_state)
{
    /* 使用寄存器地址来寻址 */
    if (ioctl(sl_ch423s_fd, I2C_SLAVE, _uc_cmd >> 1) < 0)
    {
        dp_err_n_c("Failed to set I2C slave address: %s\n", strerror(errno));

        return -1;
    }
    
    if (write(sl_ch423s_fd, &_uc_state, 1) != 1)
    {
        dp_err_n_c("Failed to write data: %s", strerror(errno));

        return -2;
    }
    return 0;
}

/**
 * @brief
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @param[in/out]  {uint8_t} _uc_idx
 * @param[in/out]  {uint8_t} _uc_state
 * @return         * void
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
static void ch423s_updata_port_status(uint8_t _uc_idx, uint8_t _uc_state)
{
    uint32_t ul_data_1 = 0, ul_data_2 = 0;
    ul_data_1 = sul_io_status;
    if ((sul_io_status >> _uc_idx) & 0x01)
    {
        sul_io_status ^= (1 << _uc_idx);
    }
    ul_data_2 = sul_io_status;

    /* 重新设置 */
    sul_io_status |= _uc_state ? (0 << _uc_idx) : (1 << _uc_idx);
    // dp_err_n_c_rt("ul_data_1 = 0x%06x, ul_data_2 = 0x%06x, sul_io_status = 0x%06x\r\n", ul_data_1, ul_data_1, sul_io_status);
}

/**
 * @brief          定时更新gpio状态
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int ch423sio_updata_status_ontime(void)
{
    uint8_t uca_state[3] = {0};
    int l_ret = 0;

    uca_state[0] = (uint8_t)(sul_io_status & 0xff);
    (0 == ch423_write_data(CH423_OC_L_CMD, uca_state[0])) ? l_ret : l_ret--;

    uca_state[1] = (uint8_t)((sul_io_status >> 8) & 0xff);
    (0 == ch423_write_data(CH423_OC_H_CMD, uca_state[1])) ? l_ret : l_ret--;

    uca_state[2] = (uint8_t)((sul_io_status >> 16) & 0xff);
    (0 == ch423_write_data(CH423_SET_IO_CMD, uca_state[2])) ? l_ret : l_ret--;

    return l_ret;
}

/**
 * @brief          设置io状态
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @param[in/out]  {uint8_t} _uc_idx 下标
 * @param[in/out]  {uint8_t} _uc_state 电平状态
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int ch423sio_set_io(uint8_t _uc_idx, uint8_t _uc_state)
{
    if (_uc_idx >= LED_MAX_NUM || sl_ch423s_init < 0 || _uc_state > 1)
    {
        return -1;
    }

    switch (mst_led_cfg[_uc_idx].uc_port)
    {
    case CH423_OC_L_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        break;

    case CH423_OC_H_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        break;

    case CH423_SET_IO_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        break;

    default:
        return -2;
        break;
    }
    return 0;
}

/**
 * @brief          设置io状态
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @param[in/out]  {uint8_t} _uc_idx 下标
 * @param[in/out]  {uint8_t} _uc_state 电平状态
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int ch423sio_set_ioupdata(uint8_t _uc_idx, uint8_t _uc_state)
{
    int ret = -1;
    uint8_t io_val[3] = {0};

    if (_uc_idx >= LED_MAX_NUM || sl_ch423s_init < 0 || _uc_state > 1)
    {
        dp_err_n_c("_uc_idx = %d, sl_ch423s_init = %d, _uc_state = %d", _uc_idx, sl_ch423s_init, _uc_state);
        return -1;
    }

    switch (mst_led_cfg[_uc_idx].uc_port)
    {
    case CH423_OC_L_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        io_val[0] = (uint8_t)(sul_io_status & 0xff);
        ret = ch423_write_data(CH423_OC_L_CMD, io_val[0]);
        break;

    case CH423_OC_H_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        io_val[1] = (uint8_t)((sul_io_status & 0xff00) >> 8);
        ret = ch423_write_data(CH423_OC_H_CMD, io_val[1]);
        break;

    case CH423_SET_IO_CMD:
        ch423s_updata_port_status(mst_led_cfg[_uc_idx].uc_pin, _uc_state);
        io_val[2] = (uint8_t)((sul_io_status & 0xff0000) >> 16);
        ret = ch423_write_data(CH423_SET_IO_CMD, io_val[2]);
        break;

    default:
        return -2;
        break;
    }
    return ret;
}

/**
 * @brief          io测试
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251230
 * @return         * int
 * @retval         none
 * 
 * @warning        none
 * @note           none
 */
static int ch423sio_run_test(void)
{
    int i = 0;

    for (i = 0; i < LED_MAX_NUM; i++)
    {
        ch423sio_set_ioupdata(i, 1);
        usleep(1000 * 50);
    }
    usleep(1000 * 100);

    for (i = (LED_MAX_NUM - 1); i >= 0; i--)
    {
        ch423sio_set_ioupdata(i, 0);
        usleep(1000 * 50);
    }
    usleep(1000 * 100);

    for (i = 0; i < LED_MAX_NUM; i++)
    {
        ch423sio_set_ioupdata(i, 1);
    }
    usleep(1000 * 100);

    for (i = 0; i < LED_MAX_NUM; i++)
    {
        ch423sio_set_ioupdata(i, 0);
    }
    usleep(1000 * 100);

    return 0;
}

/**
 * @brief          初始化设备
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int ch423sio_init_dev(void)
{
    // 打开I2C设备
    sl_ch423s_fd = open(I2C_DEV, O_RDWR);
    if (sl_ch423s_fd < 0)
    {
        dp_err_n_c_rt("Failed to open I2C device %s: %s", I2C_DEV, strerror(errno));

        return -1;
    }

    ch423_write_data(CH423_SYS_CMD, BIT_IO_OE); /* 将IO7～IO0设置为输出 其它参数使用默认值 */
    ch423_write_data(CH423_OC_L_CMD, 0xff);
    ch423_write_data(CH423_OC_H_CMD, 0xff);
    ch423_write_data(CH423_SET_IO_CMD, 0xff);

    sl_ch423s_init = 0;

    ch423sio_run_test();

    return 0;
}

/**
 * @brief          关闭句柄
 * @author         lch (lch_work@foxmail.com)
 * @version        1.0
 * @date           20251224
 * @return         * int
 * @retval         none
 *
 * @warning        none
 * @note           none
 */
int ch423sio_exit_dev(void)
{
    if (sl_ch423s_fd >= 0)
    {
        close(sl_ch423s_fd);
        sl_ch423s_fd = -1;
    }

    return 0;
}

/**
 * @brief       控制led驱动
 *
 * @param[in]   {uint8_t} _uc_idx 硬件地址
 * @param[in]   {uint8_t} _uc_set 模式 0灭 1亮
 */
static void ch423_control_drive(uint8_t _uc_idx, uint8_t _uc_set)
{
    if (_uc_idx >= LED_MAX_NUM || _uc_set >= e_io_mode_max)
        return;

    switch (_uc_set)
    {
    case e_io_mode_off:
        ch423sio_set_io(_uc_idx, e_io_mode_off);
        mst_led_cfg[_uc_idx].uc_state = e_io_mode_off;
        break;

    case e_io_mode_on:
        ch423sio_set_io(_uc_idx, e_io_mode_on);
        mst_led_cfg[_uc_idx].uc_state = e_io_mode_on;
        break;

    default:
        return;
        break;
    }
}

/**
 * @brief       切换led模式
 *
 * @details     闪烁模式一直在计时，uc_en使能标志不会清零
 */
void ch423sio_handle_mode(void)
{
    uint8_t uc_updata = 0;

#if (0) /* 测试打印代码 */
    static uint64_t sull_printf_led = 0;
    if (sull_printf_led++ >= (10 * 10))
    {
        sull_printf_led = 0;
        dp_info_n_c("dTCounter = %d", dTCounter);
        for (uint8_t j = 0; j < LED_MAX_NUM; j++)
        {
            dp_info_n_c("[%02d] uc_en = %d, mode = %d, state = %d, ul_time = %d, ul_count = %d",
                        j,
                        mst_led_cfg[j].uc_en,
                        mst_led_cfg[j].uc_mode,
                        mst_led_cfg[j].uc_state,
                        mst_led_cfg[j].ul_time,
                        mst_led_cfg[j].ul_count);
        }
    }
#endif

    for (uint8_t i = 0; i < LED_MAX_NUM; i++)
    {
        if (mst_led_cfg[i].uc_en == 0)
            continue;

        mst_led_cfg[i].ul_count += 100;
        if (mst_led_cfg[i].ul_count > mst_led_cfg[i].ul_time)
        {
            mst_led_cfg[i].ul_count = 0;
            uc_updata = 1;
            if (mst_led_cfg[i].uc_mode != e_io_mode_shine)
            {
                mst_led_cfg[i].uc_en = 0;
                ch423_control_drive(i, mst_led_cfg[i].uc_mode);
            }
            else
            {
                if (e_io_mode_on == mst_led_cfg[i].uc_state)
                {
                    ch423_control_drive(i, e_io_mode_off);
                }
                else
                {
                    ch423_control_drive(i, e_io_mode_on);
                }
            }
        }
    }
    if (1 == uc_updata)
    {
        ch423sio_updata_status_ontime();
    }
}

/**
 * @brief
 *
 * @param[in]   {uint8_t} _uc_idx led映射
 * @param[in]   {enum e_mode_ch423} _e_mode 模式 0灭 1亮 2闪烁
 * @param[in]   {uint32_t} _ul_time 闪烁时间
 * @return      int
 * @retval      0设置模式成功
 * @details     none
 */
int ch423sio_control_state(uint8_t _uc_idx, enum e_mode_ch423 _e_mode, uint32_t _ul_time)
{
    if (_uc_idx >= LED_MAX_NUM || _e_mode >= e_io_mode_max)
        return -1;

    mst_led_cfg[_uc_idx].uc_en = 1;
    mst_led_cfg[_uc_idx].uc_mode = _e_mode;
    if (_e_mode == e_io_mode_shine)
        mst_led_cfg[_uc_idx].ul_time = _ul_time;

    return 0;
}

#endif /* TMP_CHIP_AHT20 */