/* Copyright 2024 Markus Lehr
 *
 *
 * This Software is owned by Markus Lehr.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
// ##################################################################################################
/*
 * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID 
 * and functions to handle the monitoring in a Generic Netlink in the kernel.  
 * "It registers a Netlink family called "vrpmdv-monitoring_cmd".
 *
 *
 * You can find some more interesting documentation about Generic Netlink here:
 * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/
 */


#include <linux/kernel.h>
#include <linux/rpmsg.h>

// basic definitions for kernel module development
#include <linux/module.h>
// definitions for generic netlink families, policies etc;
// transitive dependencies for basic netlink, sockets etc
#include <net/genetlink.h>
// required for locking inside the .dumpit callback demonstration
#include <linux/mutex.h>
#include <linux/wait.h>


// data/vars/enums/properties that describes our protocol that we implement
// on top of generic netlink (like functions we want to trigger on the receiving side)
//#include "vrpmdv-monitoring-cmd.h"


/* ######################## CONVENIENT LOGGING MACROS ######################## */
// (Re)definition of some convenient logging macros from <linux/printk.h>. You can see the logging
// messages when printing the kernel log, e.g. with `$ sudo dmesg`.
// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h

// with this redefinition we can easily prefix all log messages from pr_* logging macros
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* ########################################################################### */


#define MSG		"hello Monitoring!"

static int count = 100;
module_param(count, int, 0644);

struct instance_data {
	int rx_count;
};


// std::chrono::seconds timeoutPeriod = 5;
// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod;


/** -----  NETLINK Driver defintion ------------------*/


/**
 * Generic Netlink will create a Netlink family with this name. Kernel will asign
 * a numeric ID and afterwards we can talk to the family with its ID. To get
 * the ID we use Generic Netlink in the userland and pass the family name.
 *
 * Short for: Generic Netlink VRPMDV Monitoring  gnl_foobar_mcmd
 */
#define FAMILY_NAME "gnl-vrpmdv-mcmd"

/**
 * These are the attributes that we want to share in gnl_foobar_xmpl.
 * You can understand an attribute as a semantic type. This is
 * the payload of Netlink messages.
 * GNl: Generic Netlink
 */
enum GNL_VRPMDV_XMPL_ATTRIBUTE {
    /**
     * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code.
     * We do the same, although I'm not sure, if this is really enforced by code.
     */
    GNL_VRPMDV_MCMD_A_UNSPEC,
    /** We expect a MSG to be a null-terminated C-string. */
    GNL_VRPMDV_MCMD_A_MSG,
    /** Unused marker field to get the length/count of enum entries. No real attribute. */
    __GNL_VRPMDV_MCMD_A_MAX,
};
/**
 * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`.
 */
#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX)
/**
 * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`.
 * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used.
 */
#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1)

/**
 * Enumeration of all commands (functions) that our custom protocol on top
 * of generic netlink supports. This can be understood as the action that
 * we want to trigger on the receiving side.
 */
enum GNL_VRPMDV_MCMD_COMMAND {
    /**
     * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code.
     * We do the same, although I'm not sure, if this is really enforced by code.
     */
    GNL_VRPMDV_MCMD_C_UNSPEC,

    // first real command is "1" (>0)
    /**
     * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to
     * be present in the Generic Netlink request message. The kernel reads the message from the packet and
     * sent it to the copro. THe result will be return by creating a new Generic Netlink response message 
     * with an corresponding attribute/payload.
     *
     * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as
     * "echo specific data" instead of return a 1:1 copy of the package, which you could do with
     * `NLM_F_ECHO (0x08)` for example.
     */
    GNL_VRPMDV_MCMD_C_MSG,

    /**
     * Provokes a NLMSG_ERR answer to this request as described in netlink manpage
     * (https://man7.org/linux/man-pages/man7/netlink.7.html).
     */
    GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR,

    /** Unused marker field to get the length/count of enum entries. No real attribute. */
    __GNL_VRPMDV_MCMD_C_MAX,
};
/**
 * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`.
 */
#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX)
/**
 * The number of actual usable commands  in `enum GNL_FOOBAR_XMPL_COMMAND`.
 * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used.
 */
#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1)

#define NODATARECEIVED 0
#define DATARECEIVED 1

/**
 * Data structure required for our .doit callback handler to
 * know about the progress of an ongoing cmd execution.
 * See the cmd callback handler how it is used.
 */
// struct {
//     // from <linux/mutex.h>
//     /**
//      * rpmsg wait for response from copro side.
//      */
//     struct mutex sendMTX;
//     /**
//      * rpmsg wait for response from copro side.
//      */
//     struct mutex receiveCV;
//     /**
//      * Wait Queue: if it is signaled we have received data from copro
//     */
//     wait_queue_head_t receive_queue;
//     /**
//      * Waitflag: 0= no data received, 1 = data received
//     */
//     int receive_queue_flag = NODATARECEIVED;
//     /**
//      * Condition vaiable signal we have received data from copro
//     */
// //    std::condition_variable cv;
//     // /**
//     //  * Number that describes how many packets we need to send until we are done
//     //  * during an ongoing dumpit process. 0 = done.
//     //  */
//     // int unsigned runs_to_go;
//     // /**
//     //  * Number that describes how many packets per dump are sent in total.
//     //  * Constant per dump.
//     //  */
//     // int unsigned total_runs;

//     //the rpmsg device which sends the data to the copro 
//     struct rpmsg_device	*rpdev;	/* handle rpmsg device */    
// } cmd_cb_progress_data;


// struct rpmsg_vrpmdv_mon_t{
//     // from <linux/mutex.h>
//     /**
//      * rpmsg wait for response from copro side.
//      */
//     struct mutex sendMTX;
//     /**
//      * rpmsg wait for response from copro side.
//      */
//     struct mutex receiveCV;
//     /**
//      * Wait Queue: if it is signaled we have received data from copro
//     */
//     wait_queue_head_t receive_queue;
//     /**
//      * Waitflag: 0= no data received, 1 = data received
//     */
//     int receive_queue_flag;
//     //the rpmsg device which sends the data to the copro 
//     struct rpmsg_device* rpdev;	/* handle rpmsg device */    
// };

// struct rpmsg_vrpmdv_mon_t vrpmdv_mon;


    /**
     * rpmsg wait for response from copro side.
     */
    struct mutex sendMTX;
    /**
     * rpmsg wait for response from copro side.
     */
    struct mutex receiveCV;
    /**
     * Wait Queue: if it is signaled we have received data from copro
    */
    wait_queue_head_t receive_queue;
    /**
     * Waitflag: 0= no data received, 1 = data received
    */
    int receive_queue_flag = NODATARECEIVED;

    char* received_bytes = NULL;
    int   received_len = 0;
    //the rpmsg device which sends the data to the copro 
    struct rpmsg_device* prpdev = NULL;	/* handle rpmsg device */    


	// struct mutex	mutex; /* mutex to protect the ioctls */
	// struct miscdevice mdev; /* misc device ref */
	// struct rpmsg_device	*rpdev;	/* handle rpmsg device */
	// struct list_head buffer_list; /* buffer instances list */



// Documentation is on the implementation of this function.
int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info);

// Documentation is on the implementation of this function.
int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info);

/**
 * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily
 * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic.
 * For example, you can use the same command multiple times and - dependent by flag -
 * invoke a different callback handler. In our simple example we just use one .doit callback
 * per operation/command.
 */
#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT)

/**
 * Array with all operations that our protocol on top of Generic Netlink
 * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of
 * received Generic Netlink message) and the corresponding ".doit" callback function.
 * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148
 */
struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = {
        {
                /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */
                .cmd = GNL_VRPMDV_MCMD_C_MSG,
                /* TODO Use case ? */
                .flags = 0,
                /* TODO Use case ? */
                .internal_flags = 0,
                /* Callback handler when a request with the specified ".cmd" above is received.
                 * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate"
                 * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717
                 *
                 * Quote from: https://lwn.net/Articles/208755
                 *  "The 'doit' handler should do whatever processing is necessary and return
                 *   zero on success, or a negative value on failure.  Negative return values
                 *   will cause a NLMSG_ERROR message to be sent while a zero return value will
                 *   only cause a NLMSG_ERROR message to be sent if the request is received with
                 *   the NLM_F_ACK flag set."
                 *
                 * You can find this in Linux code here:
                 * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499
                 *
                 * One can find more information about NLMSG_ERROR responses and how to handle them
                 * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html
                 *
                 */
                .doit = gnl_cb_vrpmdv_doit,
                /* This callback is similar in use to the standard Netlink 'dumpit' callback.
                 * The 'dumpit' callback is invoked when a Generic Netlink message is received
                 * with the NLM_F_DUMP flag set.
                 *
                 * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e.
                 * the userland can receive as long as the .dumpit callback returns data.
                 *
                 * .dumpit is not mandatory, but either it or .doit must be provided, see
                 * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367
                 *
                 * To be honest I don't know in what use case one should use .dumpit and why
                 * it is useful, because you can achieve the same also with .doit handlers.
                 * Anyway, this is just an example/tutorial.
                 *
                 * Quote from: https://lwn.net/Articles/208755
                 *  "The main difference between a 'dumpit' handler and a 'doit' handler is
                 *   that a 'dumpit' handler does not allocate a message buffer for a response;
                 *   a pre-allocated sk_buff is passed to the 'dumpit' handler as the first
                 *   parameter.  The 'dumpit' handler should fill the message buffer with the
                 *   appropriate response message and return the size of the sk_buff,
                 *   i.e. sk_buff->len, and the message buffer will automatically be sent to the
                 *   Generic Netlink client that initiated the request.  As long as the 'dumpit'
                 *   handler returns a value greater than zero it will be called again with a
                 *   newly allocated message buffer to fill, when the handler has no more data
                 *   to send it should return zero; error conditions are indicated by returning
                 *   a negative value.  If necessary, state can be preserved in the
                 *   netlink_callback parameter which is passed to the 'dumpit' handler; the
                 *   netlink_callback parameter values will be preserved across handler calls
                 *   for a single request."
                 *
                 * You can see the check for the NLM_F_DUMP-flag here:
                 * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780
                 */
                .dumpit = NULL, //gnl_cb_echo_dumpit,
                /* Start callback for dumps. Can be used to lock data structures. */
                .start = NULL, //gnl_cb_echo_dumpit_before,
                /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */
                .done = NULL, //gnl_cb_echo_dumpit_before_after,
                /*
                 0 (= "validate strictly") or value `enum genl_validate_flags`
                 * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108
                 */
                .validate = 0,
        },
        {
                .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR,
                .flags = 0,
                .internal_flags = 0,
                .doit = gnl_cb_doit_reply_with_nlmsg_err,
                // .dumpit is not required, only optional; application specific/dependent on your use case
                // in a real application you probably have different .dumpit handlers per operation/command
                .dumpit = NULL,
                // in a real application you probably have different .start handlers per operation/command
                .start = NULL,
                // in a real application you probably have different .done handlers per operation/command
                .done = NULL,
                .validate = 0,
        }
};


/**
 * Attribute policy: defines which attribute has which type (e.g int, char * etc).
 * This get validated for each received Generic Netlink message, if not deactivated
 * in `gnl_foobar_xmpl_ops[].validate`.
 * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717
 */
static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = {
        // In case you are seeing this syntax for the first time (I also learned this just after a few years of
        // coding with C myself): The following static array initiations are equivalent:
        // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`.

        [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC},

        // You can set this to NLA_U32 for testing and send an ECHO message from the userland
        // It will fail in this case and you see a entry in the kernel log.

        // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String
        [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING},
};



/**
 * Definition of the Netlink family we want to register using Generic Netlink functionality
 */
static struct genl_family gnl_vrpmdv_mcmd_family = {
        // automatically assign an id
        .id = 0,
        // we don't use custom additional header info / user specific header
        .hdrsize = 0,
        // The name of this family, used by userspace application to get the numeric ID
        .name = FAMILY_NAME,
        // family specific version number; can be used to evolve application over time (multiple versions)
        .version = 1,
        // delegates all incoming requests to callback functions
        .ops = gnl_vrpmdv_mcmd_ops,
        // length of array `gnl_foobar_xmpl_ops`
        .n_ops = GNL_VRPMDV_OPS_LEN,
        // attribute policy (for validation of messages). Enforced automatically, except ".validate" in
        // corresponding ".ops"-field is set accordingly.
        .policy = gnl_vrpmdv_mcmd_policy,
        // Number of attributes / bounds check for policy (array length)
        .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN,
        // Owning Kernel module of the Netlink family we register.
        .module = THIS_MODULE,

        // Actually not necessary because this memory region would be zeroed anyway during module load,
        // but this way one sees all possible options.

        // if your application must handle multiple netlink calls in parallel (where one should not block the next
        // from starting), set this to true! otherwise all netlink calls are mutually exclusive
        .parallel_ops = 0,
        // set to true if the family can handle network namespaces and should be presented in all of them
        .netnsok = 0,
        // called before an operation's doit callback, it may do additional, common, filtering and return an error
        .pre_doit = NULL,
        // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks
        .post_doit = NULL,
};



/**
 * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received.
 * Please look into the comments where this is used as ".doit" callback above in
 * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks.
*/
int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) {
    struct nlattr *na;
    struct sk_buff *reply_skb;
    int rc;
    void *msg_head;
    char *recv_msg;

    pr_info("%s() invoked\n", __func__);

    if (info == NULL) {
        // should never happen
        pr_err("An error occurred in %s():\n", __func__);
        return -EINVAL;
    }

    /*
     * For each attribute there is an index in info->attrs which points to a nlattr structure
     * in this structure the data is stored.
     */
    na = info->attrs[GNL_VRPMDV_MCMD_A_MSG];

    if (!na) {
        pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG);
        return -EINVAL; // we return here because we expect to recv a msg
    }

    recv_msg = (char *) nla_data(na);
    if (recv_msg == NULL) {
        pr_err("error while receiving data\n");
    } else {
        pr_info("received: '%s'\n", recv_msg);
    }

    //aquire lock for cmd repmsg channel     
    // std::lock sendlk(cmd_cb_progress_data.sendmtx);    

    //send the message to the copro over RPMSG 
    if (prpdev) {
        rc = rpmsg_send(prpdev->ept, recv_msg, strlen(recv_msg));
        if (rc) {
            pr_err("rpmsg_send failed: %d\n", rc);
            return rc;
        }

        // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev);

        // Send a message back after we receive the reply from rpmsg channel
        // ---------------------

        // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE
        reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
        if (reply_skb == NULL) {
            pr_err("An error occurred in %s():\n", __func__);
            return -ENOMEM;
        }

        // Create the message headersprpdev

        // Add header to netlink message;
        // afterwards the buffer looks like this:
        // ----------------------------------
        // | netlink header                 |
        // | generic netlink header         |
        // | <space for netlink attributes> |
        // ----------------------------------
        // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff *
        //                        // According to my findings: this is not used for routing
        //                        // This can be used in an application specific way to target
        //                        // different endpoints within the same user application
        //                        // but general rule: just put sender port id here
        //                        info->snd_portid, // sending port (not process) id: int
        //                        info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory)
        //                        &gnl_vrpmdv_mcmd_family, // struct genl_family *
        //                        0, // flags for Netlink header: int; application specific and not mandatory
        //                        // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header
        //                        GNL_VRPMDV_MCMD_C_MSG
        // );

        msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff *
                                    info, // info
                                    &gnl_vrpmdv_mcmd_family, // struct genl_family *
                                    0, // flags for Netlink header: int; application specific and not mandatory
                                    // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header
                                    info->genlhdr->cmd
        );

        if (msg_head == NULL) {
            rc = ENOMEM;
            pr_err("An error occurred in %s():\n", __func__);
            return -rc;
        }

        {
            pr_info("wait for response\n");
            // wait until receive_queue_flag=1 , that means we have received data from Copro
            wait_event_interruptible(receive_queue, receive_queue_flag != 0 );
            
            //Copy data
            receive_queue_flag = NODATARECEIVED;



            // std::unique_lock lk(cmd_cb_progress_data.receivemtx);
            // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout)
            // {
            //     dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n");
            //     return -1:
            // }

            //pr_info("get response: '%s'\n", recv_msg);
            if (received_len > 0) {                
                pr_info("received data from copro %s\n", received_bytes);                
            }
            else {
                pr_err("don't received data from Copro \n");                
            }

        }

        // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent)
        // echo the value we just received
        rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, received_bytes);

        if (rc != 0) {
            pr_err("An error occurred in %s():\n", __func__);
            //free the buffer
            kfree(received_bytes);
            received_bytes = NULL;
            return -rc;
        }

        // Finalize the message:
        // Corrects the netlink message header (length) to include the appended
        // attributes. Only necessary if attributes have been added to the message.
        genlmsg_end(reply_skb, msg_head);

        // Send the message back
        rc = genlmsg_reply(reply_skb, info);
        // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid)
        // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326

        kfree(received_bytes);
        received_bytes = NULL;

        if (rc != 0) {
            pr_err("An error occurred in %s():\n", __func__);
            return -rc;
        }
        return 0;
    
    }
    pr_info("Device not set in Probe. Should not happen");
    return -1;
}

/**
 * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received.
 * Please look into the comments where this is used as ".doit" callback above in
 * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks.
*/
int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) {
    pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__);

    /*
     * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply
     * Quote from https://lwn.net/Articles/208755/:
     *  "The 'doit' handler should do whatever processing is necessary and return
     *   zero on success, or a negative value on failure.  Negative return values
     *   will cause a NLMSG_ERROR message to be sent while a zero return value will
     *   only cause a NLMSG_ERROR message to be sent if the request is received with
     *   the NLM_F_ACK flag set."
     *
     * You can find this in Linux code here:
     * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499
     *
     * One can find more information about NLMSG_ERROR responses and how to handle them
     * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html
     */
    return -EINVAL;
}



/** -----  NETLINK Driver defintion ------------------*/





/**
 * callback that is called after the copro send data
 * we have to copy it in a buffer for the netlink and later send it back to the userland
 * 
*/
static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len,
						void *priv, u32 src)
{
	int ret = 0;
	// struct instance_data *idata = dev_get_drvdata(&rpdev->dev);

	// dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n",
	// 	 ++idata->rx_count, src);

	print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len,
			     true);
   	if (len == 0) {
		pr_err("(%s) Empty lenght requested\n", __func__);
		return -EINVAL;
	}
    else {
        pr_info("received data from copro %s\n", (char*) data);                
    }
    
    // received_bytes = (char *)kmalloc(len+1, GFP_KERNEL);
	// memcpy(received_bytes, data, len);

    // rpmsg_RxBuf[len] = 0;



    received_bytes = (char *)kmalloc(len, GFP_KERNEL);
	memcpy(received_bytes, data, len);    
    received_len = len;
    receive_queue_flag= DATARECEIVED;
    wake_up_interruptible(&receive_queue);

	// /* samples should not live forever */
	// if (idata->rx_count >= count) {
	// 	dev_info(&rpdev->dev, "goodbye!\n");
	// 	return 0;
	// }

	/* send a new message now */
	// ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
	// if (ret)
	// 	dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);

	return ret;
}

static int vrpmdv_monitoring_probe(struct rpmsg_device *rpdev)
{
    int rc;	

	// int ret;
	// struct instance_data *idata;

	// dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
	// 				rpdev->src, rpdev->dst);

    pr_info("RPMSG monitroing control device driver started.\n");

	// idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL);
	// if (!idata)
	// 	return -ENOMEM;

	// dev_set_drvdata(&rpdev->dev, idata);

	// /* send a message to our remote processor to */
	// ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
	// if (ret) {
	// 	dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret);
	// 	return ret;
	// }

	// return 0;

	// struct device *dev;
    // dev = &rpdev->dev;
	// struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon;

	// rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL);
	// if (!rpmsg_vrpmdv_mon)
	// 	return -ENOMEM;

    mutex_init(&sendMTX);
    init_waitqueue_head (&receive_queue);    
	prpdev = rpdev;

	// dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon);
    pr_info("RPMSG CMD Device set.\n");


	/** NEU **/
    // if (cmd_cb_progress_data.rpdev == NULL) {
     // cmd_cb_progress_data.rpdev = rpdev;
     // pr_info("RPMSG CMD Device set.\n");
    // }
    // else {
    //     pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n");        
    // }

    pr_info("Generic Netlink VRPMDV-Monitroring_Controler Module started.\n");

    // Register family with its operations and policies
    rc = genl_register_family(&gnl_vrpmdv_mcmd_family);
    if (rc != 0) {
        pr_err("FAILED: genl_register_family(): %i\n", rc);
        pr_err("An error occurred while inserting the generic netlink example module\n");
        return -1;
    } else {
        pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n");
    }

    return 0;



}

static void vrpmdv_monitoring_remove(struct rpmsg_device *rpdev)
{
    int ret;
    pr_info("Generic Netlink Example Module unloaded.\n");

    // Unregister the family
    ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family);
    if (ret != 0) {
        pr_err("genl_unregister_family() failed: %i\n", ret);
        return;
    } else {
        pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n");
    }

    mutex_destroy(&sendMTX);
    wake_up_interruptible(&receive_queue);

	pr_info("vrpmdv-monitoring controler driver is removed\n");	
	// dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n");

}

static struct rpmsg_device_id vrpmdv_monitoring_controler_id_table[] = {
	{ .name	= "vrpmdv-monitoring-controler" },
	{ },
};
MODULE_DEVICE_TABLE(rpmsg, vrpmdv_monitoring_controler_id_table);

static struct rpmsg_driver vrpmdv_monitoring_controler = {
	.drv.name	= KBUILD_MODNAME,
	.id_table	= vrpmdv_monitoring_controler_id_table,
	.probe		= vrpmdv_monitoring_probe,
	.callback	= vrpmdv_monitoring_cb,
	.remove		= vrpmdv_monitoring_remove,
};
module_rpmsg_driver(vrpmdv_monitoring_controler);


// static struct rpmsg_driver vrpmdv_monitoring_data = {
// 	.drv.name	= KBUILD_MODNAME,
// 	.id_table	= vrpmdv_monitoring_controler_id_table,
// 	.probe		= vrpmdv_monitoring_probe,
// 	.callback	= vrpmdv_monitoring_cb,
// 	.remove		= vrpmdv_monitoring_remove,
// };

// module_rpmsg_driver(vrpmdv_monitoring_data);

MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler");
MODULE_LICENSE("GPL v2");



// /**
//  * Module/driver initializer. Called on module load/insertion.
//  *
//  * @return success (0) or error code.
//  */
// static int __init gnl_foobar_xmpl_module_init(void) {
//     int rc;
//     pr_info("Generic Netlink Example Module inserted.\n");

//     // Register family with its operations and policies
//     rc = genl_register_family(&gnl_foobar_xmpl_family);
//     if (rc != 0) {
//         pr_err("FAILED: genl_register_family(): %i\n", rc);
//         pr_err("An error occurred while inserting the generic netlink example module\n");
//         return -1;
//     } else {
//         pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n");
//     }

//     mutex_init(&dumpit_cb_progress_data.mtx);

//     return 0;
// }

// /**
//  * Module/driver uninitializer. Called on module unload/removal.
//  *
//  * @return success (0) or error code.
//  */
// static void __exit gnl_foobar_xmpl_module_exit(void) {
//     int ret;
//     pr_info("Generic Netlink Example Module unloaded.\n");

//     // Unregister the family
//     ret = genl_unregister_family(&gnl_foobar_xmpl_family);
//     if (ret != 0) {
//         pr_err("genl_unregister_family() failed: %i\n", ret);
//         return;
//     } else {
//         pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n");
//     }

//     mutex_destroy(&dumpit_cb_progress_data.mtx);
// }


// ----

// static int __init rpmsg_sdb_drv_init(void)
// {
// 	int ret = 0;

// 	/* Register rpmsg device */
// 	ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv);

// 	if (ret) {
// 		pr_err("rpmsg_sdb(ERROR): Failed to register device\n");
// 		return ret;
// 	}

// 	pr_info("rpmsg_sdb: Init done\n");

// 	return ret;
// }

// static void __exit rpmsg_sdb_drv_exit(void)
// {
// 	unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv);
// 	pr_info("rpmsg_sdb: Exit\n");
// }

// module_init(rpmsg_sdb_drv_init);
// module_exit(rpmsg_sdb_drv_exit);
