/* 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 #include // basic definitions for kernel module development #include // definitions for generic netlink families, policies etc; // transitive dependencies for basic netlink, sockets etc #include // required for locking inside the .dumpit callback demonstration #include #include // 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 . 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 // /** // * 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 // /** // * 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 | // | | // ---------------------------------- // 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);