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

/*
 * dma_tty_read.h
 *
 *  Created on: 26.03.2022
 *      Author: preitinger
 */

// MOEGLICHE FLAGS FUER
///*
//  * When a break, frame error, or parity error happens, these codes are
//  * stuffed into the flags buffer.
//  */
// #define TTY_NORMAL      0
// #define TTY_BREAK       1
// #define TTY_FRAME       2
// #define TTY_PARITY      3
// #define TTY_OVERRUN     4

// frame: stop-bit nicht 1
// break: schwammig definiert, in der regel so: wenn mehr als 10 oder 12 bits rx='0' ist, wird es als break gelesen
// parity: falsche paritaetsbits, falls solche definiert
// overrun: koennte ich im fpga triggern, wenn der receive fifo full ist wenn ein byte hinzugefuegt werden soll; z.b. wenn definierter dma nicht schnell genug den rx fifo im fpga ausliest.

#ifndef DMAFRAMEWORK_DMA_TTY_READ_H_
#define DMAFRAMEWORK_DMA_TTY_READ_H_

#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kthread.h>

#include "../dmaFramework/dmaFramework.h"
#include "KM_uart_rx_buffer.h"
#include "fpgaCommands.h"
#include "fpgaMem.h"
#include "../KM_DmaChan_MappedIo.h"
#include "throttling.h"


#define CV_TTY_MINORS                              1
///**
// * DEPRECATED
// */
//#define TTY_RD_FIFO_SIZE                        1024
///**
// * DEPRECATED
// */
//#define TTY_RD_FIFO_ALMOST_EMPTY                 512

// 4 Parametersaetze fuer die DMA-Aktivitaet "RX Chunk"
// und
// 2 fuer die DMA-Aktivitaet "RX Count"
#define TR_PARAMSET_COUNT                      (4+2)

#ifdef TEST_UART_OFF_9
int32_t testUartOff9Buf = 0;
#endif

#ifdef TEST_UART_OFF_11
int32_t testUartOff11Buf = 0;
#endif

enum TRState {
    TR_ON,
    TR_EXITED
};

struct cvtty_port {
    struct tty_port port;
    struct mutex m;
//    int open_count;
    enum TRState st;
    /**
     * Zielpuffer fuer RX (Double-Buffering)
     */
    TUartRxBuffer rxBuf[2];
    /**
     * Index des gerade aktiven RX Zielpuffers. rxBuf[0] oder rxBuf[1]
     */
    int rxBufIdx;
    int paramsets[TR_PARAMSET_COUNT];
    char lastWrongFlag;
    int readSum;
    int throttleCount;
    CvThrottled_t throttling;
};

static const struct tty_port_operations null_ops = { };

static struct cvtty_port cv_port[CV_TTY_MINORS];
//
//static void trOnOpen(int index) {
//#if 1
//    struct cvtty_port* p = cv_port + index;
//
////    ASSERT(!mutex_is_locked(&p->m));
//    mutex_lock(&p->m);
//
//    switch (p->st) {
//        case TR_CLOSED:
////            p->open_count = 1;
//            break;
//
//        case TR_OPEN:
//            // no break
//        case TR_THROTTLED:
//            ++p->open_count;
//            break;
//
//        default:
//            ASSERT(false); // never
//    }
//
//    ASSERT(mutex_is_locked(&p->m));
//    mutex_unlock(&p->m);
//#endif
//}

//static void trOnClose(int index) {
//#if 1
//    struct cvtty_port* p = cv_port + index;
//
////    ASSERT(!mutex_is_locked(&p->m));
//    mutex_lock(&p->m);
//
//    switch (p->st) {
//        case TR_CLOSED:
//            ASSERT(false); // never
//            break;
//
//        case TR_OPEN:
//            // no break
//        case TR_THROTTLED:
//            --p->open_count;
//            ASSERT(p->open_count >= 0);
//            if (p->open_count == 0) {
////                if (p->lastWrongFlag != 0) {
////                    pr_info("lastWrongFlag: %d\n", (int) p->lastWrongFlag);
////                }
////                    pr_info("readSum: %d\n", (int) p->readSum);
//                p->st = TR_CLOSED;
//
//            }
//            break;
//
//        default:
//            ASSERT(false); // never
//    }
//
//    ASSERT(mutex_is_locked(&p->m));
//    mutex_unlock(&p->m);
//#endif
//}

#ifdef TEST_DMA_TTY_READ

struct TrTestData {
    struct task_struct* task;
    struct mutex m;
    bool testTaskExiting, testTaskStopping;
    int throttleCount, unthrottleCount;
    int writtenBytes;
};

static struct TrTestData trTestData = {
        .task = NULL,
        .testTaskExiting = false,
        .testTaskStopping = false,
        .throttleCount = 0,
        .unthrottleCount = 0
};

static int trTestTask(void *arguments) {
    int i;
    bool stopping = false;
    trTestData.writtenBytes = 0;

//    pr_info("trTestTask: vor ssleep(10)\n");
//    ssleep(10);
//    pr_info("trTestTask: nach ssleep(10)\n");

    for (i = 0; !kthread_should_stop(); ++i) {
#if 0
        bool shallBreak = false;

        mutex_lock(&trTestData.m);
        shallBreak = (trTestData.throttleCount > 0);
        mutex_unlock(&trTestData.m);

        if (shallBreak) {
            break;
        }
#endif

        {
            unsigned char* chars = 0;
            size_t size;
            int avail;
            unsigned int spaceAvail = 0;

            if (i % 1000 == 0) {
                pr_info("trTestTask: i=%d\n", i);
            }

            // Ansonsten schaufeln wir weiter Daten in den port
            size = TTY_RD_FIFO_ALMOST_EMPTY;
//            // TODO for test only 4
//            size = 4;
            // TODO test for 4096
            size = 4096;
            {
                spaceAvail = tty_buffer_space_avail(&cv_port[0].port);
                pr_info("tty_buffer_space_avail returns: %d\n", spaceAvail);
            }

            if (spaceAvail > 0) {
                avail = tty_prepare_flip_string(&cv_port[0].port, &chars, size);
                pr_info("avail=%d\n", (int) avail);

                if (avail > 0) {
                    ASSERT(chars);
                    memset(chars, 'A', avail);
                    chars[avail - 1] = '\n';
                    trTestData.writtenBytes += avail;
                }

                if (avail < size) {
                    pr_warn("avail=%d\n", avail);
                    ssleep(10);
                } else {
                    tty_flip_buffer_push(&cv_port[0].port);
                }
            } else {
                pr_info("schlafe 10s da spaceAvail=%d (writtenBytes=%d)\n", spaceAvail, trTestData.writtenBytes);
                ssleep(10);
            }

#if 1
            msleep(4);
#else
            msleep(500);

//            if (i >= 2) break; // TODO remove me
#endif
        }
    }

    pr_info("trTestTask before lock\n");
    mutex_lock(&trTestData.m);
    pr_info("trTestTask after lock\n");
    stopping = trTestData.testTaskStopping;
    if (!stopping) {
        trTestData.testTaskExiting = true;
    }
    mutex_unlock(&trTestData.m);
    pr_info("trTestTask after unlock\n");

    if (stopping) {
        // kthread_stop wird aufgerufen bzw. wird aufgerufen werden, also unbedingt warten, bis kthread_should_stop() true ist

        while (!kthread_should_stop()) {
            msleep(1);
        }
    }
    do_exit(4);

    return 3;

}

void trStartTestThread(void) {
    mutex_lock(&trTestData.m);
    pr_info("trStartTestThread\n");
    if (!trTestData.testTaskStopping) {
        trTestData.testTaskExiting = false;
        pr_info("before kthread_create\n");
        trTestData.task = kthread_create(trTestTask, &trTestData, "trTestTask");
        if (IS_ERR(trTestData.task)) {
            pr_err("trTestThread with fn trTestTask could not be started because of error %ld\n", (long) trTestData.task);
            return;
        }
        pr_info("trTestThread created.\n");
        wake_up_process(trTestData.task);
        pr_info("trTestThread waked up.\n");
    }

    mutex_unlock(&trTestData.m);
}

void trStopTestThread(void) {
    pr_info("trStopTestThread\n");

    if (!IS_ERR(trTestData.task)) {
        int res;
        bool shallStop = false;

        mutex_lock(&trTestData.m);
        if (!trTestData.testTaskExiting) {
            trTestData.testTaskStopping = true;
            if (trTestData.task) {
                shallStop = true;
            }
        } else {
            pr_info("testTask already exited or just exiting\n");
        }
        mutex_unlock(&trTestData.m);

        if (shallStop) {
            res = kthread_stop(trTestData.task);
            pr_info("kthread_stop returned %d\n", res);
        } else {
            pr_info("kthread_stop musste nicht aufgerufen werden, da der thread gar nicht erzeugt wurde\n");
        }

        mutex_lock(&trTestData.m);
        trTestData.task = NULL;
        trTestData.testTaskStopping = false;
        mutex_unlock(&trTestData.m);

    }

    pr_info("throttleCount=%d, unthrottleCount=%d\n", trTestData.throttleCount, trTestData.unthrottleCount);
}

#endif // TEST_DMA_TTY_READ

static int trAllocParamsets(struct cvtty_port* p) {
    int res, i, j;

    for (i = 0; i < TR_PARAMSET_COUNT; ++i) {
        p->paramsets[i] = my_edma_alloc_slot(-1);

        if (p->paramsets[i] < 0) {
            pr_err("paramsets[%d] konnte nicht allokiert werden!\n", i);

            for (j = i - 1; j >= 0; --j) {
                my_edma_free_slot(p->paramsets[j]);
                p->paramsets[j] = -1;
            }

            res = p->paramsets[i];
            p->paramsets[i] = -1;
            return res;
        }
    }

    return 0;
}

static void trFreeParamsets(struct cvtty_port* p) {
    int i;

    for (i = TR_PARAMSET_COUNT - 1; i >= 0; --i) {
        if (p->paramsets[i] >= 0) {
            if (!(p->paramsets[i] < 256)) {
                pr_err("Ungueltiger paramset[%d]: %d\n", i, p->paramsets[i]);
            }
            ASSERT(p->paramsets[i] < 256);
            my_edma_free_slot(p->paramsets[i]);
            p->paramsets[i] = -1;
        }
    }
}

static int testDumpTask(void* data) __attribute((unused));

static int testDumpTask(void* data) {
    uint16_t* p = (uint16_t*) data;
    int i;

    ssleep(10);

    pr_info("testDumpTask:\n");

    for (i = 0; i < 16; ++i) {
        pr_info("[%d] - 0x%08x\n", i, (int)p[i]);
    }

    return 0;
}

/**
 * realisiert wird nur ein port, naemlich /dev/cvtty0 (die anderen hoechstens fuer tests)
 * Der Port wird beim ersten open zu cvtty0 aktiviert (samt Leseaktivitaet im FPGA) und
 * beim letzten close deaktiviert.
 *
 * Benoetigter Platz im zentral allokierten DMA-Block werden ab dmaOffEnd verwendet, und zwar absteigend
 * Nach dem Aufruf zeigt dmaOffEnd auf das neue Ende des noch verfuegbaren DMA-faehigen Speicherbereichs.
 */
static int dma_tty_read_init(int32_t* dmaOffEnd) {
#if 1

    int res = 0;
    int i;
    int32_t oldDmaOffEnd = *dmaOffEnd;

#ifdef TEST_DMA_TTY_READ
    mutex_init(&trTestData.m);
    trTestData.task = NULL;
#endif

    for (i = 0; i < CV_TTY_MINORS; ++i) {
        struct cvtty_port* p = cv_port + i;

        mutex_init(&p->m);
        tty_port_init(&p->port);
        {
            int res2;
            res2 = tty_buffer_set_limit(&p->port, 4096 << 10);
            ASSERT(0 == res2);
            res2 = tty_buffer_request_room(&p->port, 4096 << 1);
            pr_info("res of request room: %d\n", res2);
        }
        pr_info("tty port mem limit: %d Byte\n", p->port.buf.mem_limit);
//        pr_info("p->port.ops nach init: %p\n", p->port.ops);
        p->port.ops = &null_ops;

        p->rxBufIdx = 0;
        p->st = TR_ON;
        p->lastWrongFlag = 0;
        p->readSum = 0;
        p->throttleCount = 0;
        cv_throttling_init(&p->throttling);
    }


#if 0 // TODO BEGIN  remove after testing
    {
        int res;
        const char* testData = "Bla1";
        res = tty_insert_flip_string(&cv_port->port, testData, 4);
        pr_info("res of insert_flip_string(\"Bla\"): %d\n", res);
        tty_flip_buffer_push(&cv_port->port);
    }
#endif // TODO END  remove after testing


//    pr_info("vor uart_rx_buffer_create\n");
    res = uart_rx_buffer_create(&cv_port[0].rxBuf[0], dmaOffEnd);
//    pr_info("nach uart_rx_buffer_create: res=%d\n", res);

    if (res) {
        goto undo_nothing;
    }

    res = uart_rx_buffer_create(&cv_port[0].rxBuf[1], dmaOffEnd);

    if (res) {
        goto undo_uart_rx_buffer_create_a;
    }

    res = trAllocParamsets(&cv_port[0]);

    if (res) {
        goto undo_uart_rx_buffer_create_b;
    }



    {
        // Parametersaetze initialisieren
        // siehe: CV_Gen2.Softwarearchitektur.Zusammenarbeit Userspace-Kernel-FPGA.COM im FPGA.Analyse UART RX mit DMA

        int* paramsets = cv_port[0].paramsets;
        const int payloadChanNum = edma_channel_num(payloadKMCh);
        // Parametersaetze fuer den Vorgang "RX Chunk":
        const int psCopyPsRxData = paramsets[0],
                psCopyPsRxFlags = paramsets[1],
                psRxData = paramsets[2],
                psRxFlags = paramsets[3];
        // Parametersaetze fuer den Vorgang "RX Count":
        const int psRxCountRespType = paramsets[4],
                psRxDstCount = paramsets[5];
#if 0
        int32_t dmaOffTestDest;
        uint32_t dmaTestDest;
        char* cpuTestDest;
#endif
        ASSERT(5 < TR_PARAMSET_COUNT);

        pr_info("psCopyPsRxData=%d (%x)\npsCopyPsRxFlags=%d (%x)\npsRxData=%d (%x)\npsRxFlags=%d (%x)\n", psCopyPsRxData, psCopyPsRxData << 5, psCopyPsRxFlags, psCopyPsRxFlags << 5, psRxData, psRxData << 5, psRxFlags, psRxFlags << 5);
        pr_info("psRxCountRespType=%d (%x)\n", psRxCountRespType, psRxCountRespType << 5);
        pr_info("payloadChanNum=%d\n", payloadChanNum);
        pr_info("src in psCopyPsRxData wird gesetzt auf: 0x%x\n", dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA));
        pr_info("dst in psCopyPsRxData wird gesetzt auf: 0x%x\n", dmaAddrEdmaParamset(psRxData));
        pr_info("mappedCpu=0x%08x, mappedDma=0x%08x, mappedSize=0x%08x\n", (int)dmaAddressesEnv.mappedCpu, (int)dmaAddressesEnv.mappedDma, (int)dmaAddressesEnv.mappedSize);

//        ASSERT(*dmaOffEnd <= 0);
//        *dmaOffEnd -= 0x40;
//        // evtl. nicht durch 4 teilbar, also aufrunden
//        while ((*dmaOffEnd & 3) != 0) {
//            ++*dmaOffEnd;
//        }
//        dmaOffTestDest = *dmaOffEnd;
//        pr_info("dmaOffTestDest: %d\n", (int)dmaOffTestDest);
//        dmaTestDest = dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + dmaOffTestDest;
//        pr_info("dmaAddressesEnv.mappedSize + dmaOffTestDest=%d\n", (int) (dmaAddressesEnv.mappedSize + dmaOffTestDest));
//        cpuTestDest = ((char*) dmaAddressesEnv.mappedCpu + (dmaAddressesEnv.mappedSize + dmaOffTestDest));
//        pr_info("cpuTestDest=%p, dmaTestDest=0x%08x\n", cpuTestDest, dmaTestDest);
//        memset(cpuTestDest, 0xff, 0x20);
//        {
//            // in 10 s Inhalt bei cpuTestDest ausgeben lassen
//            struct task_struct* task = kthread_create(testDumpTask, cpuTestDest, "testDumpTask");
//            if (IS_ERR(task)) {
//                pr_err("testDumpTask with fn testDumpTask could not be started because of error %ld\n", (long) task);
//                return -1;
//            }
//            pr_info("testDumpTask created.\n");
//            wake_up_process(task);
//            pr_info("testDumpTask waked up.\n");
//
//        }

        // Parametersaetze fuer den Vorgang "RX Chunk":
        {
            // read ram_km[RX_PS_DATA], ff.
            // write paramset psRxData
            struct edmacc_param ps = {
#ifdef TEST_UART_OFF_4
                    // bei diesem Test nur verketten zu pkmChainBackPs, keine reads vom fpga durchfuehren
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
                    .src = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                    .a_b_cnt = COMBINE_LO_HI(2, 0), // wird im FPGA vor jedem dma-trigger beschrieben, acnt dabei immer = 2
                    .dst = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0, // spielt bei ccnt == 1 keine rolle
                    .ccnt = 1
#elif defined(TEST_UART_OFF_6)

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddrEdmaParamset(psRxData),
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1

#elif defined(TEST_UART_OFF_7)

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddrEdmaParamset(psRxData),
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 0

#elif defined(TEST_UART_OFF_8)

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA),
                    .a_b_cnt = COMBINE_LO_HI(2, 4),
#if 1
                    .dst = dmaAddrEdmaParamset(psRxData),
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1

#elif defined(TEST_UART_OFF_9)


#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = /*dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + testUartOff9Buf, //*/ dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + testUartOff9Buf,
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1

#elif defined(TEST_UART_OFF_10)

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = FPGA_ADDR_BASE | ADDR_READ_APPLICATOR,
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddrEdmaParamset(psRxData),
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1

#elif defined(TEST_UART_OFF_11)

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = FPGA_ADDR_BASE | ADDR_READ_APPLICATOR,
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + (u32) testUartOff11Buf,
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,//32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1

#else

#if 1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_DATA),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
#if 1
                    .dst = dmaAddrEdmaParamset(psRxData),
#else
                    .dst = dmaTestDest,
#endif
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
#if 1
                    .link_bcntrld = COMBINE_LO_HI(psCopyPsRxFlags << 5, 0),
#else // testweise alles ueberspringen
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
#endif
                    .src_dst_cidx = 0,//32, // spielt bei Kent == 1 eigentlich keine rolle
                    .ccnt = 1

#endif
            };

//            pr_info("vor write_slot: psCopyPsRxData=%d\n", psCopyPsRxData);
            my_edma_write_slot(edmaMappedIo1._a, psCopyPsRxData, &ps);
        }

        {
            // read ram_km[RX_PS_FLAGS], ff.
            // write paramset psRxFlags
            struct edmacc_param ps = {
#ifdef TEST_UART_OFF_5

#ifdef TEST_RX_CHUNK_BREAK
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_FLAGS),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
                    .dst = dmaAddrEdmaParamset(psRxFlags),
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 16),
                    .src_dst_cidx = 32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1
#else

#ifdef TEST_RX_CHUNK_BREAK
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum),
#else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_PS_FLAGS),
                    .a_b_cnt = COMBINE_LO_HI(2, 16),
                    .dst = dmaAddrEdmaParamset(psRxFlags),
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(psRxData << 5, 16),
                    .src_dst_cidx = 32, // spielt bei ccnt == 1 eigentlich keine rolle
                    .ccnt = 1
#endif
            };

//            pr_info("vor write_slot: psCopyPsRxFlags=%d\n", psCopyPsRxFlags);
            my_edma_write_slot(edmaMappedIo1._a, psCopyPsRxFlags, &ps);
        }


//        { // dieser sollte eigentlich komplett vom fpga kopiert werden, aber irgendwie funktioniert das nicht
//            // verdacht ist dass es an dem static bit und der tatsache dass gerade ein dma-transfer statt findet liegen koennte.
//            // also auch den parametersatz direkt mit was "sinnvollem" vorbereiten nicht nur den ram_km-block
//
//            // read data from ram_km as specified in psRxData (copied from ram_km[RX_PS_DATA])
//            // write data to destination buffer as specified in psRxData
//            struct edmacc_param ps = {
//                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
//                    .src = 0, // wird im FPGA vor jedem dma-trigger beschrieben
//                    .a_b_cnt = COMBINE_LO_HI(2, 0), // wird im FPGA vor jedem dma-trigger beschrieben, acnt dabei immer = 2; fpga verlaesst sich auf acnt = 2 und ueberschreibt nur 16-bit-wort mit bcnt!
//                    .dst = 0, // wird im FPGA vor jedem dma-trigger beschrieben
//                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
//#if 0
//                    .link_bcntrld = COMBINE_LO_HI(psRxFlags << 5, 0),
//#else
//                    .link_bcntrld = COMBINE_LO_HI(0xffff, 0),
//#endif
//                    .src_dst_cidx = 0, // spielt bei ccnt == 1 keine rolle
//                    .ccnt = 2
//            };
//
//            pr_info("vor write_slot: psRxData=%d\n", psRxData);
//            my_edma_write_slot(edmaMappedIo1._a, psRxData, &ps);
//        }


        // Parametersaetze fuer den Vorgang "RX Count":
        {
            struct edmacc_param ps = {
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
                    .src = dmaReadAddrFpgaRamKm(FRKM_RESPONSE_TYPE),
                    .a_b_cnt = COMBINE_LO_HI(2, 1),
                    .dst = dmaBufEndAddr(dmaAddressesEnv.mappedDma, dmaAddressesEnv.mappedSize, DBE_RESPONSE_TYPE),
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(psRxDstCount << 5, 0),
                    .src_dst_cidx = 0,
                    .ccnt = 1
            };

//            pr_info("vor write_slot: psRxCountRespType=%d\n", psRxCountRespType);
            my_edma_write_slot(edmaMappedIo1._a, psRxCountRespType, &ps);
        }

        {
            struct edmacc_param ps = {
#if 0 // TEST
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
#else // TODO ORIGINAL
# ifndef TEST_UART_OFF_1
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN | TCINTEN,
# else
                    .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
# endif
#endif
                    .src = dmaReadAddrFpgaRamKm(FRKM_RX_DST_COUNT),
                    .a_b_cnt = COMBINE_LO_HI(2, 1),
                    .dst = dmaBufEndAddr(dmaAddressesEnv.mappedDma, dmaAddressesEnv.mappedSize, DBE_RX_COUNT),
                    .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                    .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                    .src_dst_cidx = 0,
                    .ccnt = 1
            };

//            pr_info("vor write_slot: psRxDstCount=%d\n", psRxDstCount);
            my_edma_write_slot(edmaMappedIo1._a, psRxDstCount, &ps);
        }
    }

#ifdef TEST_UART_OFF_9
    *dmaOffEnd -= 32;
    testUartOff9Buf = *dmaOffEnd;
#endif

#ifdef TEST_UART_OFF_11
    *dmaOffEnd -= 32;
    testUartOff11Buf = *dmaOffEnd;
    {
        uint8_t* p = (uint8_t*) dmaAddressesEnv.mappedCpu + dmaAddressesEnv.mappedSize + testUartOff11Buf;
        memset(p, 0, 32);
    }
#endif



    goto undo_nothing;

#if 0 // vorbereitet, falls spaeter neue aktion hinzugefuegt wird, bei deren fehlschlag dies hier rueckgaengig zu machen ist
    undo_allocParamsets: // ueberfluessig, ausser wenn spaeter neue aktion angefuegt wird
    trFreeParamsets(&cv_port[0]);
#endif

    undo_uart_rx_buffer_create_b:

    uart_rx_buffer_destroy(&cv_port[0].rxBuf[1]);

    undo_uart_rx_buffer_create_a:

    *dmaOffEnd = oldDmaOffEnd;
    uart_rx_buffer_destroy(&cv_port[0].rxBuf[0]);

    undo_nothing:

    return res;
#else
    return 0;
#endif
}

static void dma_tty_read_prepare_ram_km(void) {
    int* paramsets = cv_port[0].paramsets;
    const int payloadChanNum = edma_channel_num(payloadKMCh);
    // Parametersaetze fuer den Vorgang "RX Chunk":
    const int psCopyPsRxData = paramsets[0],
            psRxFlags = paramsets[3];
    // Parametersaetze fuer den Vorgang "RX Count":
    const int psRxCountRespType = paramsets[4];

    ASSERT(5 < TR_PARAMSET_COUNT);

    {
        // read data from ram_km as specified in psRxData (copied from ram_km[RX_PS_DATA])
        // write data to destination buffer as specified in psRxData
        struct edmacc_param ps = {
                .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
                .src = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                .a_b_cnt = COMBINE_LO_HI(2, 0), // wird im FPGA vor jedem dma-trigger beschrieben, acnt dabei immer = 2; fpga verlaesst sich auf acnt = 2 und ueberschreibt nur 16-bit-wort mit bcnt!
                .dst = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                .link_bcntrld = COMBINE_LO_HI(psRxFlags << 5, 0),
                .src_dst_cidx = 0, // spielt bei ccnt == 1 keine rolle
                .ccnt = 1
        };

        // Da dieser Parametersatz im FPGA angepasst wird, bevor er dynamisch auf psRxData
//        pr_info("vor ramKmWrite auf FRKM_RX_PS_DATA\n");
        ramKmWrite(FRKM_RX_PS_DATA, &ps, sizeof(ps));
    }

    {
        // read data from ram_km as specified in psRxFlags (copied from ram_km[RX_PS_FLAGS])
        // write data to destination buffer as specified in psRxFlags
        struct edmacc_param ps = {
                .opt = SYNCDIM | EDMA_TCC(payloadChanNum) | TCCHEN,
                .src = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                .a_b_cnt = COMBINE_LO_HI(2, 0), // wird im FPGA vor jedem dma-trigger beschrieben, acnt dabei immer = 2
                .dst = 0, // wird im FPGA vor jedem dma-trigger beschrieben
                .src_dst_bidx = COMBINE_LO_HI(2, 2), // == ACNT
                .link_bcntrld = COMBINE_LO_HI(pkmChainBackPs << 5, 0),
                .src_dst_cidx = 0, // spielt bei ccnt == 1 keine rolle
                .ccnt = 1
        };

        // Da dieser Parametersatz im FPGA angepasst wird, bevor er dynamisch auf psRxData
//        pr_info("vor ramKmWrite auf FRKM_RX_PS_FLAGS\n");
        ramKmWrite(FRKM_RX_PS_FLAGS, &ps, sizeof(ps));
    }

//    pr_info("vor ramKmWrite16 auf FRKM_INIT_RX_CHUNK_LINK\n");
    ramKmWrite16(FRKM_INIT_RX_CHUNK_LINK, psCopyPsRxData << 5);

    ramKmWrite16(FRKM_INIT_RX_COUNT_LINK, psRxCountRespType << 5);


    { // fuer uart-rx-dma-transfers relevante Felder im ram_km im FPGA initialisieren:

        TUartRxBuffer* b0 = &cv_port[0].rxBuf[0];
        TUartRxBuffer* b1 = &cv_port[0].rxBuf[1];
        pr_info("(dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + b0->dmaOffData)=0x%8x\n", (int) ((dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + b0->dmaOffData)));
        pr_info("schreibe in FRKM_INIT_RX_DMA_DST_BASE: 0x%x\n", (int) ((dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + b0->dmaOffData) & ~0x0000ffff));
        ramKmWrite32(FRKM_INIT_RX_DMA_DST_BASE, (dmaAddressesEnv.mappedDma + dmaAddressesEnv.mappedSize + b0->dmaOffData) & ~0x0000ffff);
        memset(dmaAddressesEnv.mappedCpu + dmaAddressesEnv.mappedSize + b0->dmaOffData, 0xba, RX_DST_CAPACITY);


//        ramKmWrite16(FRKM_INIT_RX_START_PS_A, a->psCopyPsData << 5);
//        ramKmWrite16(FRKM_INIT_RX_START_PS_B, b->psCopyPsData << 5);
#if 1 // original
        ramKmWrite16(FRKM_INIT_RX_DST_DATA_0, dmaAddressesEnv.mappedSize + b0->dmaOffData);
#else // Test
        ramKmWrite16(FRKM_INIT_RX_DST_DATA_0, dmaAddressesEnv.mappedSize + b1->dmaOffData);
#endif
        ramKmWrite16(FRKM_INIT_RX_DST_DATA_1, dmaAddressesEnv.mappedSize + b1->dmaOffData);
        ramKmWrite16(FRKM_INIT_RX_DST_FLAGS_0, dmaAddressesEnv.mappedSize + b0->dmaOffFlags);
        ramKmWrite16(FRKM_INIT_RX_DST_FLAGS_1, dmaAddressesEnv.mappedSize + b1->dmaOffFlags);
        // TODO pruefen und ggf. FRKM_INIT_RX_* vervollstaendigen

    }

}

static void dma_tty_read_exit(void) {

#ifdef TEST_UART_OFF_11
    pr_info("Inhalt von testUartOff11Buf:\n");
    {
        int i;
        uint8_t* p8 = (uint8_t*) dmaAddressesEnv.mappedCpu + dmaAddressesEnv.mappedSize;
        for (i = 0; i < 8; ++i) {
            uint32_t x = *p8++;
            x <<= 8;
            x |= *p8++;
            x <<= 8;
            x |= *p8++;
            x <<= 8;
            x |= *p8++;
            pr_info("[%d]: %8x\n", i, x);
        }
    }
#endif

#if 1
//    pr_info("dma_tty_read_exit\n");

    pr_info("dma_tty_read_exit: throttleCount=%d\n", cv_port[0].throttleCount);
    trFreeParamsets(&cv_port[0]);

    uart_rx_buffer_destroy(&cv_port[0].rxBuf[1]);
    uart_rx_buffer_destroy(&cv_port[0].rxBuf[0]);

    {
        int i;

        for (i = CV_TTY_MINORS - 1; i >= 0; --i) {
            struct cvtty_port* p = cv_port + i;
            tty_port_destroy(&p->port);
            mutex_destroy(&p->m);
        }
    }

#ifdef TEST_DMA_TTY_READ
    mutex_destroy(&trTestData.m);
#endif

#endif
}

static void dma_tty_read_onRespRxCount(void) {
    // Daten vom gerade aktiven RX-Zielpuffer in die internen TTY-line-discipline-Datenstruktur schaufeln.

    struct cvtty_port* p;
    TUartRxBuffer* rxBuf;
    unsigned char* chars;
    char* flags;
    size_t size;
    int res;
    static int count = 0;
//    int i;

    p = &cv_port[0];

//    ASSERT(!mutex_is_locked(&p->m));
    mutex_lock(&p->m);

    switch (p->st) {
        case TR_EXITED:
            pr_err("rx data arrived in state TR_EXITED\n");
            // in diesem Zustand die Daten ignorieren
            break;

        default:
            rxBuf = p->rxBuf + p->rxBufIdx;
            chars = cpuBufEndAddr(dmaAddressesEnv.mappedCpu, dmaAddressesEnv.mappedSize, rxBuf->dmaOffData);
            flags = cpuBufEndAddr(dmaAddressesEnv.mappedCpu, dmaAddressesEnv.mappedSize, rxBuf->dmaOffFlags);
//            msleep(1);
            size = (size_t) *((uint16_t*) cpuBufEndAddr(dmaAddressesEnv.mappedCpu, dmaAddressesEnv.mappedSize, DBE_RX_COUNT));
//            if (size > 0) {
//                pr_info("size=%d\n", (int) size);
//            }

            ASSERT(size >= 0); // nicht '>' wegen timeout, koennte man evtl. noch optimieren dass das fpga dann keine dmas durchfuehrt solange zielpuffer komplett leer aber das waere aufwaendigere logik...
            count += size;
//            pr_info("count=%d\n", count);

#if 0
            for (i = 0; i < size; ++i) {
                if (unlikely(0 != (uint8_t) flags[i])) {
                    p->lastWrongFlag = flags[i];
//                    pr_err("i=%d: flag %d, statt 0\n", i, (int) flags[i]);
                }
            }
#endif

            if (size > 0) {
                res = tty_insert_flip_string_flags(&p->port, chars, flags, size);
                p->readSum += size;

                if (unlikely((int) size != res)) {
                    pr_err("size=%d, res=%d\n", (int) size, (int) res);
                }
                ASSERT((int) size == res);
                tty_flip_buffer_push(&p->port);
//                pr_info("pushed to flip buffer\n");
//                pr_info("nach buffer push: limit=%d, used=%d\n", p->port.buf.mem_limit, atomic_read(&p->port.buf.mem_used));
            }

            break;
    }

    p->rxBufIdx = 1 - p->rxBufIdx;
    ASSERT(p->rxBufIdx >= 0 && p->rxBufIdx <= 1);

    ASSERT(mutex_is_locked(&p->m));
    mutex_unlock(&p->m);

    // nun dies nicht mehr, da jede response generell mit resp ack command bestaetigt wird
//    DmaChan_MappedIo_set16(&fpgaMappedIo, ADDR_OTH_KM, create_rx_count_ack());
}

static void trOnThrottle(int index) {
    struct cvtty_port* p;

    ASSERT(index >= 0 && index < CV_TTY_MINORS);
    p = &cv_port[index];

    cv_throttling_kernel_throttle(&p->throttling);
    ++p->throttleCount;
}

static void trOnUnthrottle(int index) {
    struct cvtty_port* p;

    ASSERT(index >= 0 && index < CV_TTY_MINORS);
    p = &cv_port[index];

    cv_throttling_kernel_unthrottle(&p->throttling);
}

static void trOnShutdown(int index) {
    struct cvtty_port* p;

    ASSERT(index >= 0 && index < CV_TTY_MINORS);
    p = &cv_port[index];

    cv_throttling_shutdown(&p->throttling);

}


#endif /* DMAFRAMEWORK_DMA_TTY_READ_H_ */
