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

/*
 * KM_CvKernelAccess.h
 *
 *  Created on: Aug 1, 2016
 *      Author: preitinger
 */

#ifndef KM_CVKERNELACCESS_H_
#define KM_CVKERNELACCESS_H_

/*. IMPORT ================================================================*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/bug.h>
#include <linux/stddef.h>
#include <linux/platform_data/edma.h>
#include <linux/cdev.h>

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

/*-------------------------------- Makros ---------------------------------*/
#define ASSERT(b) \
    do { if (unlikely(!(b))) { \
        pr_alert("assertion failed in %s:%d (%s)\n", __FILE__, __LINE__, __func__); \
        BUG(); \
    } } while(0)
// neu: siehe JobApi_messages.h
//// TODO alt:
//#define MAPPED_DMA                      0x00000000
//#if 0
//#define DMA_CHAN_ALLOC                  0x00010000
//#define DMA_CHAN_FREE                   0x00020000
//#endif
//#define DMA_CHAN_CROSSBAR_MAP           0x00030000
//#if 0
//#define DMA_PARAMSET_ALLOC              0x00040000
//#define DMA_PARAMSET_FREE               0x00050000
//#endif
//#define DMA_PINMUX                      0x00060000
//#define DMA_CHAN_SHUTDOWN               0x00070000

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

typedef struct SMyDevice
{
    dev_t devNum;
    struct cdev dev;
    struct class* class;
    atomic_t accessCount;
} MyDevice;

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

static void
packPinmuxArgs(int off, int mode, bool pullEnabled, bool upSelected,
               bool receiverEnabled, uint32_t* dest);
static void
unpackPinmuxArgs(/*in*/uint32_t src, /*out*/int* off, int* mode,
                 bool* pullEnabled, bool* upSelected, bool* receiverEnabled);

static int
MyDevice_create(MyDevice* d, struct class* class,
                const struct file_operations* fops, const char* devNameFmt,
                ...);

static void
MyDevice_delete(MyDevice* p);

//// forward decl
//static int
//doDmaChanCrossbarMap(uint32_t cmd);

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

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

static void addBits(/*out*/uint32_t* dest, /*in*/uint32_t bits, /*inout*/int* pos, /*in*/int count) {
    *dest |= ((bits << *pos) & GENMASK(*pos + count - 1, *pos));
    *pos += count;
}

static uint32_t takeBits(uint32_t src, int* pos, int count) {
    uint32_t res = (src >> *pos) & GENMASK(count - 1, 0);
    *pos += count;
    return res;
}

static void
packPinmuxArgs(/*in*/int off, int mode, bool pullEnabled, bool upSelected,
               bool receiverEnabled, /*out*/uint32_t* dest) __attribute((unused));

static void
packPinmuxArgs(/*in*/int off, int mode, bool pullEnabled, bool upSelected,
               bool receiverEnabled, /*out*/uint32_t* dest)
{
    // off 0 bis 0x234, 10 bits
    // mode 0 bis 7, 3 bits
    // bools je 1 bit

    int offLen = 10;
    int modeLen = 3;
    int boolLen = 1;
    int pos = 0;

    ASSERT(16 >= offLen + modeLen + boolLen * 3); // sonst passts nicht in das low nibble von *dest
    *dest &= ~GENMASK(15, 0);

    addBits(dest, off, &pos, offLen);
    addBits(dest, mode, &pos, modeLen);
    addBits(dest, pullEnabled ? 1 : 0, &pos, boolLen);
    addBits(dest, upSelected ? 1 : 0, &pos, boolLen);
    addBits(dest, receiverEnabled ? 1 : 0, &pos, boolLen);
    ASSERT(pos == offLen + modeLen + boolLen * 3);
    ASSERT(pos <= 16);
}

static void
unpackPinmuxArgs(/*in*/uint32_t src, /*out*/int* off, int* mode,
                 bool* pullEnabled, bool* upSelected, bool* receiverEnabled)
{

    int offLen = 10;
    int modeLen = 3;
    int boolLen = 1;
    int pos = 0;
    ASSERT(16 >= offLen + modeLen + boolLen * 3); // sonst passts nicht in das low nibble von *dest

    *off = (int) takeBits(src, &pos, offLen);
    *mode = (int) takeBits(src, &pos, modeLen);
    *pullEnabled = (bool) takeBits(src, &pos, boolLen);
    *upSelected = (bool) takeBits(src, &pos, boolLen);
    *receiverEnabled = (bool) takeBits(src, &pos, boolLen);
}

#endif /* KM_CVKERNELACCESS_H_ */
