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

/*
 * Edma_neu.h
 *
 *  Created on: Dec 6, 2017
 *      Author: preitinger
 */

#ifndef EDMA_NEU_H_
#define EDMA_NEU_H_

/*. IMPORT ================================================================*/
#include "KM_DmaChan_ControlModulAccess.h"
#include "KM_CvKernelAccess.h"
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/dmaengine.h>
#include <linux/edma.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "PR.h"
#include "JobApi_messages.h"
/*. ENDIMPORT =============================================================*/

/*. EXPORT ================================================================*/

/*-------------------------------- Makros ---------------------------------*/
#define DMA_CHAN_CLASS_NAME2 "KM_Chan2"
#define DMA_CHAN_DEV_NAME2 "cv_dma_channels2"
#define DMA_PARAM_SET_CLASS_NAME2 "KM_ParamSet2"
#define DMA_PARAM_SET_DEV_NAME2 "cv_dma_paramsets2"

// cv_dma_channels2 wird grob beschrieben so verwendet:
// Wenn ueber die JobApi ein DMA-Kanal allokiert wird, wird ein file handle
// geoeffnet fuer "/dev/cv_dma_channels2"
// Dann wird ueber write optional ein bestimmter oder beliebiger Kanal fest gelegt.
// Und der allokierte Kanal ueber read ausgelesen.
// Wenn der Kanal freigegeben werden soll, wird ueber das zugehoerige
// File-Handle mit write das Kommando CV_CHAN_FREE gesendet, und das KM gibt den Kanal frei.

#define STORED_STATUS_CAPACITY 128


static void JobApi_handleCrash(void);


/* PaRAM slots are laid out like this */

//struct edmacc_opt {
//    int sam:1;
//    int dam:1;
//    int syncdim:1;
//    int staticSet:1;
//    int reserved0:4;
//    int fwid:3;
//    int tccmode:1;
//    int tcc:6;
//    int reserved1:2;
//    int tcinten:1;
//    int itcinten:21;
//    int tcchen:1;
//    int itcchen:1;
//    int privid:4;
//    int reserved2:3;
//    int priv:1;
//} __packed;

struct edmacc_param {
//        struct edmacc_opt opt;
    u32 opt;
    u32 src;
    u32 a_b_cnt;
    u32 dst;
    u32 src_dst_bidx;
    u32 link_bcntrld;
    u32 src_dst_cidx;
    u32 ccnt;
} __packed;

/* Offsets matching "struct edmacc_param" */
#define PARM_OPT                0x00
#define PARM_SRC                0x04
#define PARM_A_B_CNT            0x08
#define PARM_DST                0x0c
#define PARM_SRC_DST_BIDX       0x10
#define PARM_LINK_BCNTRLD       0x14
#define PARM_SRC_DST_CIDX       0x18
#define PARM_CCNT               0x1c

#define PARM_SIZE               0x20

/* fields in edmacc_param.opt */
#define SAM             BIT(0)
#define DAM             BIT(1)
#define SYNCDIM         BIT(2)
#define STATIC          BIT(3)
#define EDMA_FWID       (0x07 << 8)
#define TCCMODE         BIT(11)
#define EDMA_TCC(t)     ((t) << 12)
#define TCINTEN         BIT(20)
#define ITCINTEN        BIT(21)
#define TCCHEN          BIT(22)
#define ITCCHEN         BIT(23)

#define COMBINE_LO_HI(lo, hi) ((u32) (lo) | ((u32) (hi) << 16))

/*--------------------------- Typdeklarationen ----------------------------*/

typedef enum EDmaChan2State {
    DCS2_PROVIDE_CHAN,
    DCS2_ACTIVE,
//    DCS2_SHUTDOWN,
    DCS2_ERROR
} DmaChan2State;

typedef struct SDmaChan2
{
    int ch;
    struct dma_chan* chan;
    struct completion comp;
    DmaChan2State st;
#ifdef DMA_ALT
    uint16_t storedStatus[STORED_STATUS_CAPACITY];
#else
    enum dma_status storedStatus[STORED_STATUS_CAPACITY];
#endif
    uint16_t storedStatusSize;
} DmaChan2;

typedef struct SDmaParamSet2
{
    int nr;
} DmaParamSet2;

/*------------------------------ Prototypen -------------------------------*/

static int
Edma_neu_init(void);

static void
Edma_neu_exit(void);

static int
DmaChan2_create(DmaChan2* p, int ch);

static void
DmaChan2_delete(DmaChan2* p);

static int
DmaParamSet2_create(DmaParamSet2* p, int nr);

static void
DmaParamSet2_delete(DmaParamSet2* p);

static int
DmaChan2_open(struct inode * n, struct file * f);

static int
DmaChan2_release(struct inode * n, struct file * f);

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

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


static int
DmaParamSet2_open(struct inode * n, struct file * f);

static int
DmaParamSet2_release(struct inode * n, struct file * f);

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

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

/*------------------------ Konstantendeklarationen ------------------------*/

/*------------------------ Variablendeklarationen ------------------------*/

static struct dma_chan* start1Chan = NULL;
static struct dma_chan* adcChan = NULL;


static struct class* dmaChanClass2 = NULL;
static struct class* dmaParamSetClass2 = NULL;
static MyDevice dmaChanDev2, dmaParamSetDev2;
static unsigned long usedChans[2];

static struct file_operations dmaChanOps2 =
{
        .open = DmaChan2_open,
        .release = DmaChan2_release,
        .write = DmaChan2_write,
        .read = DmaChan2_read,
};

static struct file_operations dmaParamSetOps2 = {
        .open = DmaParamSet2_open,
        .release = DmaParamSet2_release,
        .write = DmaParamSet2_write,
        .read = DmaParamSet2_read
};

static DmaChan_MappedIo edmaMappedIo1;


/*. ENDEXPORT =============================================================*/


/*
 * paRAM slot management functions
 */
static void my_edma_write_slot(void __iomem* edmaBase, int slot,
                const struct edmacc_param *param)
{
    slot = EDMA_CHAN_SLOT(slot);

    if (slot >= 256) {
        pr_err("Illegal slot: %d\n", slot);
        return;
    }

//    pr_info("edmaBase: %x\n", (int) edmaBase);
//    pr_info("parm->src: 0x%x, PARM_SIZE=0x%x\n", param->src, PARM_SIZE);
//    pr_info("offset in memcpy_toio: 0x%x\n", (slot << 5));
//    pr_info("vor memcpy in my_edma_write_slot for slot=%d\n", slot);
    memcpy_toio(edmaBase + 0x4000 + (slot << 5), param, PARM_SIZE);
//    pr_info("nach memcpy in my_edma_write_slot\n");
}

static int my_edma_read_slot(void __iomem* edmaBase, int slot,
               struct edmacc_param *param) __attribute__((unused));

static int my_edma_read_slot(void __iomem* edmaBase, int slot,
               struct edmacc_param *param)
{
    slot = EDMA_CHAN_SLOT(slot);
    if (slot >= 256)
        return -EINVAL;
    memcpy_fromio(param, edmaBase + 0x4000 + (slot << 5), PARM_SIZE);

    return 0;
}

static void writeStartParamSets(struct edmacc_param* start1, struct edmacc_param* start2, struct edmacc_param* start3, int slot1, int slot2, int slot3) __attribute((unused));

/**
 * setzt die Edma-Parametersaetze start1, start2 und start3 so auf:
 * start1 liest ein 16Bit-Wort vom FPGA an Adresse ADDR_READ_DMA_TCC_LO und schreibt es in das
 * OPT-low-Wort des Parametersatzes start3.
 * start2 liest ein 16Bit-Wort vom FPGA an Adresse ADDR_READ_DMA_TCC_HI und schreibt es in das
 * OPT-hi-Wort des Parametersatzes start3.
 * start3 wird als leerer Transfer konfiguriert, so dass nur das Chaining entsprechend dem
 * aus dem FPGA gelesenen OPT-Register ausgefuehrt wird.
 *
 * Somit muss das FPGA nicht mehr notwendigerweise einen kompletten Parametersatz bestehend aus 32 Byte
 * vorbereiten, sondern nur noch die 4 Byte im OPT-Register.
 */
static void writeStartParamSets(struct edmacc_param* start1, struct edmacc_param* start2, struct edmacc_param* start3, int slot1, int slot2, int slot3) {
#if 0 // OLD
    p->opt = 0;
    // sam false
    // dam false
    // ab-sync
    // static true
    // fifoWidth not relevant
    // tccmode: normal = 0
    // no transfer complete interrupt
    // no intermediate transfer complete interrupt
    // transfer complete chaining: yes
    // intermediate transfer complete chaining: yes (actually irrelevant)
    p->opt = SYNCDIM | STATIC | EDMA_TCC(tcc) | TCCHEN | ITCCHEN;
    p->src = 0x02000000 | 0xa; // TODO richtige adresse fuer lesen aus neuem fpga-fifo
    p->a_b_cnt = (2) | (1 << 16);
    p->src_dst_bidx = 0 | (0 << 16);
    p->link_bcntrld = 0xffff | (1 << 16);
    p->src_dst_cidx = 0 | (0 << 16);
    p->ccnt = 1;

#endif
}

/**
 * in neuer Linux-Version nicht mehr (schoen) 1:1 gemapt fuer channels.
 * Frueher war der eine paramset fuer channel n immer auch nr n.
 * Jetzt gibt es wohl einen neuen ausgekluegelten Verteilungsalgorithmus in edma.c.
 * Macht nix, wir koennen es in folgendem Register nach der Allokation eines DMA-Channels auslesen:
 * DCHMAP_<ch>
 */
static unsigned paramSetOfChannel(unsigned ch) {
    const unsigned long DCHMAP_0 = 0x100;

    if (ch > 63) {
        pr_err("Illegal argument in paramSetOfChannel. Converting implicitly %u to %u.\n", ch, ch & 63);
        ch &= 63;
    }

    return (DmaChan_MappedIo_get32(&edmaMappedIo1, DCHMAP_0 + (ch << 2)) >> 5) & 0xff;
}


// Implementierung gleich hier, da in kernel modul alles in einer compilation unit kompiliert
static int
Edma_neu_init(void)
{
    int res = 0;
//    pr_info("vor DmaChan_MappedIo_create fuer edmaMappedIo\n");
//    res = DmaChan_MappedIo_create(&edmaMappedIo1, 0x49000000, (0x6000 + (PAGE_SIZE - 1)) / PAGE_SIZE * PAGE_SIZE); // passt zwar, aber so sind wir max. kompatibel, falls mal irgendwann 0x6000 nicht mehr durch PAGE_SIZE teilbar sein sollte - gott bewahre ;-)
//    pr_info("nach DmaChan_MappedIo_create fuer edmaMappedIo\n");
//
//
//    if (res) {
//        goto e0;
//    }

    dmaChanClass2 = class_create(THIS_MODULE, DMA_CHAN_CLASS_NAME2);

    if (IS_ERR(dmaChanClass2))
    {
        res = -EIO;
        goto e1;
    }

    dmaParamSetClass2 = class_create(THIS_MODULE, DMA_PARAM_SET_CLASS_NAME2);

    if (IS_ERR(dmaParamSetClass2))
    {
        res = -EIO;
        goto e2;
    }

    if (MyDevice_create(&dmaChanDev2, dmaChanClass2, &dmaChanOps2, "%s", DMA_CHAN_DEV_NAME2)) {
        // nicht 0, also fehler
        res = -EIO;
        goto e3;
    }

    if (MyDevice_create(&dmaParamSetDev2, dmaParamSetClass2, &dmaParamSetOps2, "%s", DMA_PARAM_SET_DEV_NAME2)) {
        res = -EIO;
        goto e4;
    }

    usedChans[0] = 0;
    usedChans[1] = 0;

//    pr_info("Edma_neu_init vor return 0\n");
    return 0;

    e4: MyDevice_delete(&dmaChanDev2);

    e3: class_destroy(dmaParamSetClass2);

    e2: class_destroy(dmaChanClass2);
    dmaChanClass2 = NULL;

//    e1: DmaChan_MappedIo_delete(&edmaMappedIo1);
//
//    e0: return res;

    e1: return res;
}

static void
Edma_neu_exit(void) {
    MyDevice_delete(&dmaParamSetDev2);
    MyDevice_delete(&dmaChanDev2);
    class_destroy(dmaParamSetClass2);
    class_destroy(dmaChanClass2);
}

static int
DmaChan2_open(struct inode * n, struct file * f) {

    // Hier noch nicht bekannt welcher channel ueber diese file-instanz (entspricht einem file handle im user space) verwaltet wird.
    // Dies wird durch das erste write definiert werden.
    f->private_data = NULL;


    if (!try_module_get(THIS_MODULE))
    {
        return -EIO;
    }

    return 0;
}

static void doFreeDmaChan(struct file* file) {
    if (likely(file->private_data)) {
        DmaChan2* p = (DmaChan2*) file->private_data;

//        pr_info("doFreeDmaChan - p->ch=%d, p->chan=%p\n", p->ch, p->chan);

        {
            if (p->ch >= 0) {
                ASSERT(p->ch < 64);
                ASSERT(p->chan);
                // TODO edma_stop zum testen auskommentiert
        //        edma_stop(p->ch);

                if (!(p->chan == start1Chan || p->chan == adcChan)) {
                        dma_release_channel(p->chan);
                        dmaengine_put();
                }

//                pr_info("edma channel %d released.\n", p->ch);
                p->chan = NULL;
            }
        }

        complete_all(&p->comp);
    }
}

static int
DmaChan2_release(struct inode * n, struct file * f) {
    DmaChan2* p = f->private_data;

//    pr_info("DmaChan2_release\n");

    if (likely(p)) {
//        pr_info("DmaChan2_release: p\n");
        if (unlikely(p->chan)) {
//            pr_info("vor handleCrash\n");
            // Nur moeglich, wenn file handle geschlossen wird ohne vorher das Kommando CV_CHAN_FREE zu senden.
            // In diesem Fall wird also von einem Absturz des Prozesses mit der JobApi ausgegangen:
            JobApi_handleCrash();
            doFreeDmaChan(f);
        }

        f->private_data = NULL;
//        pr_info("vor DmaChan2_delete in DmaChan2_release\n");
        DmaChan2_delete(p);
        kfree(p);
        p = NULL;
    }

    module_put(THIS_MODULE);
    return 0;
}

static ssize_t
DmaChan2_write(struct file *file, const char __user *buf, size_t count,
              loff_t *ppos)
{
    if (file->private_data) {
        // nicht erstes write, dient als hint fuer shutdown
        DmaChan2* p;

        pr_debug("DmaChan2_write\n");
        p = (DmaChan2*) file->private_data;

        switch (p->st) {
            case DCS2_PROVIDE_CHAN:
                pr_err("DmaChan2_write in Zustand PROVIDE_CHAN\n");
                return -EPROTO;
                // no break
            case DCS2_ACTIVE: {
                uint32_t cmd = 0;
                if (count != sizeof(cmd)) {
                    pr_err("%s - DCS2_ACTIVE: write mit count=%d, statt %d\n", __FUNCTION__, count, sizeof(cmd));
                    return -EPROTO;
                }
                {
                    unsigned long notCopied = copy_from_user(&cmd, buf, count);
                    if (notCopied) {
                        pr_err("%s: copy_from_user gab unerwartet zurueck: %d statt 0\n", __FUNCTION__, (int) notCopied);
                        ASSERT(false);
                    }
                }

//                pr_info("cmd=%d\n", (int) cmd);

                switch (cmd & CMD_MASK) {
                    case CV_CHAN_CROSSBAR_MAP: {
                        int ch, evt;
                        ch = ((cmd & 0x0000ff00) >> 8);
                        ASSERT(p->ch == ch);
                        evt = (cmd & 0x000000ff);
                        {
                            DmaChan_ControlModulAccess control;
                            ASSERT(0 == DmaChan_ControlModulAccess_create(&control));
                            DmaChan_ControlModulAccess_setCrossbarMapping(&control, ch,
                                    evt);
                            DmaChan_ControlModulAccess_delete(&control);
                        }

                        return count;
                        break;
                    }

                    case CV_CHAN_FREE: {
//                        pr_info("CV_CHAN_FREE\n");
                        doFreeDmaChan(file);
                        return count;
                    }

                    default: {
                        pr_warn("%s: Unbekanntes Kommando: %d\n", __FUNCTION__, (int) cmd);
                        return -EPROTO;
                    }

                }

            }
            break;
//            case DCS2_SHUTDOWN:
//                pr_err("DmaChan2_write in Zustand SHUTDOWN\n");
//                return -EPROTO;
            case DCS2_ERROR:
                pr_err("DmaChan2_write in Zustand ERROR\n");
                return -EPROTO;

            default:
                pr_err("Unerwarteter Zustand in DmaChan2_write: %d\n", p->st);
                return -EIO;
        }
    } else {
        // erstes write
        ssize_t res = count;
        int32_t data;
        int ch;

//        pr_info("DmaChan2_write: erstes fuer ch\n");

        if (unlikely(count != sizeof(data))) {
            pr_err("%s: count=%d, anstatt %d\n", __FUNCTION__, (int) count, (int) sizeof(data));
            res = -EPROTO;
            goto e1;
        }
        ASSERT(count == sizeof(data));
        if (unlikely(copy_from_user(&data, buf, sizeof(data)) != 0)) {
            pr_err("%s: copy_from_user nicht wie erwartet\n", __FUNCTION__);
            res = -EIO;
            goto e2;
        }

        ch = (int) data;
        if (unlikely(ch < -1 || ch >= 64)) {
            pr_err("Dma_Chan_write: Falscher Wert in erstem write - kein gueltiger EDMA-Kanal (0-63): %d\n", ch);
            res = -EPROTO;
            goto e3;
        }
        ASSERT(ch >= -1);
        ASSERT(ch < 64);
        // data identifiziert einen dma channel?


        {
            int createRes = 0;
            file->private_data = kzalloc(sizeof(DmaChan2), GFP_KERNEL);

            if (unlikely(ZERO_OR_NULL_PTR(file->private_data))) {
                file->private_data = NULL;
                pr_err("DmaChan2_write: Nicht genuegend Speicher!\n");
                res = -EIO;
                goto e4;
            }

            createRes = DmaChan2_create(file->private_data, ch);

            if (unlikely(createRes)) {
                // wahrscheinlich bestimmter ch (nicht -1) und schon reserviert
                res = -EIO;
                goto e5;
            }
        }

        return res; // erfolgreich

        // ab hier fehlerbehandlungen, d.h. aufraeumen in umgekehrter reihenfolge
        // je nachdem wie weit vorhin gekommen

        e5: kfree(file->private_data);
        file->private_data = NULL;
        e4:
        e3:
        e2:
        e1: return res;
    }

    return (ssize_t) count;
}

static ssize_t
DmaChan2_read(struct file* f, char __user* buf, size_t count, loff_t *ppos)
{
    ssize_t res;
    DmaChan2* p = NULL;

    if (unlikely(ZERO_OR_NULL_PTR(f->private_data))) {
        f->private_data = NULL;
        return -EPROTO; // zu frueh gelesen
    }

    p = (DmaChan2*) f->private_data;

    ASSERT(p);

    switch (p->st) {
        case DCS2_PROVIDE_CHAN: {
            int32_t i32Ch = (int32_t) p->ch;
            if (count != sizeof(int32_t)) {
                pr_err("%s - PROVIDE_CHAN: unerwarteter count=%d\n", __FUNCTION__, count);
                return -EPROTO;
            }
            res = copy_to_user(buf, &i32Ch, sizeof(int32_t));
            p->st = p->ch < 0 ? DCS2_ERROR : DCS2_ACTIVE;
            if (0 != res) {
                pr_err("%s: Unerwartetes Verhalten von copy_to_user: res=%d, statt wie erwartet %d\n", __FUNCTION__, res, 0);
                return count - res;
            }
            return count;
        }
        case DCS2_ACTIVE: {
            int32_t val = 0;
            int waitRes = 0;
            if (count != sizeof(uint32_t)) {
                pr_err("%s - PROVIDE_COMPLETION: unerwarteter count=%d\n", __FUNCTION__, count);
                return -EPROTO;
            }

            pr_warn("[NO WARN] read von ch=%d: vor wait_for_completion_interruptible\n", (int) p->ch);
            waitRes = wait_for_completion_interruptible(&p->comp);
            pr_warn("[NO WARN] read von ch=%d: nach wait_for_completion_interruptible\n", (int) p->ch);

            if (p->chan == NULL) {
                return -ENODATA;
            }

            if (unlikely(0 != waitRes)) {
                if (likely(waitRes == -ERESTARTSYS)) {
                    pr_debug("%s - ACTIVE: -ERESTARTSYS\n", __FUNCTION__);
                } else {
                    pr_warn("%s - PROVIDE_COMPLETION: unerwartetes waitRes=%d (-ERESTARTSYS=%d)\n", __FUNCTION__, waitRes, -ERESTARTSYS);
                }
                return waitRes;
            } else {
                val = 1;
                // weitere wenn nicht blockierend:
                while (p->st == DCS2_ACTIVE && try_wait_for_completion(&p->comp))
                {
                    ++val;
                    pr_debug("ch=%d: val=%d\n", (int) p->ch, (int) val);
                }
                res = copy_to_user(buf, &val, sizeof(val));
                if (0 != res)
                {
                    pr_err("Unerwartetes verhalten von copy_to_user: res=%d\n", res);
                    return count - res;
                }
                pr_debug("read von ch=%d: endgueltig val=%d\n", (int) p->ch, (int) val);
                return count;
            }
            ASSERT(false); // irgend ein return wurde in diesem case erreicht - oder aber es wurde was "angepasst" ;-)
            return 0;
        }
//        case DCS2_SHUTDOWN:
//            pr_info("%s in zustand SHUTDOWN\n", __FUNCTION__);
//            return -ENODATA; // kein protokollfehler! Blockierendes read, das vor shutdown haengt, muss so zurueckkehren!
        case DCS2_ERROR:
            return -EIO;
        default:
            ASSERT(false);
            return -EIO;
    }
}


static void
dmaCompletionCb2(unsigned channel, enum dma_status status, void* data)
{
//      char dummy = 1;
    DmaChan2* p = (DmaChan2*)data;

    if (test_bit(channel, usedChans) && p) {
        if (p->storedStatusSize < STORED_STATUS_CAPACITY) {
            p->storedStatus[p->storedStatusSize++] = status;
        }
        if (p->ch != channel) {
            pr_err("p->ch=%d, channel=%d, status=%d\n", (int) p->ch, (int) channel, (int) status);
        }

        if (DMA_COMPLETE == status) {
            complete(&p->comp);
        } else {
            pr_err("dmaCompletionCb2 called with error status: %d (ch=%d, channel=%d\n)", (int) status, (int) p->ch, (int) channel);
        }
    }
}

static int
DmaChan2_create(DmaChan2* p, int chanNum)
{
    int res;

    if (!(chanNum == -1 || (chanNum >= 0 && chanNum < 64)))
    {
        pr_err("%s: illegal chanNum=%d\n", __FUNCTION__, chanNum);
        return -1;
    }

    p->storedStatusSize = 0;
    {
        int i;
        for (i = 0; i < STORED_STATUS_CAPACITY; ++i) {
            p->storedStatus[i] = (u16)-1;
        }
    }

//    pr_info("[10] chanNum was %d\n", chanNum);

    init_completion(&p->comp);
//    pr_info("completion for ch initialized, p=%p\n", p);


    {
        dma_cap_mask_t mask;
        struct CvHandler h = {
                dmaCompletionCb2,
                p
        };

        dma_cap_zero(mask);
        if (chanNum == -1) {
            dmaengine_get();
            p->chan = dma_request_chan_by_mask(&mask);
        } else {
            if (edma_channel_num(start1Chan) == chanNum) {
//                pr_info("Spezialfall alloc: startChan\n");
                p->chan = start1Chan;
            } else if(edma_channel_num(adcChan) == chanNum) {
//                pr_info("Spezialfall alloc: adcChan\n");
                p->chan = adcChan;
            } else {
//                pr_info("Normalfall alloc (nicht -1)\n");
                dmaengine_get();
                p->chan = dma_request_channel(mask, edma_filter_fn, &chanNum);

                if (unlikely(IS_ERR_OR_NULL(p->chan))) {
                    dmaengine_put();
                }
            }
        }
        if (IS_ERR_OR_NULL(p->chan)) {
            p->chan = NULL;
            p->ch = -1;
            pr_err("dma_request_channel failed: ptr_err=%ld\n", PTR_ERR(p->chan));
            // kein fehlerrueckgabewert!
        } else {
            p->ch = chanNum = edma_channel_num(p->chan);
            edma_setCvHandler(p->chan, h); // in edma.c wurde Zuruecksetzen dieses Handlers "hinein gehackt".
        }
    }

    pr_info("Voruebergehend EVENTQ_0 verwendet...\n");

    if (unlikely(p->ch < 0))
    {
        p->ch = -1;
        p->chan = NULL;
        pr_warn("edma_alloc_channel failed\n");
    } else {
        ASSERT(p->ch < 64); // sonst waere edma_alloc_channel fehlerhaft
//        pr_info("DmaChan2_create mit ch=%d\n", p->ch);

        // test_and_set_bit ist atomar!
        // gibt true zurueck genau dann wenn das bit chanNum in usedChans[] vorher schon gesetzt war
        if (unlikely(test_and_set_bit(chanNum, usedChans))) {
            if (edma_channel_num(start1Chan) == chanNum ||edma_channel_num(adcChan) == chanNum) {
//                pr_info("channel %d bereits allokiert fuer einen der Spezialkanaele startChan oder adcChan\n", chanNum);
                p->chan = NULL;
                p->ch = -1;
                pr_warn("dma_request_channel failed (spezialfall)\n");
            } else {
                // war schon allokiert?!
                pr_err("%s: channel %d already in use though edma_alloc_channel was successful?!\n", __FUNCTION__, (int) p->ch);
                // was bisher geaendert wurde in p->... darf ruhig bleiben bzw. muss nicht rueckgaengig gemacht werden

                // neu: dma_release_channel() und danach dmaengine_put()
                if (!(p->chan == start1Chan || p->chan == adcChan)) {
                    dma_release_channel(p->chan);
                    dmaengine_put();
                }
                p->chan = NULL;
                p->ch = -1;
                return -1; // wirklich ein fehler, darf nie erreicht werden
            }
        }
    }


    res = 0; // nun immer, ausser grober unfug programmiert ;-)

    ASSERT(p->ch >= -1 && p->ch < 64);
    p->st = DCS2_PROVIDE_CHAN;
    return res;
}

static void
DmaChan2_delete(DmaChan2* p)
{
    if (p->ch >= 0) {
        ASSERT(p->ch < 64);
        // TODO edma_stop zum testen auskommentiert
//        edma_stop(p->ch);

        if (p->chan) {
            if (!(p->chan == start1Chan || p->chan == adcChan)) {
                dma_release_channel(p->chan);
                dmaengine_put();
            }

//            pr_info("edma channel %d released.\n", p->ch);
            p->chan = NULL;
        } else {
//            pr_info("DmaChan2_delete: p->chan was already NULL as now normal.\n");
        }

//        pr_info("storedStatus of freed channel %d:\n", p->ch);
//        {
//            int i;
//            for (i = 0; i < p->storedStatusSize; ++i) {
//                pr_info("status[%d][%d]=%d\n", (int) p->ch, (int) i, (int) p->storedStatus[i]);
//            }
//        }

        clear_bit(p->ch, usedChans);
        p->ch = -1;
    }
}

static int
DmaParamSet2_create(DmaParamSet2* p, int slot) {
    if (slot < 0)
    {
        // any slot
        slot = MY_EDMA_SLOT_ANY; // nur zur sicherheit, damit implementierungsunabhaengig
    }
    else
    {
        if (unlikely(slot >= 256))
        {
            pr_err("%s: ungueltiger Slot: %d\n", __FUNCTION__, slot);
            p->nr = -1;
            return -1;
        }
    }

    // Beachte; my_edma_alloc_slot garantiert, dass bei slot != -1
    // ausschliesslich der gewuenschte slot oder kein slot allokiert wird, nicht wie die variante im alten edma-treiber, die auch eine alternative allokierte, wenn der angefragte paramset nicht mehr verfuegbar war.
    slot = my_edma_alloc_slot(slot);
    PR("my_edma_alloc_slot returned: %d\n", slot);

    if (likely(slot >= 0))
    {
        ASSERT(slot < 256);
        // slot erfolgreich allokiert
        p->nr = slot;
        return 0;
    }
    else
    {
        // slot nicht erfolgreich allokiert, aber Objekt trotzdem erfolgreich erzeugt
        p->nr = -1;
        return 0;
    }
}

static void
DmaParamSet2_delete(DmaParamSet2* p) {
    ASSERT(p);
    if (p->nr >= 0) { // Objekt kann auch existieren ohne dass erfolgreich ein channel allokiert wurde!
        ASSERT(p->nr < 256);

        my_edma_free_slot(p->nr);
//        pr_info("EDMA slot %d freigegeben...\n", p->nr);
    }
    p->nr = -1;
}

static int
DmaParamSet2_open(struct inode * n, struct file * file) {
    if (unlikely(!try_module_get(THIS_MODULE))) {
        return -EIO;
    }

    file->private_data = NULL;
    return 0;
}

static void doFreeDmaParamSet(struct file* file) {
    DmaParamSet2* p;

    if (likely(file->private_data)) {
        p = (DmaParamSet2*) file->private_data;
        file->private_data = NULL;

        DmaParamSet2_delete(p);
        kfree(p);
        p = NULL;
    }

}

static int
DmaParamSet2_release(struct inode * n, struct file * f) {
    if (!ZERO_OR_NULL_PTR(f->private_data)) {
        JobApi_handleCrash();
        doFreeDmaParamSet(f);
    }

    module_put(THIS_MODULE);
    return 0;
}

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

    if (!file->private_data) {
        // erstes write fuer bereitstellung des slots, bzw. der nr des paramsets
        ssize_t res = count;
        int32_t data;
        int slot;
//        pr_info("DmaParamSet2_write: erstes fuer slot\n");

        if (unlikely(count != sizeof(data))) {
            pr_err("%s: count=%d, anstatt %d\n", __FUNCTION__, (int) count, (int) sizeof(data));
            res = -EPROTO;
            goto e1;
        }
        ASSERT(count == sizeof(data));
        if (unlikely(copy_from_user(&data, buf, sizeof(data)) != 0)) {
            pr_err("%s: copy_from_user nicht wie erwartet\n", __FUNCTION__);
            res = -EIO;
            goto e2;
        }
        slot = (int) data;
        if (unlikely(slot < -1 || slot >= 256)) {
            pr_err("%s: Falscher Wert in erstem write - kein gueltiger ParamSet-Slot: %d\n", __FUNCTION__, slot);
            res = -EPROTO;
            goto e3;
        }
        ASSERT(slot >= -1);
        ASSERT(slot < 256);

        {
            int createRes = 0;
            file->private_data = kzalloc(sizeof(DmaParamSet2), GFP_KERNEL);
            if (ZERO_OR_NULL_PTR(file->private_data)) {
                pr_err("DmaParamSet2_write: Nicht genuegend Speicher!\n");
                res = -EIO;
                goto e4;
            }

            createRes = DmaParamSet2_create(file->private_data, slot);

            if (unlikely(createRes)) {
                res = -EIO;
                goto e5;
            }
        }

        return res;

        e5: kfree(file->private_data);
        file->private_data = NULL;
        e4:
        e3:
        e2:
        e1: return res;

    } else {
        uint32_t cmd = 0;

        if (count != sizeof(cmd)) {
            pr_err("%s - 2. write fuer DmaParamSet2: count=%d, statt %d\n", __FUNCTION__, count, sizeof(cmd));
            return -EPROTO;
        }
        {
            unsigned long notCopied = copy_from_user(&cmd, buf, count);
            if (notCopied) {
                pr_err("%s: copy_from_user gab unerwartet zurueck: %d statt 0\n", __FUNCTION__, (int) notCopied);
                ASSERT(false);
            }
        }

//        pr_info("cmd=%d\n", (int) cmd);

        if (cmd == CV_PRMS_FREE) {
            // Muss CV_PRMS_FREE sein.
            doFreeDmaParamSet(file);
            return count;
        } else {
            // Sonst ignorieren, protokoll-fehler
            pr_warn("Unerwartetes 2. DmaParamSet2_write nach dem ersten erwarteten write (CV_PRMS_FREE erwartet)\n");
            return -EPROTO;
        }
    }
}

static ssize_t
DmaParamSet2_read(struct file* f, char __user* buf, size_t count, loff_t *ppos) {
    DmaParamSet2* p = NULL;
    int32_t val;
    int res = 0;

    if (!f->private_data) {
        return -EPROTO; // zu frueh gelesen
    }

    // Hier einfach immer nur "f->private_data->nr" zurueckschreiben
    p = (DmaParamSet2*) (f->private_data);
    val = (int32_t) p->nr;
    if (count != sizeof(val)) {
        pr_err("%s: count=%d, nicht wie erwartet %d\n", __FUNCTION__, count, sizeof(val));
        return -EPROTO;
    }

    res = copy_to_user(buf, &val, sizeof(val));
    if (0 != res) {
        pr_err("%s: Unerwartetes Verhalten von copy_to_user: res=%d, anstatt wie erwartet %d\n", __FUNCTION__, res, 0);
        return count - res;
    }

    return count;
}

static void chan_trigger(int chanNum) __attribute((unused));

// VORSICHT! Falsches Argument kann andere Treiber "kaputt machen" - im schlimmsten Fall zu Systemabsturz fuehren!
static void chan_trigger(int chanNum) {
    unsigned long off;
    uint32_t val;

    if (chanNum < 32) {
        off = 0x1010;
        val = (1 << chanNum);
    } else {
        off = 0x1014;
        val = (1 << (chanNum - 32));
    }

    DmaChan_MappedIo_set32(&edmaMappedIo1, off, val);
}

#endif /* EDMA_NEU_H_ */
