lib: Add initial automake build related files
[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 <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         }
113 static bool _list_is_present(struct rpmsg_char_endpt *ept)
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;
125 static int _rpmsg_char_check_system(void)
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;
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)
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;
210 static int _rpmsg_char_find_ctrldev(struct rpmsg_char_endpt *ept,
211                                     char *dev_name, int remote_port)
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;
275 static int _rpmsg_char_create_eptdev(struct rpmsg_char_endpt *ept,
276                                      char *eptdev_name, int remote_port)
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;
307 static int _rpmsg_char_destroy_eptdev(int fd)
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;
322 static int _rpmsg_char_get_rpmsg_id(struct rpmsg_char_endpt *ept,
323                                     char *eptdev_name)
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;
371 static int _rpmsg_char_get_local_endpt(struct rpmsg_char_endpt *ept)
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);
392 static int _rpmsg_char_open_eptdev(struct rpmsg_char_endpt *ept,
393                                    char *eptdev_name, int flags)
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;
424 static struct rpmsg_char_endpt *_rpmsg_char_endpt_alloc(void)
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;
442 static void _rpmsg_char_endpt_free(struct rpmsg_char_endpt *ept)
444         if (ept->rpath)
445                 free(ept->rpath);
446         free(ept);
449 static void _rpmsg_char_cleanup(void)
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         }
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)
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;
527 int rpmsg_char_close(rpmsg_char_dev_t *rcdev)
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;
557 int rpmsg_char_init(char *soc_name)
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;
583 void rpmsg_char_exit(void)
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);