/***************************************************************************/
/*                             Druckersoftware                             */
/*                    Copyright (C) Carl Valentin GmbH                     */
/***************************************************************************/

/*
 * uart_test_dev.h
 *
 *  Created on: 18.07.2022
 *      Author: preitinger
 *
 *
 *  character device um Testinhalte in den tty flip buffer
 *  zu schreiben so wie wenn der Inhalt vom FPGA-Uart per RX empfangen worden waere.
 *  So kann z.B. Reaktion auf xon/xoff d.h. Start/Stop-Bytes getestet werden etc.
 */

#ifndef FPGASERIAL_UART_TEST_DEV_H_
#define FPGASERIAL_UART_TEST_DEV_H_

#include <linux/module.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include "../cv_char_dev_simple.h"


static ssize_t
utd_sim_rx_write(struct file *file, const char __user *buf, size_t count,
           loff_t *ppos);

static ssize_t
utd_sim_rx_flag_write(struct file *file, const char __user *buf, size_t count,
           loff_t *ppos);

static struct UtdGlobals {
    t_cv_char_dev_simple sim_rx, sim_rx_flag;
    struct file_operations sim_rx_fops, sim_rx_flag_fops;
    struct tty_port* port;
} utd_globals = {
        .sim_rx_fops = {
                .write = utd_sim_rx_write,
                .owner = THIS_MODULE
        },
        .sim_rx_flag_fops = {
                .write = utd_sim_rx_flag_write,
                .owner = THIS_MODULE
        }
};


static int utd_init(struct tty_port* port) {
    struct UtdGlobals* p = &utd_globals;
    int err = 0;

    p->port = port;

    err = cv_cds_create(&p->sim_rx, "FPGA0_sim_rx", &p->sim_rx_fops);

    if (err) {
        goto err0;
    }

    err = cv_cds_create(&p->sim_rx_flag, "FPGA0_sim_rx_flag", &p->sim_rx_flag_fops);

    if (err) {
        goto err1;
    }
    goto err0;

    err1:
    cv_cds_delete(&p->sim_rx);

    err0:
    return err;

}

static void utd_exit(void) {
    struct UtdGlobals* p = &utd_globals;

    cv_cds_delete(&p->sim_rx_flag);
    cv_cds_delete(&p->sim_rx);

}

static ssize_t
utd_sim_rx_write(struct file *file, const char __user *buf, size_t count,
           loff_t *ppos) {

    struct UtdGlobals* p = &utd_globals;
    int res;
    unsigned char* dst;
    struct tty_port* port = p->port;

    res = tty_prepare_flip_string(port, &dst, count);
    res -= copy_from_user(dst, buf, res);
    tty_flip_buffer_push(p->port);
    pr_info("utd_write: count=%d, res=%d\n", count, res);
    return res;
}

static ssize_t
utd_sim_rx_flag_write(struct file *file, const char __user *buf, size_t count,
           loff_t *ppos) {

    struct UtdGlobals* p = &utd_globals;
    char c;
    int res;
    int flag;


    if (count > 0) {
        res = copy_from_user(&c, buf, 1);

        if (res) {
            return -EIO;
        }

        switch (c) {
            case 'b': case 'B': // break
                flag = TTY_BREAK;
                break;
            case 'f': case 'F': // frame
                flag = TTY_FRAME;
                break;
            case 'p': case 'P': // parity
                flag = TTY_PARITY;
                break;
            case 'o': case 'O': // overrun
                flag = TTY_OVERRUN;
                break;
            default:
                pr_err("Do not understand flag: '%c'\n", c);
                return -EIO;
                break;
        }

        pr_info("before insert flip char\n");
        if (1 != tty_insert_flip_char(p->port, 0, flag)) {
            pr_info("Could not process flag: '%c'\n", c);
            return -EIO;
        }

        pr_info("before flip buffer push\n");
        tty_flip_buffer_push(p->port);

        return count;
    } else {
        return 0;
    }
}


#endif /* FPGASERIAL_UART_TEST_DEV_H_ */
