main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <linux/input.h>

#include <stdint.h>

int uart_open(const char *port)
{
    int fd;
    fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (-1 == fd)
    {
        perror("Can't Open Serial Port");
        return -1;
    }

    if (fcntl(fd, F_SETFL, 0) < 0)
    {
        printf("fcntl failed!\n");
        return -1;
    }

    printf("serial open=%d\n", fd);
    return fd;
}

void uart_close(int fd)
{
    close(fd);
}

int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
    int i;
    int speed_arr[] = {B115200, B19200, B9600, B4800, B2400, B1200, B300};
    int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};

    struct termios options;

    if (tcgetattr(fd, &options) != 0)
    {
        perror("SetupSerial 1");
        return -1;
    }

    for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++)
    {
        if (speed == name_arr[i])
        {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
        }
    }

    options.c_cflag |= CLOCAL;
    options.c_cflag |= CREAD;

    switch (flow_ctrl)
    {

    case 0:
        options.c_cflag &= ~CRTSCTS;
        break;

    case 1:
        options.c_cflag |= CRTSCTS;
        break;
    case 2:
        options.c_cflag |= IXON | IXOFF | IXANY;
        break;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits)
    {
    case 5:
        options.c_cflag |= CS5;
        break;
    case 6:
        options.c_cflag |= CS6;
        break;
    case 7:
        options.c_cflag |= CS7;
        break;
    case 8:
        options.c_cflag |= CS8;
        break;
    default:
        fprintf(stderr, "Unsupported data size\n");
        return -1;
    }

    switch (parity)
    {
    case 'n':
    case 'N':
        options.c_cflag &= ~PARENB;
        options.c_iflag &= ~INPCK;
        break;
    case 'o':
    case 'O':
        options.c_cflag |= (PARODD | PARENB);
        options.c_iflag |= INPCK;
        break;
    case 'e':
    case 'E':
        options.c_cflag |= PARENB;
        options.c_cflag &= ~PARODD;
        options.c_iflag |= INPCK;
        break;
    case 's':
    case 'S':
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        break;
    default:
        fprintf(stderr, "Unsupported parity\n");
        return -1;
    }

    switch (stopbits)
    {
    case 1:
        options.c_cflag &= ~CSTOPB;
        break;
    case 2:
        options.c_cflag |= CSTOPB;
        break;
    default:
        fprintf(stderr, "Unsupported stop bits\n");
        return -1;
    }

    options.c_oflag &= ~OPOST;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    // 0x0D 0x0A
    options.c_iflag &= ~(INLCR | ICRNL | IGNCR);
    options.c_oflag &= ~(ONLCR | OCRNL);


    options.c_cc[VTIME] = 1;
    options.c_cc[VMIN] = 1;

    tcflush(fd, TCIFLUSH);

    if (tcsetattr(fd, TCSANOW, &options) != 0)
    {
        perror("com set error!\n");
        return -1;
    }
    return 0;
}

int uart_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
    if (uart_set(fd, speed, flow_ctrl, databits, stopbits, parity) == -1)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

int uart_read(int fd, char *rcv_buf, int data_len)
{
    int len, fs_sel;
    fd_set fs_read;

    struct timeval time;

    FD_ZERO(&fs_read);
    FD_SET(fd, &fs_read);

    time.tv_sec = 10;
    time.tv_usec = 0;

    fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);
    // printf("fs_sel = %d\n",fs_sel);
    if (fs_sel)
    {
        len = read(fd, rcv_buf, data_len);
        return len;
    }
    else
    {
        return 0;
    }
}

// int uart_write(int fd, char *send_buf, int data_len)
// {
//     int len = 0;

//     len = write(fd, send_buf, data_len);
//     if (len == data_len)
//     {
//         printf("send data: %s\n", send_buf);
//         return len;
//     }
//     else
//     {

//         tcflush(fd, TCOFLUSH);
//         return -1;
//     }
// }

void dump_cmd_frame(const char *buf, int len)
{
    int i;
    // printf("dump_cmd_frame: \n");
    for (i = 0; i < len; i++)
    {
        printf(" %02X", buf[i] & 0xFF);
    }
    printf("\n");
}

// MIYOO 282
#define MIYOO_AXIS_MAX_COUNT  16

#define MIYOO_PLAYER_MAGIC     0xFF
#define MIYOO_PLAYER_MAGIC_END 0xFE

#if 0
struct MIYOO_PAD_FRAME
{
    uint8_t magic;
    uint8_t axis0;
    uint8_t axis1;
    uint8_t magicEnd;
};
#define MIYOO_PAD_FRAME_LEN 4 // sizeof(struct MIYOO_PAD_FRAME)    =12 ????? why

#else
struct MIYOO_PAD_FRAME
{
    uint8_t magic;
    uint8_t unused0;
    uint8_t unused1;
    uint8_t axis0;
    uint8_t axis1;
    uint8_t magicEnd;
};
#define MIYOO_PAD_FRAME_LEN 6 // sizeof(struct MIYOO_PAD_FRAME)    =12 ????? why
#endif

static struct MIYOO_PAD_FRAME s_frame;
// static struct MIYOO_PAD_FRAME s_frame_last;

// static uint32_t s_pkio;
// static uint32_t s_pkio_last;
static int32_t s_miyoo_axis[MIYOO_AXIS_MAX_COUNT];
static int32_t s_miyoo_axis_last[MIYOO_AXIS_MAX_COUNT];

// static int key_pressed(int key)
// {
//     if (s_pkio & (1 << key) && !(s_pkio_last & (1 << key)))
//         return 1;
//     return 0;
// }

// static int key_released(int key)
// {
//     if (!(s_pkio & (1 << key)) && s_pkio_last & (1 << key))
//         return 1;
//     return 0;
// }

// static void check_key_event()
// {
//     int i;
//     for (i = 0; i < MIYOO_IO_MAX; i++)
//     {
//         if (key_pressed(i))
//         {
//             printf("pressed %s\n", s_btn_label[i]);
//             miyoo_report_key(EV_KEY, g_miyoo_key_event[i], 1);
//         }

//         if (key_released(i))
//         {
//             printf("released %s\n", s_btn_label[i]);
//             miyoo_report_key(EV_KEY, g_miyoo_key_event[i], 0);
//         }
//     }
// }

#if 0
#define MIYOO_ADC_MAX_X (200)
#define MIYOO_ADC_ZERO_X (137)
#define MIYOO_ADC_MIN_X (76)

#define MIYOO_ADC_MAX_Y (200)
#define MIYOO_ADC_ZERO_Y (135)
#define MIYOO_ADC_MIN_Y (72)

#define MIYOO_ADC_DEAD_ZONE (10)
#define MIYOO_AXIS_INT8_DRIFT (5)
#else

static int MIYOO_ADC_MAX_X = 200;
static int MIYOO_ADC_ZERO_X = 137;
static int MIYOO_ADC_MIN_X = 76;

static int MIYOO_ADC_MAX_Y = 200;
static int MIYOO_ADC_ZERO_Y = 135;
static int MIYOO_ADC_MIN_Y = 72;

static int MIYOO_ADC_DEAD_ZONE = 10;
static int MIYOO_AXIS_INT8_DRIFT = 5;

#define JOYPAD_CONFIG_FILE "/config/joypad.config"

static int getKeyValueDefault(const char *str, const char *key, int defaultValue)
{
    int index;
    char buf[16];
    const char *p = strstr(str, key);
    if (!p)
        return defaultValue;

    p = strchr(p, '=');
    if (!p)
        return defaultValue;

    memset(buf, 0, sizeof(buf));
    memcpy(buf, p + 1, 3);

    index = defaultValue;
    sscanf(buf, "%d", &index);
    return index;
}

int fileToMem(const char *path, void *data)
{
    int len, fileLen;
    FILE *fp = fopen(path, "r");
    if (!fp)
    {
        // printf("can not open %s\n", path);
        return 0;
    }
    fseek(fp, 0, SEEK_END);
    fileLen = ftell(fp);
    // printf("file %s len=%d\n", path, fileLen);
    fseek(fp, 0, SEEK_SET);
    len = fread(data, 1, fileLen, fp);
    fclose(fp);
    return len;
}

static void defaule_cal_config()
{
    printf("defaule_cal_config\n");
    MIYOO_ADC_MAX_X = 200;
    MIYOO_ADC_ZERO_X = 137;
    MIYOO_ADC_MIN_X = 76;

    MIYOO_ADC_MAX_Y = 200;
    MIYOO_ADC_ZERO_Y = 135;
    MIYOO_ADC_MIN_Y = 72;
}

static void miyoo_read_cal_config()
{
    char configBuf[4096];
    memset(configBuf, 0, sizeof(configBuf));
    fileToMem(JOYPAD_CONFIG_FILE, configBuf);

    // static int MIYOO_ADC_MAX_X  = 200;
    // static int MIYOO_ADC_ZERO_X = 137;
    // static int MIYOO_ADC_MIN_X  = 76;

    // static int MIYOO_ADC_MAX_Y  = 200;
    // static int MIYOO_ADC_ZERO_Y = 135;
    // static int MIYOO_ADC_MIN_Y  = 72;

    // x_min=83
    // x_max=195
    // y_min=74
    // y_max=226
    // x_zero=134
    // y_zero=148

    MIYOO_ADC_MIN_X = getKeyValueDefault(configBuf, "x_min", 76);
    MIYOO_ADC_MAX_X = getKeyValueDefault(configBuf, "x_max", 200);
    MIYOO_ADC_MIN_Y = getKeyValueDefault(configBuf, "y_min", 72);
    MIYOO_ADC_MAX_Y = getKeyValueDefault(configBuf, "y_max", 200);
    MIYOO_ADC_ZERO_X = getKeyValueDefault(configBuf, "x_zero", 137);
    MIYOO_ADC_ZERO_Y = getKeyValueDefault(configBuf, "y_zero", 135);

    if (MIYOO_ADC_MAX_X == MIYOO_ADC_ZERO_X || MIYOO_ADC_MIN_X == MIYOO_ADC_ZERO_X)
        defaule_cal_config();
    if (MIYOO_ADC_MAX_Y == MIYOO_ADC_ZERO_Y || MIYOO_ADC_MIN_Y == MIYOO_ADC_ZERO_Y)
        defaule_cal_config();

    printf("joystick read cal: [%d %d %d] [%d %d %d]\n",
           MIYOO_ADC_MIN_X,
           MIYOO_ADC_ZERO_X,
           MIYOO_ADC_MAX_X,
           MIYOO_ADC_MIN_Y,
           MIYOO_ADC_ZERO_Y,
           MIYOO_ADC_MAX_Y);
}
#endif

static int filterDeadzone(int newAxis, int oldAxis)
{
    // miyoo- 20220823 disable deadzone drift filter, already in MCU.
#if 0
    if(newAxis > MIYOO_ADC_DEAD_ZONE || newAxis < -MIYOO_ADC_DEAD_ZONE 
      || oldAxis > MIYOO_ADC_DEAD_ZONE || oldAxis < -MIYOO_ADC_DEAD_ZONE )
        return 0;

    if(abs(newAxis - oldAxis) < MIYOO_AXIS_INT8_DRIFT)
        return 1;
#endif
    return 0;
}

static int limitValue8(int value)
{
    if (value > 127)
        value = 127;
    else if (value < -128)
        value = -128;
    return value;
}

int g_lastX = 0;
int g_lastY = 0;
static void check_axis_event()
{
    int i;
    for (i = 0; i < MIYOO_AXIS_MAX_COUNT; i++)
    {
        if (s_miyoo_axis[i] != s_miyoo_axis_last[i])
        {
            if (!filterDeadzone(s_miyoo_axis[i], s_miyoo_axis_last[i]))
            {
                if (i == 0)
                {
                    g_lastX = limitValue8(s_miyoo_axis[i]);
                    // miyoo_report_abs(EV_ABS, ABS_X, g_lastX);
                }
                else if (i == 1)
                {
                    g_lastY = limitValue8(s_miyoo_axis[i]);
                    // miyoo_report_abs(EV_ABS, ABS_Y, g_lastY);
                }
                // printf("[raw ]\t%d \t %d\n", s_frame.axis0, s_frame.axis1);
                // printf("[asix]\t%d \t %d\n", g_lastX, g_lastY);
            }
        }
        s_miyoo_axis_last[i] = s_miyoo_axis[i];
    }
}

int miyoo_frame_to_axis_x(uint8_t rawX)
{
    int value = 0;
    if (rawX > MIYOO_ADC_ZERO_X)
        value = (rawX - MIYOO_ADC_ZERO_X) * 126 / (MIYOO_ADC_MAX_X - MIYOO_ADC_ZERO_X);

    if (rawX < MIYOO_ADC_ZERO_X)
        value = (rawX - MIYOO_ADC_ZERO_X) * 126 / (MIYOO_ADC_ZERO_X - MIYOO_ADC_MIN_X);

    if (value > 0 && value < MIYOO_ADC_DEAD_ZONE)
        return 0;

    if (value < 0 && value > -(MIYOO_ADC_DEAD_ZONE))
        return 0;

    return value;
}

int miyoo_frame_to_axis_y(uint8_t rawY)
{
    int value = 0;
    if (rawY > MIYOO_ADC_ZERO_Y)
        value = (rawY - MIYOO_ADC_ZERO_Y) * 126 / (MIYOO_ADC_MAX_Y - MIYOO_ADC_ZERO_Y);

    if (rawY < MIYOO_ADC_ZERO_Y)
        value = (rawY - MIYOO_ADC_ZERO_Y) * 126 / (MIYOO_ADC_ZERO_Y - MIYOO_ADC_MIN_Y);

    if (value > 0 && value < MIYOO_ADC_DEAD_ZONE)
        return 0;

    if (value < 0 && value > -(MIYOO_ADC_DEAD_ZONE))
        return 0;

    return value;
}

static void parser_miyoo_input(const char *cmd, int len)
{
    int i, p;
    // printf("len=%d, size=%d\n", len, sizeof(s_frame));
    if (!cmd || len < MIYOO_PAD_FRAME_LEN)
        return;

    for (i = 0; i < len - MIYOO_PAD_FRAME_LEN + 1; i += MIYOO_PAD_FRAME_LEN)
    {
        for (p = 0; p < MIYOO_PAD_FRAME_LEN - 1; p++)
        {
            if ((cmd[i] == MIYOO_PLAYER_MAGIC) && (cmd[i + MIYOO_PAD_FRAME_LEN - 1] == MIYOO_PLAYER_MAGIC_END))
            {
                memcpy(&s_frame, cmd + i, sizeof(s_frame));
                break;
            }
            else
                i++;
        }
    }

    s_miyoo_axis[ABS_X] = miyoo_frame_to_axis_x(s_frame.axis0);
    s_miyoo_axis[ABS_Y] = miyoo_frame_to_axis_y(s_frame.axis1);
    check_axis_event();
}

#define SERIAL_GAMEDECK ("/dev/ttyS0")
static pthread_t ntid;
static volatile int s_serial_run = 0;
static int s_fd = -1;
void *thread_serial_gamedeck(void *arg)
{
    int len;
    int i;
    char rcv_buf[100];
    printf("thread serial joystick\n");
    while (s_serial_run)
    {
        len = uart_read(s_fd, rcv_buf, 99);
        if (len > 0)
        {
            rcv_buf[len] = '\0';
            // dump_cmd_frame(rcv_buf, len);
            // parser_miyoo_input(rcv_buf, len);
            parser_miyoo_input(rcv_buf, len);
        }
        else
        {
            printf("cannot receive data\n");
        }
        usleep(14);
        // sleep(2);
    }
    uart_close(s_fd);
    s_fd = -1;
}

int miyoo_init_serial_input()
{
    int err;
    miyoo_read_cal_config();
    // miyoo_create_ukey();
    memset(&s_frame, 0, sizeof(s_frame));
    // memset(&s_frame_last, 0, sizeof(s_frame_last));

    memset(s_miyoo_axis, 0, sizeof(s_miyoo_axis));
    memset(s_miyoo_axis_last, 0, sizeof(s_miyoo_axis_last));

    s_fd = uart_open(SERIAL_GAMEDECK);
    err = uart_init(s_fd, 9600, 0, 8, 1, 'N');

    if (s_fd <= 0)
    {
        printf("open %s error.\n", SERIAL_GAMEDECK);
        return -1;
    }

    s_serial_run = 1;
    err = pthread_create(&ntid, NULL, thread_serial_gamedeck, NULL);
    return 0;
}

void miyoo_close_serial_input()
{
    s_serial_run = 0;
}