lib: Add support for J721S2 SoCs
[rpmsg/ti-rpmsg-char.git] / src / rpmsg_char.c
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 <signal.h>
45 #include <sys/ioctl.h>
46 #include <sys/stat.h>
47 #include <linux/rpmsg.h>
49 #define RPMSG_ADDR_ANY          0xFFFFFFFF
51 #include "ti_rpmsg_char.h"
52 #include "rpmsg_char_internal.h"
54 #define printerr printf
56 #define to_rpmsg_char_endpt(ptr) ({                             \
57                 const rpmsg_char_dev_t *_mptr = (ptr);          \
58                 (struct rpmsg_char_endpt *)((char *)_mptr -     \
59                         offsetof(struct rpmsg_char_endpt, rcdev)); })
61 struct rpmsg_char_endpt {
62         struct rpmsg_char_endpt *next;
63         const struct rproc_map *map;
64         char *rpath;
65         struct rpmsg_char_dev rcdev;
66         char rpmsg_dev_name[64];
67         int remoteproc_id;
68         int virtio_id;
69         int ctrl_id;
70         int rpmsg_id;
71 };
73 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
75 static bool inited = false;
76 static int soc_id = -1;
77 static struct soc_rprocs soc_data;
78 static struct rpmsg_char_endpt *ghead = NULL;
79 static struct rpmsg_char_endpt *gtail = NULL;
81 static void _list_add(struct rpmsg_char_endpt *ept)
82 {
83         if (!ghead)
84                 ghead = ept;
86         if (gtail)
87                 gtail->next = ept;
88         gtail = ept;
89 }
91 static void _list_remove(struct rpmsg_char_endpt *ept)
92 {
93         struct rpmsg_char_endpt *iter, *prev;
95         if (ghead == ept) {
96                 if (gtail == ept)
97                         gtail = NULL;
98                 ghead = ept->next;
99                 return;
100         }
102         prev = ghead;
103         for (iter = ghead->next; iter != NULL; prev = iter, iter = iter->next) {
104                 if (iter == ept) {
105                         if (prev)
106                                 prev->next = ept->next;
107                         if (gtail == ept)
108                                 gtail = prev;
109                         return;
110                 }
111         }
114 static bool _list_is_present(struct rpmsg_char_endpt *ept)
116         struct rpmsg_char_endpt *iter;
118         for (iter = ghead; iter != NULL; iter = iter->next) {
119                 if (iter == ept)
120                         return true;
121         }
123         return false;
126 static int _rpmsg_char_check_system(void)
128         if (check_dir("/sys/class/remoteproc")) {
129                 fprintf(stderr, "remoteproc framework is not enabled/installed\n");
130                 return -ENOENT;
131         }
133         if (check_dir("/sys/bus/virtio")) {
134                 fprintf(stderr, "virtio framework is not enabled/installed\n");
135                 return -ENOENT;
136         }
138         if (check_dir("/sys/bus/rpmsg")) {
139                 fprintf(stderr, "rpmsg bus driver is not enabled/installed\n");
140                 return -ENOENT;
141         }
143         if (check_dir("/sys/class/rpmsg")) {
144                 fprintf(stderr, "rpmsg_chrdev driver is not enabled/installed\n");
145                 return -ENOENT;
146         }
148         return 0;
151 /* parse sysfs to find associated kernel-allocated remoteproc and virtio ids */
152 static int _rpmsg_char_find_rproc(struct rpmsg_char_endpt *ept,
153                                   enum rproc_id id)
155         const struct rproc_map *r = soc_data.map;
156         unsigned int remoteproc_id;
157         unsigned int virtio_id;
158         char fpath[512];
159         int i;
160         int ret = -ENODEV;
162         for (i = 0; i < soc_data.num_rprocs; i++, r++) {
163                 if (id == r->id)
164                         break;
165         }
166         if (i == soc_data.num_rprocs) {
167                 fprintf(stderr, "%s: SoC doesn't have rproc id %d\n",
168                         __func__, id);
169                 return -EINVAL;
170         }
172         ept->rpath = file_deref_link("/sys/bus/platform/devices", r->rproc_name);
173         if (check_dir(ept->rpath)) {
174                 fprintf(stderr, "%s: %s device is mostly yet to be created!\n",
175                         __func__, r->rproc_name);
176                 return -ENOENT;
177         }
179         /* retrieve the dynamically created kernel remoteproc id */
180         memset(&fpath, 0, sizeof(fpath));
181         sprintf(fpath, "%s/remoteproc", ept->rpath);
182         ret = get_child_dir_suffix(fpath, "remoteproc%u", &remoteproc_id);
183         if (ret) {
184                 if (ret == -ENOENT) {
185                         fprintf(stderr, "%s: %s is either not probed or not a remoteproc!\n",
186                                 __func__, r->rproc_name);
187                 }
188                 return ret;
189         }
191         /* retrieve the dynamically created kernel virtio id */
192         memset(&fpath, 0, sizeof(fpath));
193         sprintf(fpath, "%s/remoteproc/remoteproc%u/remoteproc%u#vdev0buffer",
194                 ept->rpath, remoteproc_id, remoteproc_id);
195         ret = get_child_dir_suffix(fpath, "virtio%u", &virtio_id);
196         if (ret) {
197                 if (ret == -ENOENT) {
198                         fprintf(stderr, "%s: %s does not have any virtio devices!\n",
199                                 __func__, r->rproc_name);
200                 }
201                 return ret;
202         }
204         ept->map = r;
205         ept->remoteproc_id = remoteproc_id;
206         ept->virtio_id = virtio_id;
208         return 0;
211 static int _rpmsg_char_find_ctrldev(struct rpmsg_char_endpt *ept,
212                                     char *dev_name, int remote_port)
214         char virtio[16] = { 0 };
215         struct dirent *iter;
216         DIR *dir;
217         char fpath[512];
218         char *rpath;
219         unsigned int ctrl_id;
220         int ret;
222         sprintf(virtio, "virtio%u", ept->virtio_id);
223         rpath = file_deref_link("/sys/bus/virtio/devices", virtio);
224         if (!rpath)
225                 return -ENOENT;
227         sprintf(ept->rpmsg_dev_name, "virtio%u.%s.-1.%d", ept->virtio_id,
228                 dev_name, remote_port);
230         dir = opendir(rpath);
231         if (!dir) {
232                 ret = -errno;
233                 goto free_rpath;
234         }
236         /*
237          * find a directory that matches the published rpmsg device of the
238          * form virtio%d.<rpmsg_dev.%d.%d
239          */
240         while ((iter = readdir(dir))) {
241                 if (iter->d_type == DT_DIR) {
242                         if (!strcmp(iter->d_name, ept->rpmsg_dev_name))
243                                 break;
244                 }
245         }
246         if (!iter) {
247                 fprintf(stderr, "%s: could not find the matching rpmsg_ctrl device for %s\n",
248                         __func__, ept->rpmsg_dev_name);
249                 ret = -ENOENT;
250                 goto chrdev_nodir;
251         }
253         memset(&fpath, 0, sizeof(fpath));
254         sprintf(fpath, "%s/%s/rpmsg", rpath, iter->d_name);
255         if (check_dir(fpath)) {
256                 fprintf(stderr, "%s: rpmsg directory doesn't exist under %s\n",
257                         __func__, ept->rpmsg_dev_name);
258                 ret = -ENOENT;
259                 goto chrdev_nodir;
260         }
262         /* get rpmsg_ctrl id */
263         ret = get_child_dir_suffix(fpath, "rpmsg_ctrl%u", &ctrl_id);
264         if (ret)
265                 goto chrdev_nodir;
267         ept->ctrl_id = ctrl_id;
269 chrdev_nodir:
270         closedir(dir);
271 free_rpath:
272         free(rpath);
273         return ret;
276 static int _rpmsg_char_create_eptdev(struct rpmsg_char_endpt *ept,
277                                      char *eptdev_name, int remote_port)
279         int fd, ret;
280         char ctrldev_path[32] = { 0 };
281         struct rpmsg_endpoint_info ept_info = { 0 };
283         sprintf(ctrldev_path, "/dev/rpmsg_ctrl%u", ept->ctrl_id);
285         fd = open(ctrldev_path, O_RDWR);
286         if (fd < 0) {
287                 fprintf(stderr, "%s: could not open rpmsg_ctrl dev node for id %d\n",
288                         __func__, ept->ctrl_id);
289                 return fd;
290         }
292         /* let kernel dynamically allocate the local end-point */
293         ept_info.src = RPMSG_ADDR_ANY;
294         ept_info.dst = remote_port;
295         sprintf(ept_info.name, "%s", eptdev_name);
297         ret = ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);
298         if (ret) {
299                 fprintf(stderr, "%s: ctrl_fd ioctl: %s\n", __func__,
300                         strerror(errno));
301         }
303         close(fd);
305         return ret;
308 static int _rpmsg_char_destroy_eptdev(int fd)
310         int ret;
312         ret = ioctl(fd, RPMSG_DESTROY_EPT_IOCTL, NULL);
313         if (ret) {
314                 fprintf(stderr, "%s: could not destroy endpt %d\n",
315                         __func__, fd);
316                 return ret;
317         }
319         close(fd);
320         return 0;
323 static int _rpmsg_char_get_rpmsg_id(struct rpmsg_char_endpt *ept,
324                                     char *eptdev_name)
326         const struct rproc_map *r = ept->map;
327         bool found = false;
328         char rpmsg[16] = { 0 };
329         char fpath[512];
330         char dev_name[32];
331         char *rpath;
332         struct dirent *iter;
333         DIR *dir;
334         int ret;
336         sprintf(rpmsg, "rpmsg_ctrl%u", ept->ctrl_id);
337         rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
338         if (!rpath)
339                 return -ENOENT;
341         dir = opendir(rpath);
342         if (!dir) {
343                 free(rpath);
344                 return -errno;
345         }
347         while ((iter = readdir(dir))) {
348                 if (iter->d_type != DT_DIR ||
349                     strncmp(iter->d_name, "rpmsg", strlen("rpmsg")))
350                         continue;
352                 memset(&fpath, 0, sizeof(fpath));
353                 memset(&dev_name, 0, sizeof(dev_name));
354                 sprintf(fpath, "%s/%s/name", rpath, iter->d_name);
355                 ret = file_read_string(fpath, dev_name, sizeof(dev_name));
356                 if (ret < 0)
357                         continue;
359                 if (strcmp(dev_name, eptdev_name))
360                         continue;
362                 sscanf(iter->d_name, "rpmsg%u", &ept->rpmsg_id);
363                 found = true;
364                 break;
365         }
367         free(rpath);
368         closedir(dir);
369         return found ? 0 : -1;
372 static int _rpmsg_char_get_local_endpt(struct rpmsg_char_endpt *ept)
374         const struct rproc_map *r = ept->map;
375         char fpath[512] = { 0 };
376         char rpmsg[16] = { 0 };
377         char *rpath;
379         sprintf(rpmsg, "rpmsg%u", ept->rpmsg_id);
380         rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
381         if (!rpath) {
382                 fprintf(stderr, "%s: rpmsg%u realpath failed\n",
383                         __func__, ept->rpmsg_id);
384                 return -ENOENT;
385         }
387         sprintf(fpath, "%s/src", rpath);
388         free(rpath);
390         return file_read_value(fpath);
393 static int _rpmsg_char_open_eptdev(struct rpmsg_char_endpt *ept,
394                                    char *eptdev_name, int flags)
396         char eptdev_path[32] = { 0 };
397         int efd, endpt;
398         int ret;
400         ret = _rpmsg_char_get_rpmsg_id(ept, eptdev_name);
401         if (ret < 0)
402                 return ret;
404         sprintf(eptdev_path, "/dev/rpmsg%u", ept->rpmsg_id);
406         efd = open(eptdev_path, O_RDWR | flags);
407         if (efd < 0) {
408                 fprintf(stderr, "failed to open eptdev %s\n", eptdev_path);
409                 return -errno;
410         }
412         /* local end-point is assigned after the open call */
413         endpt = _rpmsg_char_get_local_endpt(ept);
414         if (endpt < 0) {
415                 _rpmsg_char_destroy_eptdev(efd);
416                 return endpt;
417         }
419         ept->rcdev.fd = efd;
420         ept->rcdev.endpt = endpt;
422         return 0;
425 static struct rpmsg_char_endpt *_rpmsg_char_endpt_alloc(void)
427         struct rpmsg_char_endpt *ept;
429         ept = calloc(sizeof(*ept), 1);
430         if (!ept) {
431                 fprintf(stderr, "%s: failed to allocate memory\n", __func__);
432                 return NULL;
433         }
435         ept->remoteproc_id = -1;
436         ept->virtio_id = -1;
437         ept->ctrl_id = -1;
438         ept->rpmsg_id = -1;
440         return ept;
443 static void _rpmsg_char_endpt_free(struct rpmsg_char_endpt *ept)
445         if (ept->rpath)
446                 free(ept->rpath);
447         free(ept);
450 static void _rpmsg_char_cleanup(void)
452         struct rpmsg_char_endpt *iter, *next;
453         int ret;
455         if (ghead)
456                 fprintf(stderr, "Application did not close some rpmsg_char devices\n");
458         for (iter = ghead; iter != NULL; iter = next) {
459                 next = iter->next;
460                 ret = rpmsg_char_close(&iter->rcdev);
461                 if (ret) {
462                         fprintf(stderr, "rpmsg_char_close failed during cleanup, rcdev = 0x%x, ret = %d\n",
463                                 &iter->rcdev, ret);
464                 }
465         }
468 static void signal_handler(int sig)
470         fprintf(stderr, "\nClean up and exit while handling signal %d\n", sig);
471         rpmsg_char_exit();
472         exit(EXIT_FAILURE);
475 /*
476  * setup the signal handlers optionally, only if applications have not
477  * registered one themselves. Any new signal handler registration in
478  * the application after rpmsg_char_init() is invoked will install the
479  * application's handlers. Applications are responsible for calling
480  * rpmsg_char_exit() in their signal handlers (if present) to ensure
481  * proper cleanup
482  */
483 static int _rpmsg_char_register_signal_handlers(void)
485         struct sigaction sigint = { 0 };
486         struct sigaction sigterm = { 0 };
487         int ret;
489         ret = sigaction(SIGINT, NULL, &sigint);
490         if (ret < 0)
491                 return -errno;
493         ret = sigaction(SIGTERM, NULL, &sigterm);
494         if (ret < 0)
495                 return -errno;
497         if (!sigint.sa_sigaction)
498                 signal(SIGINT, signal_handler);
499         if (!sigterm.sa_sigaction)
500                 signal(SIGTERM, signal_handler);
502         return 0;
505 rpmsg_char_dev_t *rpmsg_char_open(enum rproc_id id, char *dev_name,
506                                   int remote_endpt, char *eptdev_name,
507                                   int flags)
509         struct rpmsg_char_endpt *ept = NULL;
510         char *def_dev_name = "rpmsg_chrdev";
511         int ret, local_endpt;
513         pthread_mutex_lock(&mutex);
515         if (!inited) {
516                 fprintf(stderr, "%s cannot be invoked without initialization\n",
517                         __func__);
518                 goto unlock;
519         }
521         if (remote_endpt == RPMSG_ADDR_ANY) {
522                 fprintf(stderr, "%s: remote_endpt cannot be RPMSG_ADDR_ANY\n",
523                         __func__);
524                 goto unlock;
525         }
527         if (!eptdev_name || strlen(eptdev_name) >= 32) {
528                 fprintf(stderr, "%s: invalid eptdev_name\n", __func__);
529                 goto unlock;
530         }
532         if (!dev_name)
533                 dev_name = def_dev_name;
535         ept = _rpmsg_char_endpt_alloc();
536         if (!ept)
537                 goto unlock;
539         ret = _rpmsg_char_find_rproc(ept, id);
540         if (ret)
541                 goto out;
543         ret = _rpmsg_char_find_ctrldev(ept, dev_name, remote_endpt);
544         if (ret)
545                 goto out;
547         ret = _rpmsg_char_create_eptdev(ept, eptdev_name, remote_endpt);
548         if (ret)
549                 goto out;
551         ret = _rpmsg_char_open_eptdev(ept, eptdev_name, flags);
552         if (ret < 0)
553                 goto out;
555         _list_add(ept);
556         goto unlock;
558 out:
559         _rpmsg_char_endpt_free(ept);
560         ept = NULL;
561 unlock:
562         pthread_mutex_unlock(&mutex);
563         return ept ? &ept->rcdev : NULL;
566 int rpmsg_char_close(rpmsg_char_dev_t *rcdev)
568         int ret;
569         struct rpmsg_char_endpt *ept;
571         if (!rcdev)
572                 return -EINVAL;
574         pthread_mutex_lock(&mutex);
576         ept = to_rpmsg_char_endpt(rcdev);
577         if (!_list_is_present(ept)) {
578                 fprintf(stderr, "%s: invalid handle passed in rcdev = 0x%x\n",
579                         __func__, rcdev);
580                 ret = -ENOENT;
581                 goto out;
582         }
584         ret = _rpmsg_char_destroy_eptdev(rcdev->fd);
585         if (ret)
586                 goto out;
588         _list_remove(ept);
589         _rpmsg_char_endpt_free(ept);
591 out:
592         pthread_mutex_unlock(&mutex);
593         return ret;
596 int rpmsg_char_init(char *soc_name)
598         int ret = 0;
600         pthread_mutex_lock(&mutex);
602         if (inited) {
603                 ret = 1;
604                 goto out;
605         }
607         ret = _rpmsg_char_check_system();
608         if (ret < 0)
609                 goto out;
611         ret = _rpmsg_char_find_soc_family(soc_name, &soc_data);
612         if (ret < 0)
613                 goto out;
615         ret = _rpmsg_char_register_signal_handlers();
616         if (ret < 0)
617                 goto out;
619         inited = true;
621 out:
622         pthread_mutex_unlock(&mutex);
623         return ret;
626 void rpmsg_char_exit(void)
628         pthread_mutex_lock(&mutex);
629         if (!inited) {
630                 fprintf(stderr, "%s: library not initialized previously\n",
631                         __func__);
632                 pthread_mutex_unlock(&mutex);
633                 return;
634         }
635         pthread_mutex_unlock(&mutex);
637         _rpmsg_char_cleanup();
639         pthread_mutex_lock(&mutex);
640         inited = false;
641         pthread_mutex_unlock(&mutex);