1 /*
2 * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * * Neither the name of Texas Instruments Incorporated nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
33 #include <stdio.h>
34 #include <stdbool.h>
35 #include <stddef.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <dirent.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <stdint.h>
43 #include <pthread.h>
44 #include <sys/ioctl.h>
45 #include <sys/stat.h>
46 #include <linux/rpmsg.h>
48 #define RPMSG_ADDR_ANY 0xFFFFFFFF
50 #include "ti_rpmsg_char.h"
51 #include "rpmsg_char_internal.h"
53 #define printerr printf
55 #define to_rpmsg_char_endpt(ptr) ({ \
56 const rpmsg_char_dev_t *_mptr = (ptr); \
57 (struct rpmsg_char_endpt *)((char *)_mptr - \
58 offsetof(struct rpmsg_char_endpt, rcdev)); })
60 struct rpmsg_char_endpt {
61 struct rpmsg_char_endpt *next;
62 const struct rproc_map *map;
63 char *rpath;
64 struct rpmsg_char_dev rcdev;
65 char rpmsg_dev_name[64];
66 int remoteproc_id;
67 int virtio_id;
68 int ctrl_id;
69 int rpmsg_id;
70 };
72 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
74 static bool inited = false;
75 static int soc_id = -1;
76 static struct soc_rprocs soc_data;
77 static struct rpmsg_char_endpt *ghead = NULL;
78 static struct rpmsg_char_endpt *gtail = NULL;
80 static void _list_add(struct rpmsg_char_endpt *ept)
81 {
82 if (!ghead)
83 ghead = ept;
85 if (gtail)
86 gtail->next = ept;
87 gtail = ept;
88 }
90 static void _list_remove(struct rpmsg_char_endpt *ept)
91 {
92 struct rpmsg_char_endpt *iter, *prev;
94 if (ghead == ept) {
95 if (gtail == ept)
96 gtail = NULL;
97 ghead = ept->next;
98 return;
99 }
101 prev = ghead;
102 for (iter = ghead->next; iter != NULL; prev = iter, iter = iter->next) {
103 if (iter == ept) {
104 if (prev)
105 prev->next = ept->next;
106 if (gtail == ept)
107 gtail = prev;
108 return;
109 }
110 }
111 }
113 static bool _list_is_present(struct rpmsg_char_endpt *ept)
114 {
115 struct rpmsg_char_endpt *iter;
117 for (iter = ghead; iter != NULL; iter = iter->next) {
118 if (iter == ept)
119 return true;
120 }
122 return false;
123 }
125 static int _rpmsg_char_check_system(void)
126 {
127 if (check_dir("/sys/class/remoteproc")) {
128 fprintf(stderr, "remoteproc framework is not enabled/installed\n");
129 return -ENOENT;
130 }
132 if (check_dir("/sys/bus/virtio")) {
133 fprintf(stderr, "virtio framework is not enabled/installed\n");
134 return -ENOENT;
135 }
137 if (check_dir("/sys/bus/rpmsg")) {
138 fprintf(stderr, "rpmsg bus driver is not enabled/installed\n");
139 return -ENOENT;
140 }
142 if (check_dir("/sys/class/rpmsg")) {
143 fprintf(stderr, "rpmsg_chrdev driver is not enabled/installed\n");
144 return -ENOENT;
145 }
147 return 0;
148 }
150 /* parse sysfs to find associated kernel-allocated remoteproc and virtio ids */
151 static int _rpmsg_char_find_rproc(struct rpmsg_char_endpt *ept,
152 enum rproc_id id)
153 {
154 const struct rproc_map *r = soc_data.map;
155 unsigned int remoteproc_id;
156 unsigned int virtio_id;
157 char fpath[512];
158 int i;
159 int ret = -ENODEV;
161 for (i = 0; i < soc_data.num_rprocs; i++, r++) {
162 if (id == r->id)
163 break;
164 }
165 if (i == soc_data.num_rprocs) {
166 fprintf(stderr, "%s: SoC doesn't have rproc id %d\n",
167 __func__, id);
168 return -EINVAL;
169 }
171 ept->rpath = file_deref_link("/sys/bus/platform/devices", r->rproc_name);
172 if (check_dir(ept->rpath)) {
173 fprintf(stderr, "%s: %s device is mostly yet to be created!\n",
174 __func__, r->rproc_name);
175 return -ENOENT;
176 }
178 /* retrieve the dynamically created kernel remoteproc id */
179 memset(&fpath, 0, sizeof(fpath));
180 sprintf(fpath, "%s/remoteproc", ept->rpath);
181 ret = get_child_dir_suffix(fpath, "remoteproc%u", &remoteproc_id);
182 if (ret) {
183 if (ret == -ENOENT) {
184 fprintf(stderr, "%s: %s is either not probed or not a remoteproc!\n",
185 __func__, r->rproc_name);
186 }
187 return ret;
188 }
190 /* retrieve the dynamically created kernel virtio id */
191 memset(&fpath, 0, sizeof(fpath));
192 sprintf(fpath, "%s/remoteproc/remoteproc%u/remoteproc%u#vdev0buffer",
193 ept->rpath, remoteproc_id, remoteproc_id);
194 ret = get_child_dir_suffix(fpath, "virtio%u", &virtio_id);
195 if (ret) {
196 if (ret == -ENOENT) {
197 fprintf(stderr, "%s: %s does not have any virtio devices!\n",
198 __func__, r->rproc_name);
199 }
200 return ret;
201 }
203 ept->map = r;
204 ept->remoteproc_id = remoteproc_id;
205 ept->virtio_id = virtio_id;
207 return 0;
208 }
210 static int _rpmsg_char_find_ctrldev(struct rpmsg_char_endpt *ept,
211 char *dev_name, int remote_port)
212 {
213 char virtio[16] = { 0 };
214 struct dirent *iter;
215 DIR *dir;
216 char fpath[512];
217 char *rpath;
218 unsigned int ctrl_id;
219 int ret;
221 sprintf(virtio, "virtio%u", ept->virtio_id);
222 rpath = file_deref_link("/sys/bus/virtio/devices", virtio);
223 if (!rpath)
224 return -ENOENT;
226 sprintf(ept->rpmsg_dev_name, "virtio%u.%s.-1.%d", ept->virtio_id,
227 dev_name, remote_port);
229 dir = opendir(rpath);
230 if (!dir) {
231 ret = -errno;
232 goto free_rpath;
233 }
235 /*
236 * find a directory that matches the published rpmsg device of the
237 * form virtio%d.<rpmsg_dev.%d.%d
238 */
239 while ((iter = readdir(dir))) {
240 if (iter->d_type == DT_DIR) {
241 if (!strcmp(iter->d_name, ept->rpmsg_dev_name))
242 break;
243 }
244 }
245 if (!iter) {
246 fprintf(stderr, "%s: could not find the matching rpmsg_ctrl device for %s\n",
247 __func__, ept->rpmsg_dev_name);
248 ret = -ENOENT;
249 goto chrdev_nodir;
250 }
252 memset(&fpath, 0, sizeof(fpath));
253 sprintf(fpath, "%s/%s/rpmsg", rpath, iter->d_name);
254 if (check_dir(fpath)) {
255 fprintf(stderr, "%s: rpmsg directory doesn't exist under %s\n",
256 __func__, ept->rpmsg_dev_name);
257 ret = -ENOENT;
258 goto chrdev_nodir;
259 }
261 /* get rpmsg_ctrl id */
262 ret = get_child_dir_suffix(fpath, "rpmsg_ctrl%u", &ctrl_id);
263 if (ret)
264 goto chrdev_nodir;
266 ept->ctrl_id = ctrl_id;
268 chrdev_nodir:
269 closedir(dir);
270 free_rpath:
271 free(rpath);
272 return ret;
273 }
275 static int _rpmsg_char_create_eptdev(struct rpmsg_char_endpt *ept,
276 char *eptdev_name, int remote_port)
277 {
278 int fd, ret;
279 char ctrldev_path[32] = { 0 };
280 struct rpmsg_endpoint_info ept_info = { 0 };
282 sprintf(ctrldev_path, "/dev/rpmsg_ctrl%u", ept->ctrl_id);
284 fd = open(ctrldev_path, O_RDWR);
285 if (fd < 0) {
286 fprintf(stderr, "%s: could not open rpmsg_ctrl dev node for id %d\n",
287 __func__, ept->ctrl_id);
288 return fd;
289 }
291 /* let kernel dynamically allocate the local end-point */
292 ept_info.src = RPMSG_ADDR_ANY;
293 ept_info.dst = remote_port;
294 sprintf(ept_info.name, "%s", eptdev_name);
296 ret = ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);
297 if (ret) {
298 fprintf(stderr, "%s: ctrl_fd ioctl: %s\n", __func__,
299 strerror(errno));
300 }
302 close(fd);
304 return ret;
305 }
307 static int _rpmsg_char_destroy_eptdev(int fd)
308 {
309 int ret;
311 ret = ioctl(fd, RPMSG_DESTROY_EPT_IOCTL, NULL);
312 if (ret) {
313 fprintf(stderr, "%s: could not destroy endpt %d\n",
314 __func__, fd);
315 return ret;
316 }
318 close(fd);
319 return 0;
320 }
322 static int _rpmsg_char_get_rpmsg_id(struct rpmsg_char_endpt *ept,
323 char *eptdev_name)
324 {
325 const struct rproc_map *r = ept->map;
326 bool found = false;
327 char rpmsg[16] = { 0 };
328 char fpath[512];
329 char dev_name[32];
330 char *rpath;
331 struct dirent *iter;
332 DIR *dir;
333 int ret;
335 sprintf(rpmsg, "rpmsg_ctrl%u", ept->ctrl_id);
336 rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
337 if (!rpath)
338 return -ENOENT;
340 dir = opendir(rpath);
341 if (!dir) {
342 free(rpath);
343 return -errno;
344 }
346 while ((iter = readdir(dir))) {
347 if (iter->d_type != DT_DIR ||
348 strncmp(iter->d_name, "rpmsg", strlen("rpmsg")))
349 continue;
351 memset(&fpath, 0, sizeof(fpath));
352 memset(&dev_name, 0, sizeof(dev_name));
353 sprintf(fpath, "%s/%s/name", rpath, iter->d_name);
354 ret = file_read_string(fpath, dev_name, sizeof(dev_name));
355 if (ret < 0)
356 continue;
358 if (strcmp(dev_name, eptdev_name))
359 continue;
361 sscanf(iter->d_name, "rpmsg%u", &ept->rpmsg_id);
362 found = true;
363 break;
364 }
366 free(rpath);
367 closedir(dir);
368 return found ? 0 : -1;
369 }
371 static int _rpmsg_char_get_local_endpt(struct rpmsg_char_endpt *ept)
372 {
373 const struct rproc_map *r = ept->map;
374 char fpath[512] = { 0 };
375 char rpmsg[16] = { 0 };
376 char *rpath;
378 sprintf(rpmsg, "rpmsg%u", ept->rpmsg_id);
379 rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
380 if (!rpath) {
381 fprintf(stderr, "%s: rpmsg%u realpath failed\n",
382 __func__, ept->rpmsg_id);
383 return -ENOENT;
384 }
386 sprintf(fpath, "%s/src", rpath);
387 free(rpath);
389 return file_read_value(fpath);
390 }
392 static int _rpmsg_char_open_eptdev(struct rpmsg_char_endpt *ept,
393 char *eptdev_name, int flags)
394 {
395 char eptdev_path[32] = { 0 };
396 int efd, endpt;
397 int ret;
399 ret = _rpmsg_char_get_rpmsg_id(ept, eptdev_name);
400 if (ret < 0)
401 return ret;
403 sprintf(eptdev_path, "/dev/rpmsg%u", ept->rpmsg_id);
405 efd = open(eptdev_path, O_RDWR | flags);
406 if (efd < 0) {
407 fprintf(stderr, "failed to open eptdev %s\n", eptdev_path);
408 return -errno;
409 }
411 /* local end-point is assigned after the open call */
412 endpt = _rpmsg_char_get_local_endpt(ept);
413 if (endpt < 0) {
414 _rpmsg_char_destroy_eptdev(efd);
415 return endpt;
416 }
418 ept->rcdev.fd = efd;
419 ept->rcdev.endpt = endpt;
421 return 0;
422 }
424 static struct rpmsg_char_endpt *_rpmsg_char_endpt_alloc(void)
425 {
426 struct rpmsg_char_endpt *ept;
428 ept = calloc(sizeof(*ept), 1);
429 if (!ept) {
430 fprintf(stderr, "%s: failed to allocate memory\n", __func__);
431 return NULL;
432 }
434 ept->remoteproc_id = -1;
435 ept->virtio_id = -1;
436 ept->ctrl_id = -1;
437 ept->rpmsg_id = -1;
439 return ept;
440 }
442 static void _rpmsg_char_endpt_free(struct rpmsg_char_endpt *ept)
443 {
444 if (ept->rpath)
445 free(ept->rpath);
446 free(ept);
447 }
449 static void _rpmsg_char_cleanup(void)
450 {
451 struct rpmsg_char_endpt *iter;
452 int ret;
454 if (ghead)
455 fprintf(stderr, "Application did not close some rpmsg_char devices\n");
457 for (iter = ghead; iter != NULL; iter = iter->next) {
458 ret = rpmsg_char_close(&iter->rcdev);
459 if (ret) {
460 fprintf(stderr, "rpmsg_char_close failed during cleanup, rcdev = 0x%x, ret = %d\n",
461 &iter->rcdev, ret);
462 }
463 }
464 }
466 rpmsg_char_dev_t *rpmsg_char_open(enum rproc_id id, char *dev_name,
467 int remote_endpt, char *eptdev_name,
468 int flags)
469 {
470 struct rpmsg_char_endpt *ept = NULL;
471 char *def_dev_name = "rpmsg_chrdev";
472 int ret, local_endpt;
474 pthread_mutex_lock(&mutex);
476 if (!inited) {
477 fprintf(stderr, "%s cannot be invoked without initialization\n",
478 __func__);
479 goto unlock;
480 }
482 if (remote_endpt == RPMSG_ADDR_ANY) {
483 fprintf(stderr, "%s: remote_endpt cannot be RPMSG_ADDR_ANY\n",
484 __func__);
485 goto unlock;
486 }
488 if (!eptdev_name || strlen(eptdev_name) >= 32) {
489 fprintf(stderr, "%s: invalid eptdev_name\n", __func__);
490 goto unlock;
491 }
493 if (!dev_name)
494 dev_name = def_dev_name;
496 ept = _rpmsg_char_endpt_alloc();
497 if (!ept)
498 goto unlock;
500 ret = _rpmsg_char_find_rproc(ept, id);
501 if (ret)
502 goto out;
504 ret = _rpmsg_char_find_ctrldev(ept, dev_name, remote_endpt);
505 if (ret)
506 goto out;
508 ret = _rpmsg_char_create_eptdev(ept, eptdev_name, remote_endpt);
509 if (ret)
510 goto out;
512 ret = _rpmsg_char_open_eptdev(ept, eptdev_name, flags);
513 if (ret < 0)
514 goto out;
516 _list_add(ept);
517 goto unlock;
519 out:
520 _rpmsg_char_endpt_free(ept);
521 ept = NULL;
522 unlock:
523 pthread_mutex_unlock(&mutex);
524 return ept ? &ept->rcdev : NULL;
525 }
527 int rpmsg_char_close(rpmsg_char_dev_t *rcdev)
528 {
529 int ret;
530 struct rpmsg_char_endpt *ept;
532 if (!rcdev)
533 return -EINVAL;
535 pthread_mutex_lock(&mutex);
537 ept = to_rpmsg_char_endpt(rcdev);
538 if (!_list_is_present(ept)) {
539 fprintf(stderr, "%s: invalid handle passed in rcdev = 0x%x\n",
540 __func__, rcdev);
541 ret = -ENOENT;
542 goto out;
543 }
545 ret = _rpmsg_char_destroy_eptdev(rcdev->fd);
546 if (ret)
547 goto out;
549 _list_remove(ept);
550 _rpmsg_char_endpt_free(ept);
552 out:
553 pthread_mutex_unlock(&mutex);
554 return ret;
555 }
557 int rpmsg_char_init(char *soc_name)
558 {
559 int ret = 0;
561 pthread_mutex_lock(&mutex);
563 if (inited) {
564 ret = 1;
565 goto out;
566 }
568 ret = _rpmsg_char_check_system();
569 if (ret < 0)
570 goto out;
572 ret = _rpmsg_char_find_soc_family(soc_name, &soc_data);
573 if (ret < 0)
574 goto out;
576 inited = true;
578 out:
579 pthread_mutex_unlock(&mutex);
580 return ret;
581 }
583 void rpmsg_char_exit(void)
584 {
585 pthread_mutex_lock(&mutex);
586 if (!inited) {
587 fprintf(stderr, "%s: library not initialized previously\n",
588 __func__);
589 pthread_mutex_unlock(&mutex);
590 return;
591 }
592 pthread_mutex_unlock(&mutex);
594 _rpmsg_char_cleanup();
596 pthread_mutex_lock(&mutex);
597 inited = false;
598 pthread_mutex_unlock(&mutex);
599 }