66c98ec4728cab277ea2f701a375ac5ac55ad56c
[processor-sdk/open-amp.git] / apps / tests / master / linux / kernelspace / rpmsg_echo_test_kern_app / rpmsg_echo_test_kern_app.c
1 /*
2 * RPMSG Echo Test Kernel Driver
3 *
4 * Copyright (C) 2014 Mentor Graphics Corporation
5 * Copyright (C) 2015 Xilinx, Inc.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/rpmsg.h>
20 #include <linux/slab.h>
21 #include <linux/device.h>
22 #include <linux/mutex.h>
23 #include <linux/uaccess.h>
24 #include <linux/errno.h>
25 #include <linux/workqueue.h>
27 #define MAX_RPMSG_BUFF_SIZE 512
29 #define PAYLOAD_MIN_SIZE 1
30 #define PAYLOAD_MAX_SIZE (MAX_RPMSG_BUFF_SIZE - sizeof(struct rpmsg_hdr) - 24)
31 #define NUM_PAYLOADS (PAYLOAD_MAX_SIZE/PAYLOAD_MIN_SIZE)
33 /* Shutdown message ID */
34 #define SHUTDOWN_MSG 0xEF56A55A
36 #define RPMG_INIT_MSG "init_msg"
38 struct _rpmsg_dev_params {
39 struct device *rpmsg_dev;
40 struct mutex sync_lock;
41 struct rpmsg_channel *rpmsg_chnl;
42 char tx_buff[MAX_RPMSG_BUFF_SIZE]; /* buffer to keep the message to send */
43 u32 rpmsg_dst;
44 int err_cnt;
45 struct work_struct rpmsg_work;
46 };
48 struct _payload {
49 unsigned int num;
50 unsigned int size;
51 unsigned char data[];
52 };
54 static const char *const shutdown_argv[]
55 = { "/sbin/shutdown", "-h", "-P", "now", NULL };
57 static int rpmsg_echo_test_kern_app_echo_test(struct _rpmsg_dev_params
58 *rpmsg_dev)
59 {
60 static int payload_num = 0;
61 static int next_payload_size = PAYLOAD_MIN_SIZE;
62 int payload_size = 0;
63 int i = 0;
64 struct _payload *payload;
65 int err = 0;
66 if (!rpmsg_dev) {
67 return -1;
68 }
69 //pr_info("%s\n", __func__);
70 if (next_payload_size > PAYLOAD_MAX_SIZE) {
71 *((unsigned int *)rpmsg_dev->tx_buff) = SHUTDOWN_MSG;
72 //pr_info("Sending shutdown message to remote.\n");
73 err =
74 rpmsg_send(rpmsg_dev->rpmsg_chnl, rpmsg_dev->tx_buff,
75 sizeof(unsigned int));
76 if (err) {
77 pr_err("Shutdown message send failed.\n");
78 return -1;
79 }
80 } else {
81 payload_size = next_payload_size++;
82 payload = (struct _payload *)(rpmsg_dev->tx_buff);
83 payload->num = payload_num++;
84 payload->size = payload_size;
85 memset(payload->data, 0xA5, payload_size);
86 err =
87 rpmsg_send(rpmsg_dev->rpmsg_chnl, rpmsg_dev->tx_buff,
88 (payload_size + sizeof(struct _payload)));
89 if (err) {
90 pr_err("Failed to send echo test message to remote.\n");
91 return -1;
92 }
93 }
94 return payload_size;
95 }
97 static void rpmsg_echo_test_kern_app_work_func(struct work_struct *work)
98 {
99 struct _rpmsg_dev_params *local =
100 container_of(work, struct _rpmsg_dev_params, rpmsg_work);
101 //pr_info ("%s:%p.\n", __func__, local);
102 int local_err_cnt = 0;
103 if (rpmsg_echo_test_kern_app_echo_test(local) <= 0) {
104 mutex_lock(&local->sync_lock);
105 local_err_cnt = local->err_cnt;
106 mutex_unlock(&local->sync_lock);
107 pr_info("\r\n *******************************************\r\n");
108 pr_info("\r\n Echo Test Results: Error count = %d\r\n",
109 local_err_cnt);
110 pr_info("\r\n *******************************************\r\n");
111 }
112 }
114 static void rpmsg_echo_test_kern_app_cb(struct rpmsg_channel *rpdev, void *data,
115 int len, void *priv, u32 src)
116 {
118 struct _rpmsg_dev_params *local = dev_get_drvdata(&rpdev->dev);
119 struct _payload *payload = data;
120 int i = 0;
122 /* Shutdown Linux if such a message is received. Only applicable
123 when Linux is a remoteproc remote. */
125 //pr_info ("%s\n", __func__);
126 if (!data) {
127 return;
128 }
130 if ((*(int *)data) == SHUTDOWN_MSG) {
131 dev_info(&rpdev->dev,
132 "shutdown message is received. Shutting down...\n");
133 call_usermodehelper(shutdown_argv[0], shutdown_argv, NULL,
134 UMH_NO_WAIT);
135 } else {
136 pr_info("\r\n Master : Linux Kernal Space : Received payload ");
137 pr_info("num %d of size %d, total len %d. \r\n", payload->num,
138 payload->size, len);
139 for (i = 0; i < payload->size; i++) {
140 if (payload->data[i] != 0xA5) {
141 pr_err("\r\n Data corruption at index %d. \r\n",
142 i);
143 mutex_lock(&local->sync_lock);
144 local->err_cnt++;
145 mutex_unlock(&local->sync_lock);
146 break;
147 }
148 }
149 schedule_work(&local->rpmsg_work);
150 }
151 }
153 static int rpmsg_echo_test_kern_app_probe(struct rpmsg_channel *rpdev)
154 {
155 struct _rpmsg_dev_params *local;
156 dev_info(&rpdev->dev, "%s", __func__);
158 local = devm_kzalloc(&rpdev->dev, sizeof(struct _rpmsg_dev_params),
159 GFP_KERNEL);
160 if (!local) {
161 dev_err(&rpdev->dev,
162 "Failed to allocate memory for rpmsg user dev.\n");
163 return -ENOMEM;
164 }
165 memset(local, 0x0, sizeof(struct _rpmsg_dev_params));
167 /* Initialize mutex */
168 mutex_init(&local->sync_lock);
170 local->rpmsg_chnl = rpdev;
172 dev_set_drvdata(&rpdev->dev, local);
174 sprintf(local->tx_buff, RPMG_INIT_MSG);
175 if (rpmsg_sendto(local->rpmsg_chnl,
176 local->tx_buff, sizeof(RPMG_INIT_MSG), rpdev->dst)) {
177 dev_err(&rpdev->dev, "Failed to send init_msg to target 0x%x.",
178 local->rpmsg_dst);
179 goto error0;
180 }
181 dev_info(&rpdev->dev, "Sent init_msg to target 0x%x.",
182 local->rpmsg_dst);
184 dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
185 rpdev->src, rpdev->dst);
187 INIT_WORK(&local->rpmsg_work, rpmsg_echo_test_kern_app_work_func);
188 #if 0
189 if (rpmsg_echo_test_kern_app_echo_test(local) <= 0) {
190 pr_err("Failed to send echo test message to remote.\n");
191 return -1;
192 }
193 #else
194 schedule_work(&local->rpmsg_work);
195 #endif
197 goto out;
198 error0:
199 return -ENODEV;
200 out:
201 return 0;
202 }
204 static void rpmsg_echo_test_kern_app_remove(struct rpmsg_channel *rpdev)
205 {
206 struct _rpmsg_dev_params *local = dev_get_drvdata(&rpdev->dev);
207 flush_work(&local->rpmsg_work);
208 }
210 static struct rpmsg_device_id rpmsg_echo_test_kern_app_id_table[] = {
211 {.name = "rpmsg-openamp-demo-channel"},
212 {},
213 };
215 static struct rpmsg_driver rpmsg_echo_test_kern_app_drv = {
216 .drv.name = "rpmsg_echo_test_kern_app",
217 .drv.owner = THIS_MODULE,
218 .id_table = rpmsg_echo_test_kern_app_id_table,
219 .probe = rpmsg_echo_test_kern_app_probe,
220 .remove = rpmsg_echo_test_kern_app_remove,
221 .callback = rpmsg_echo_test_kern_app_cb,
222 };
224 static int __init init(void)
225 {
226 return register_rpmsg_driver(&rpmsg_echo_test_kern_app_drv);
227 }
229 static void __exit fini(void)
230 {
231 unregister_rpmsg_driver(&rpmsg_echo_test_kern_app_drv);
232 }
234 module_init(init);
235 module_exit(fini);
237 MODULE_DESCRIPTION
238 ("Sample driver to exposes rpmsg svcs to userspace via a char device");
239 MODULE_LICENSE("GPL v2");