Files
vrpmdv-yocto-recipes/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-controler.cold
Markus Lehr 454ae2f46e added vrpmdv-rtservice, vrpmdv-mon-datafile,
vrpmdv-monitoring-controler, vrpmdv-mon-tty
2024-06-21 08:51:47 +02:00

805 lines
30 KiB
Plaintext

/* 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;
// 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
rc = rpmsg_send(vrpmdv_mon.rpdev->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);
{
pr_info("wait for response\n");
// wait until receive_queue_flag=1 , that means we have received data from Copro
wait_event_interruptible(vrpmdv_mon.receive_queue, vrpmdv_mon.receive_queue_flag != 0 );
pr_info("received data \n");
//Copy data
vrpmdv_mon.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);
}
// 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 headers
// 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;
}
// 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, recv_msg);
if (rc != 0) {
pr_err("An error occurred in %s():\n", __func__);
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
if (rc != 0) {
pr_err("An error occurred in %s():\n", __func__);
return -rc;
}
return 0;
}
/**
* 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);
vrpmdv_mon.receive_queue_flag= DATARECEIVED;
wake_up_interruptible(&(vrpmdv_mon.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);
// 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(&(vrpmdv_mon.sendMTX));
init_waitqueue_head (&(vrpmdv_mon.receive_queue));
vrpmdv_mon.rpdev = 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(&(vrpmdv_mon.sendMTX));
wake_up_interruptible(&(vrpmdv_mon.receive_queue));
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);