lib: Add initial library source code
authorSuman Anna <s-anna@ti.com>
Wed, 14 Oct 2020 16:02:03 +0000 (11:02 -0500)
committerSuman Anna <s-anna@ti.com>
Wed, 14 Oct 2020 19:49:45 +0000 (14:49 -0500)
Add the initial source code for the new ti-rpmsg-char library.
This library is a simple utility library designed to provide
ease of usage with the Linux kernel rpmsg-char driver to exchange
messages to a rpmsg device published by a remote processor.

The library provides the following 4 API and 2 headers for
applications:
API:
 1. rpmsg_char_init()
 2. rpmsg_char_exit()
 3. rpmsg_char_open()
 4. rpmsg_char_close()

Headers: ti_rpmsg_char.h and rproc_id.h

The rpmsg_char_init() and rpmsg_char_exit() functions are used
to initialize and finalize the library. The rpmsg_char_open()
is used to create and open a local rpmsg endpoint device. The
opened file handle can be used to read and write messages to
the corresponding rpmsg device. The rpmsg_char_close() is used
to close and destroy a previously created rpmsg endpoint device.

Signed-off-by: Suman Anna <s-anna@ti.com>
include/rproc_id.h [new file with mode: 0644]
include/ti_rpmsg_char.h [new file with mode: 0644]
src/rpmsg_char.c [new file with mode: 0644]
src/rpmsg_char_internal.h [new file with mode: 0644]
src/soc.c [new file with mode: 0644]
src/utils.c [new file with mode: 0644]

diff --git a/include/rproc_id.h b/include/rproc_id.h
new file mode 100644 (file)
index 0000000..7f55621
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __RPROC_ID_H__
+#define __RPROC_ID_H__
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*!
+ * @brief Enum values identifying a remote processor uniquely across
+ *        all SoCs
+ *
+ *        The enum defines a unique remoteproc id and is limited by
+ *        RPROC_ID_MAX value, and is agnostic of the SoC family.
+ *
+ *       NOTE:
+ *       1. These enum values are to be used as identifiers only.
+ *          Do not assume a specific value associated with a particular
+ *          value.
+ *       2. The RPROC_ID_MAX again is subject to change as new remoteprocs
+ *          are added.
+ *
+ *        Any new remote processor will need to have an id added to
+ *        this enum.
+ */
+enum rproc_id {
+       R5F_MCU0_0 = 0,
+       R5F_MCU0_1,
+       R5F_MAIN0_0,
+       R5F_MAIN0_1,
+       R5F_MAIN1_0,
+       R5F_MAIN1_1,
+       DSP_C66_0,
+       DSP_C66_1,
+       DSP_C71_0,
+       RPROC_ID_MAX,
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __RPROC_ID_H__ */
diff --git a/include/ti_rpmsg_char.h b/include/ti_rpmsg_char.h
new file mode 100644 (file)
index 0000000..401b3f5
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __TI_RPMSG_CHAR_H__
+#define __TI_RPMSG_CHAR_H__
+
+#include <rproc_id.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define RPMSG_DEV_NAME_MAXLEN  32
+
+/*!
+ * @brief Structure returned during rpmsg_char_open
+ *
+ * @param fd Opened file descriptor for the local rpmsg endpoint device
+ * @param endpt Local Endpoint address used for receiving messages
+ */
+struct rpmsg_char_dev {
+       int fd;
+       int endpt;
+};
+
+typedef struct rpmsg_char_dev rpmsg_char_dev_t;
+
+/*!
+ *  @brief      Function to create and access a rpmsg endpoint device for a
+ *              given rpmsg device
+ *
+ *              This function is to be used to open a communication channel
+ *              in a Linux application with any rpmsg device that is bound to
+ *              the Linux kernel rpmsg_chrdev driver (rpmsg_char.ko module).
+ *
+ *              The function returns a pointer to a rpmsg_char_dev structure
+ *              with the file descriptor and local end-point address filled in
+ *              and usable by an application for a given remoteproc, rpmsg
+ *              device (identified by published name from remote processor) and
+ *              the remote end-point address used by the rpmsg device.
+ *
+ *              The opened file descriptor can be used with regular file
+ *              operations such as select(), read() and write() functions to
+ *              wait, receive and send messages to the rpmsg device on the
+ *              remote processor. The messages are received on the local
+ *              end-point address.
+ *
+ *  @param      rproc_id remoteproc enum value identifying a remote processor.
+ *                       Value is a local id used by the library
+ *
+ *  @param      dev_name Name of the rpmsg device name to open. Passing NULL
+ *                       will use a default name "rpmsg_chrdev" for the rpmsg
+ *                       devices, and identified by remote_endpt. The maximum
+ *                       length for the rpmsg device name is as per the virtio
+ *                       rpmsg transport, RPMSG_DEV_NAME_MAXLEN (32 bytes)
+ *
+ *  @param      remote_endpt rpmsg remote end-point address for the rpmsg device
+ *                           to open. Valid value is mandatory
+ *
+ *  @param      eptdev_name Name used to create and identify the local endpoint
+ *                          device. Needs to be unique across all the Linux
+ *                          applications. The maximum length for the
+ *                          eptdev_name is RPMSG_DEV_NAME_MAXLEN (32 bytes)
+ *
+ *  @param      flags Flags used to open the local end-point device
+ *
+ *  @ret        A valid pointer to a rpmsg_char_dev_t on success, NULL on
+ *              failures
+ *
+ *  @sa         rpmsg_char_close
+ */
+rpmsg_char_dev_t *rpmsg_char_open(enum rproc_id id, char *dev_name,
+                                 int remote_endpt, char *eptdev_name,
+                                 int flags);
+
+/*!
+ *  @brief      Function to close and delete a previously created local endpoint
+ *              device with rpmsg_char_open
+ *
+ *              Once this function is called, any regular file operations on the
+ *              underlying fd cannot be used. The pointer value is not modified
+ *              within the function, but is no longer valid after a successful
+ *              return.
+ *
+ *  @param      rcdev  Pointer to the previously created rpmsg_char_dev_t. The
+ *                     handle will be unusable with any of the regular file
+ *                     operation functions after a success
+ *
+ *  @ret        0 on success, a negative value on failure
+ *
+ *  @sa         rpmsg_char_open
+ */
+int rpmsg_char_close(rpmsg_char_dev_t *rcdev);
+
+/*!
+ *  @brief      Function to initialize the library
+ *
+ *              This function initializes the library after performing SoC
+ *              detection and corresponding initialization. This needs to be
+ *              invoked before being able to use any other function. This only
+ *              needs to be invoked once in an application, there is no reference
+ *              counting.
+ *
+ *  @param      soc_name  Name of the SoC family to be used for manual detection
+ *                        if kernel doesn't support a socbus device for a
+ *                        particular SoC and auto detection fails. Preferred
+ *                        usage is just to pass NULL
+ *
+ *  @ret        0 on success, 1 if already invoked previously, and a negative
+ *              value on failure
+ *
+ *  @sa         rpmsg_char_exit
+ */
+int rpmsg_char_init(char *soc_name);
+
+/*!
+ *  @brief      Function to finalize the library
+ *
+ *              This function finalizes and performs all the de-initialization
+ *              and any cleanup on the library. This is the last function that
+ *              needs to be invoked after all usage is done as part of the
+ *              application's cleanup. This only need to be invoked once in an
+ *              application, there is no reference counting.
+ *
+ *  @ret        None
+ *
+ *  @sa         rpmsg_char_init
+ */
+void rpmsg_char_exit(void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __TI_RPMSG_CHAR_H__ */
diff --git a/src/rpmsg_char.c b/src/rpmsg_char.c
new file mode 100644 (file)
index 0000000..b038196
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/rpmsg.h>
+
+#define RPMSG_ADDR_ANY         0xFFFFFFFF
+
+#include "ti_rpmsg_char.h"
+#include "rpmsg_char_internal.h"
+
+#define printerr printf
+
+#define to_rpmsg_char_endpt(ptr) ({                            \
+               const rpmsg_char_dev_t *_mptr = (ptr);          \
+               (struct rpmsg_char_endpt *)((char *)_mptr -     \
+                       offsetof(struct rpmsg_char_endpt, rcdev)); })
+
+struct rpmsg_char_endpt {
+       struct rpmsg_char_endpt *next;
+       const struct rproc_map *map;
+       char *rpath;
+       struct rpmsg_char_dev rcdev;
+       char rpmsg_dev_name[64];
+       int remoteproc_id;
+       int virtio_id;
+       int ctrl_id;
+       int rpmsg_id;
+};
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static bool inited = false;
+static int soc_id = -1;
+static struct soc_rprocs soc_data;
+static struct rpmsg_char_endpt *ghead = NULL;
+static struct rpmsg_char_endpt *gtail = NULL;
+
+static void _list_add(struct rpmsg_char_endpt *ept)
+{
+       if (!ghead)
+               ghead = ept;
+
+       if (gtail)
+               gtail->next = ept;
+       gtail = ept;
+}
+
+static void _list_remove(struct rpmsg_char_endpt *ept)
+{
+       struct rpmsg_char_endpt *iter, *prev;
+
+       if (ghead == ept) {
+               if (gtail == ept)
+                       gtail = NULL;
+               ghead = ept->next;
+               return;
+       }
+
+       prev = ghead;
+       for (iter = ghead->next; iter != NULL; prev = iter, iter = iter->next) {
+               if (iter == ept) {
+                       if (prev)
+                               prev->next = ept->next;
+                       if (gtail == ept)
+                               gtail = prev;
+                       return;
+               }
+       }
+}
+
+static bool _list_is_present(struct rpmsg_char_endpt *ept)
+{
+       struct rpmsg_char_endpt *iter;
+
+       for (iter = ghead; iter != NULL; iter = iter->next) {
+               if (iter == ept)
+                       return true;
+       }
+
+       return false;
+}
+
+static int _rpmsg_char_check_system(void)
+{
+       if (check_dir("/sys/class/remoteproc")) {
+               fprintf(stderr, "remoteproc framework is not enabled/installed\n");
+               return -ENOENT;
+       }
+
+       if (check_dir("/sys/bus/virtio")) {
+               fprintf(stderr, "virtio framework is not enabled/installed\n");
+               return -ENOENT;
+       }
+
+       if (check_dir("/sys/bus/rpmsg")) {
+               fprintf(stderr, "rpmsg bus driver is not enabled/installed\n");
+               return -ENOENT;
+       }
+
+       if (check_dir("/sys/class/rpmsg")) {
+               fprintf(stderr, "rpmsg_chrdev driver is not enabled/installed\n");
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+/* parse sysfs to find associated kernel-allocated remoteproc and virtio ids */
+static int _rpmsg_char_find_rproc(struct rpmsg_char_endpt *ept,
+                                 enum rproc_id id)
+{
+       const struct rproc_map *r = soc_data.map;
+       unsigned int remoteproc_id;
+       unsigned int virtio_id;
+       char fpath[512];
+       int i;
+       int ret = -ENODEV;
+
+       for (i = 0; i < soc_data.num_rprocs; i++, r++) {
+               if (id == r->id)
+                       break;
+       }
+       if (i == soc_data.num_rprocs) {
+               fprintf(stderr, "%s: SoC doesn't have rproc id %d\n",
+                       __func__, id);
+               return -EINVAL;
+       }
+
+       ept->rpath = file_deref_link("/sys/bus/platform/devices", r->rproc_name);
+       if (check_dir(ept->rpath)) {
+               fprintf(stderr, "%s: %s device is mostly yet to be created!\n",
+                       __func__, r->rproc_name);
+               return -ENOENT;
+       }
+
+       /* retrieve the dynamically created kernel remoteproc id */
+       memset(&fpath, 0, sizeof(fpath));
+       sprintf(fpath, "%s/remoteproc", ept->rpath);
+       ret = get_child_dir_suffix(fpath, "remoteproc%u", &remoteproc_id);
+       if (ret) {
+               if (ret == -ENOENT) {
+                       fprintf(stderr, "%s: %s is either not probed or not a remoteproc!\n",
+                               __func__, r->rproc_name);
+               }
+               return ret;
+       }
+
+       /* retrieve the dynamically created kernel virtio id */
+       memset(&fpath, 0, sizeof(fpath));
+       sprintf(fpath, "%s/remoteproc/remoteproc%u/remoteproc%u#vdev0buffer",
+               ept->rpath, remoteproc_id, remoteproc_id);
+       ret = get_child_dir_suffix(fpath, "virtio%u", &virtio_id);
+       if (ret) {
+               if (ret == -ENOENT) {
+                       fprintf(stderr, "%s: %s does not have any virtio devices!\n",
+                               __func__, r->rproc_name);
+               }
+               return ret;
+       }
+
+       ept->map = r;
+       ept->remoteproc_id = remoteproc_id;
+       ept->virtio_id = virtio_id;
+
+       return 0;
+}
+
+static int _rpmsg_char_find_ctrldev(struct rpmsg_char_endpt *ept,
+                                   char *dev_name, int remote_port)
+{
+       char virtio[16] = { 0 };
+       struct dirent *iter;
+       DIR *dir;
+       char fpath[512];
+       char *rpath;
+       unsigned int ctrl_id;
+       int ret;
+
+       sprintf(virtio, "virtio%u", ept->virtio_id);
+       rpath = file_deref_link("/sys/bus/virtio/devices", virtio);
+       if (!rpath)
+               return -ENOENT;
+
+       sprintf(ept->rpmsg_dev_name, "virtio%u.%s.-1.%d", ept->virtio_id,
+               dev_name, remote_port);
+
+       dir = opendir(rpath);
+       if (!dir) {
+               ret = -errno;
+               goto free_rpath;
+       }
+
+       /*
+        * find a directory that matches the published rpmsg device of the
+        * form virtio%d.<rpmsg_dev.%d.%d
+        */
+       while ((iter = readdir(dir))) {
+               if (iter->d_type == DT_DIR) {
+                       if (!strcmp(iter->d_name, ept->rpmsg_dev_name))
+                               break;
+               }
+       }
+       if (!iter) {
+               fprintf(stderr, "%s: could not find the matching rpmsg_ctrl device for %s\n",
+                       __func__, ept->rpmsg_dev_name);
+               ret = -ENOENT;
+               goto chrdev_nodir;
+       }
+
+       memset(&fpath, 0, sizeof(fpath));
+       sprintf(fpath, "%s/%s/rpmsg", rpath, iter->d_name);
+       if (check_dir(fpath)) {
+               fprintf(stderr, "%s: rpmsg directory doesn't exist under %s\n",
+                       __func__, ept->rpmsg_dev_name);
+               ret = -ENOENT;
+               goto chrdev_nodir;
+       }
+
+       /* get rpmsg_ctrl id */
+       ret = get_child_dir_suffix(fpath, "rpmsg_ctrl%u", &ctrl_id);
+       if (ret)
+               goto chrdev_nodir;
+
+       ept->ctrl_id = ctrl_id;
+
+chrdev_nodir:
+       closedir(dir);
+free_rpath:
+       free(rpath);
+       return ret;
+}
+
+static int _rpmsg_char_create_eptdev(struct rpmsg_char_endpt *ept,
+                                    char *eptdev_name, int remote_port)
+{
+       int fd, ret;
+       char ctrldev_path[32] = { 0 };
+       struct rpmsg_endpoint_info ept_info = { 0 };
+
+       sprintf(ctrldev_path, "/dev/rpmsg_ctrl%u", ept->ctrl_id);
+
+       fd = open(ctrldev_path, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "%s: could not open rpmsg_ctrl dev node for id %d\n",
+                       __func__, ept->ctrl_id);
+               return fd;
+       }
+
+       /* let kernel dynamically allocate the local end-point */
+       ept_info.src = RPMSG_ADDR_ANY;
+       ept_info.dst = remote_port;
+       sprintf(ept_info.name, "%s", eptdev_name);
+
+       ret = ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);
+       if (ret) {
+               fprintf(stderr, "%s: ctrl_fd ioctl: %s\n", __func__,
+                       strerror(errno));
+       }
+
+       close(fd);
+
+       return ret;
+}
+
+static int _rpmsg_char_destroy_eptdev(int fd)
+{
+       int ret;
+
+       ret = ioctl(fd, RPMSG_DESTROY_EPT_IOCTL, NULL);
+       if (ret) {
+               fprintf(stderr, "%s: could not destroy endpt %d\n",
+                       __func__, fd);
+               return ret;
+       }
+
+       close(fd);
+       return 0;
+}
+
+static int _rpmsg_char_get_rpmsg_id(struct rpmsg_char_endpt *ept,
+                                   char *eptdev_name)
+{
+       const struct rproc_map *r = ept->map;
+       bool found = false;
+       char rpmsg[16] = { 0 };
+       char fpath[512];
+       char dev_name[32];
+       char *rpath;
+       struct dirent *iter;
+       DIR *dir;
+       int ret;
+
+       sprintf(rpmsg, "rpmsg_ctrl%u", ept->ctrl_id);
+       rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
+       if (!rpath)
+               return -ENOENT;
+
+       dir = opendir(rpath);
+       if (!dir) {
+               free(rpath);
+               return -errno;
+       }
+
+       while ((iter = readdir(dir))) {
+               if (iter->d_type != DT_DIR ||
+                   strncmp(iter->d_name, "rpmsg", strlen("rpmsg")))
+                       continue;
+
+               memset(&fpath, 0, sizeof(fpath));
+               memset(&dev_name, 0, sizeof(dev_name));
+               sprintf(fpath, "%s/%s/name", rpath, iter->d_name);
+               ret = file_read_string(fpath, dev_name, sizeof(dev_name));
+               if (ret < 0)
+                       continue;
+
+               if (strcmp(dev_name, eptdev_name))
+                       continue;
+
+               sscanf(iter->d_name, "rpmsg%u", &ept->rpmsg_id);
+               found = true;
+               break;
+       }
+
+       free(rpath);
+       closedir(dir);
+       return found ? 0 : -1;
+}
+
+static int _rpmsg_char_get_local_endpt(struct rpmsg_char_endpt *ept)
+{
+       const struct rproc_map *r = ept->map;
+       char fpath[512] = { 0 };
+       char rpmsg[16] = { 0 };
+       char *rpath;
+
+       sprintf(rpmsg, "rpmsg%u", ept->rpmsg_id);
+       rpath = file_deref_link("/sys/class/rpmsg", rpmsg);
+       if (!rpath) {
+               fprintf(stderr, "%s: rpmsg%u realpath failed\n",
+                       __func__, ept->rpmsg_id);
+               return -ENOENT;
+       }
+
+       sprintf(fpath, "%s/src", rpath);
+       free(rpath);
+
+       return file_read_value(fpath);
+}
+
+static int _rpmsg_char_open_eptdev(struct rpmsg_char_endpt *ept,
+                                  char *eptdev_name, int flags)
+{
+       char eptdev_path[32] = { 0 };
+       int efd, endpt;
+       int ret;
+
+       ret = _rpmsg_char_get_rpmsg_id(ept, eptdev_name);
+       if (ret < 0)
+               return ret;
+
+       sprintf(eptdev_path, "/dev/rpmsg%u", ept->rpmsg_id);
+
+       efd = open(eptdev_path, O_RDWR | flags);
+       if (efd < 0) {
+               fprintf(stderr, "failed to open eptdev %s\n", eptdev_path);
+               return -errno;
+       }
+
+       /* local end-point is assigned after the open call */
+       endpt = _rpmsg_char_get_local_endpt(ept);
+       if (endpt < 0) {
+               _rpmsg_char_destroy_eptdev(efd);
+               return endpt;
+       }
+
+       ept->rcdev.fd = efd;
+       ept->rcdev.endpt = endpt;
+
+       return 0;
+}
+
+static struct rpmsg_char_endpt *_rpmsg_char_endpt_alloc(void)
+{
+       struct rpmsg_char_endpt *ept;
+
+       ept = calloc(sizeof(*ept), 1);
+       if (!ept) {
+               fprintf(stderr, "%s: failed to allocate memory\n", __func__);
+               return NULL;
+       }
+
+       ept->remoteproc_id = -1;
+       ept->virtio_id = -1;
+       ept->ctrl_id = -1;
+       ept->rpmsg_id = -1;
+
+       return ept;
+}
+
+static void _rpmsg_char_endpt_free(struct rpmsg_char_endpt *ept)
+{
+       if (ept->rpath)
+               free(ept->rpath);
+       free(ept);
+}
+
+static void _rpmsg_char_cleanup(void)
+{
+       struct rpmsg_char_endpt *iter;
+       int ret;
+
+       if (ghead)
+               fprintf(stderr, "Application did not close some rpmsg_char devices\n");
+
+       for (iter = ghead; iter != NULL; iter = iter->next) {
+               ret = rpmsg_char_close(&iter->rcdev);
+               if (ret) {
+                       fprintf(stderr, "rpmsg_char_close failed during cleanup, rcdev = 0x%x, ret = %d\n",
+                               &iter->rcdev, ret);
+               }
+       }
+}
+
+rpmsg_char_dev_t *rpmsg_char_open(enum rproc_id id, char *dev_name,
+                                 int remote_endpt, char *eptdev_name,
+                                 int flags)
+{
+       struct rpmsg_char_endpt *ept = NULL;
+       char *def_dev_name = "rpmsg_chrdev";
+       int ret, local_endpt;
+
+       pthread_mutex_lock(&mutex);
+
+       if (!inited) {
+               fprintf(stderr, "%s cannot be invoked without initialization\n",
+                       __func__);
+               goto unlock;
+       }
+
+       if (remote_endpt == RPMSG_ADDR_ANY) {
+               fprintf(stderr, "%s: remote_endpt cannot be RPMSG_ADDR_ANY\n",
+                       __func__);
+               goto unlock;
+       }
+
+       if (!eptdev_name || strlen(eptdev_name) >= 32) {
+               fprintf(stderr, "%s: invalid eptdev_name\n", __func__);
+               goto unlock;
+       }
+
+       if (!dev_name)
+               dev_name = def_dev_name;
+
+       ept = _rpmsg_char_endpt_alloc();
+       if (!ept)
+               goto unlock;
+
+       ret = _rpmsg_char_find_rproc(ept, id);
+       if (ret)
+               goto out;
+
+       ret = _rpmsg_char_find_ctrldev(ept, dev_name, remote_endpt);
+       if (ret)
+               goto out;
+
+       ret = _rpmsg_char_create_eptdev(ept, eptdev_name, remote_endpt);
+       if (ret)
+               goto out;
+
+       ret = _rpmsg_char_open_eptdev(ept, eptdev_name, flags);
+       if (ret < 0)
+               goto out;
+
+       _list_add(ept);
+       goto unlock;
+
+out:
+       _rpmsg_char_endpt_free(ept);
+       ept = NULL;
+unlock:
+       pthread_mutex_unlock(&mutex);
+       return ept ? &ept->rcdev : NULL;
+}
+
+int rpmsg_char_close(rpmsg_char_dev_t *rcdev)
+{
+       int ret;
+       struct rpmsg_char_endpt *ept;
+
+       if (!rcdev)
+               return -EINVAL;
+
+       pthread_mutex_lock(&mutex);
+
+       ept = to_rpmsg_char_endpt(rcdev);
+       if (!_list_is_present(ept)) {
+               fprintf(stderr, "%s: invalid handle passed in rcdev = 0x%x\n",
+                       __func__, rcdev);
+               ret = -ENOENT;
+               goto out;
+       }
+
+       ret = _rpmsg_char_destroy_eptdev(rcdev->fd);
+       if (ret)
+               goto out;
+
+       _list_remove(ept);
+       _rpmsg_char_endpt_free(ept);
+
+out:
+       pthread_mutex_unlock(&mutex);
+       return ret;
+}
+
+int rpmsg_char_init(char *soc_name)
+{
+       int ret = 0;
+
+       pthread_mutex_lock(&mutex);
+
+       if (inited) {
+               ret = 1;
+               goto out;
+       }
+
+       ret = _rpmsg_char_check_system();
+       if (ret < 0)
+               goto out;
+
+       ret = _rpmsg_char_find_soc_family(soc_name, &soc_data);
+       if (ret < 0)
+               goto out;
+
+       inited = true;
+
+out:
+       pthread_mutex_unlock(&mutex);
+       return ret;
+}
+
+void rpmsg_char_exit(void)
+{
+       pthread_mutex_lock(&mutex);
+       if (!inited) {
+               fprintf(stderr, "%s: library not initialized previously\n",
+                       __func__);
+               pthread_mutex_unlock(&mutex);
+               return;
+       }
+       pthread_mutex_unlock(&mutex);
+
+       _rpmsg_char_cleanup();
+
+       pthread_mutex_lock(&mutex);
+       inited = false;
+       pthread_mutex_unlock(&mutex);
+}
diff --git a/src/rpmsg_char_internal.h b/src/rpmsg_char_internal.h
new file mode 100644 (file)
index 0000000..10a09c1
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __RPMSG_CHAR_INTERNAL_H__
+#define __RPMSG_CHAR_INTERNAL_H__
+
+#include <rproc_id.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct rproc_map {
+       enum rproc_id id;
+       char *rproc_name;
+};
+
+struct soc_rprocs {
+       int num_rprocs;
+       const struct rproc_map *map;
+};
+
+/* soc.c */
+int _rpmsg_char_find_soc_family(const char *soc_name, struct soc_rprocs *soc);
+
+/* utils.c */
+char *find_child_dir_by_name(DIR *parent, char *child_name);
+int get_child_dir_suffix(char *fpath, const char *child_name_pattern,
+                        unsigned int *suffix);
+char *str_join(const char *fmt, ...);
+int file_read_string(char *fpath, char *buf, int size);
+int file_read_value(char *fpath);
+int check_dir(char *dirpath);
+char *file_deref_link(char *fpath, char *link_name);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/soc.c b/src/soc.c
new file mode 100644 (file)
index 0000000..298e253
--- /dev/null
+++ b/src/soc.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "rpmsg_char_internal.h"
+
+/* Increment this whenever new SoC data is added */
+#define NUM_SOC_FAMILY 3
+
+struct soc_data {
+       const char *family_name;
+       const struct rproc_map *map;
+       int num_rprocs;
+};
+
+/* TI K3 AM65x SoCs */
+const struct rproc_map am65x_map[] = {
+       { .id = R5F_MCU0_0, .rproc_name = "41000000.r5f", },
+       { .id = R5F_MCU0_1, .rproc_name = "41400000.r5f", },
+};
+
+/* TI K3 J721E SoCs */
+const struct rproc_map j721e_map[] = {
+       { .id = R5F_MCU0_0,  .rproc_name = "41000000.r5f",   },
+       { .id = R5F_MCU0_1,  .rproc_name = "41400000.r5f",   },
+       { .id = R5F_MAIN0_0, .rproc_name = "5c00000.r5f",    },
+       { .id = R5F_MAIN0_1, .rproc_name = "5d00000.r5f",    },
+       { .id = R5F_MAIN1_0, .rproc_name = "5e00000.r5f",    },
+       { .id = R5F_MAIN1_1, .rproc_name = "5f00000.r5f",    },
+       { .id = DSP_C66_0,   .rproc_name = "4d80800000.dsp", },
+       { .id = DSP_C66_1,   .rproc_name = "4d81800000.dsp", },
+       { .id = DSP_C71_0,   .rproc_name = "64800000.dsp",   },
+};
+
+/* TI K3 J7200 SoCs */
+const struct rproc_map j7200_map[] = {
+       { .id = R5F_MCU0_0,  .rproc_name = "41000000.r5f", },
+       { .id = R5F_MCU0_1,  .rproc_name = "41400000.r5f", },
+       { .id = R5F_MAIN0_0, .rproc_name = "5c00000.r5f",  },
+       { .id = R5F_MAIN0_1, .rproc_name = "5d00000.r5f",  },
+};
+
+const struct soc_data socs[NUM_SOC_FAMILY] = {
+       {
+               .family_name = "AM65X",
+               .map = am65x_map,
+               .num_rprocs = (sizeof(am65x_map) / sizeof(struct rproc_map)),
+       },
+       {
+               .family_name = "J721E",
+               .map = j721e_map,
+               .num_rprocs = (sizeof(j721e_map) / sizeof(struct rproc_map)),
+       },
+       {
+               .family_name = "J7200",
+               .map = j7200_map,
+               .num_rprocs = (sizeof(j7200_map) / sizeof(struct rproc_map)),
+       },
+};
+
+int _rpmsg_char_find_soc_family(const char *name, struct soc_rprocs *soc)
+{
+       int ret, i;
+       char family_name[32];
+       bool found = false;
+
+       if (check_dir("/sys/devices/soc0")) {
+               fprintf(stderr, "Kernel doesn't have a soc device, use fallback detection..\n");
+               goto fallback;
+       }
+
+       ret = file_read_string("/sys/devices/soc0/family", family_name,
+                               sizeof(family_name));
+       if (ret < 0)
+               return -ENOENT;
+
+       for (i = 0; i < NUM_SOC_FAMILY; i++) {
+               if (!strcmp(family_name, socs[i].family_name)) {
+                       found = true;
+                       break;
+               }
+       }
+
+fallback:
+       /* fall back to using passed in SoC family name */
+       if (!found && name) {
+               for (i = 0; i < NUM_SOC_FAMILY; i++) {
+                       if (!strcmp(name, socs[i].family_name)) {
+                               found = true;
+                               break;
+                       }
+               }
+       }
+
+       if (!found) {
+               fprintf(stderr, "%s: SoC device family match failed\n", __func__);
+               return -ENOENT;
+       }
+
+       soc->num_rprocs = socs[i].num_rprocs;
+       soc->map = socs[i].map;
+
+       return 0;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644 (file)
index 0000000..d506939
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2020 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+int get_child_dir_suffix(char *fpath, const char *child_name_pattern,
+                        unsigned int *suffix)
+{
+       struct dirent *iter;
+       DIR *parent;
+       int ret = -ENODEV;
+
+       parent = opendir(fpath);
+       if (!parent)
+               return -errno;
+
+       while ((iter = readdir(parent))) {
+               if (iter->d_type == DT_DIR &&
+                   sscanf(iter->d_name, child_name_pattern, suffix)) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       closedir(parent);
+       return ret;
+}
+
+int file_read_string(char *fpath, char *buf, int size)
+{
+       int fd, bytes;
+
+       fd = open(fpath, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "could not open %s: errno = %d\n",
+                       fpath, errno);
+               return -errno;
+       }
+
+       bytes = read(fd, buf, size);
+       close(fd);
+       if (bytes <= 0) {
+               fprintf(stderr, "could not read %s: errno = %d\n",
+                       fpath, errno);
+               return -EIO;
+       }
+       if (bytes >= size) {
+               fprintf(stderr, "%d bytes read from %s are larger than size %d\n",
+                       bytes, fpath, size);
+               return -EIO;
+       }
+
+       /* suppress the newline */
+       buf[bytes - 1] = '\0';
+
+       return bytes;
+}
+
+int file_read_value(char *fpath)
+{
+       char buf[32];
+       int ret;
+
+       ret = file_read_string(fpath, buf, sizeof(buf));
+       if (ret < 0)
+               return ret;
+
+       return strtol(buf, NULL, 0);
+}
+
+int check_dir(char *dirpath)
+{
+       struct stat s;
+
+       if (stat(dirpath, &s) == 0 && S_ISDIR(s.st_mode))
+               return 0;
+
+       return -ENOENT;
+}
+
+/* Returns a pointer to allocated memory, needs to be freed once done */
+char *file_deref_link(char *fpath, char *link_name)
+{
+       char path[512] = { 0 };
+       char rel_path[256] = { 0 };
+       int n, nr;
+
+       n = snprintf(path, 256, "%s/%s", fpath, link_name);
+       if (n < 0 || n >= 256) {
+               fprintf(stderr, "%s: could not create full path string\n",
+                       __func__);
+               return NULL;
+       }
+
+       nr = readlink(path, rel_path, sizeof(rel_path));
+       if (nr < 0) {
+               fprintf(stderr, "%s: readlink failed for %s\n", __func__, path);
+               return NULL;
+       }
+
+       if (n + nr >= 512) {
+               fprintf(stderr, "%s: full relative path exceeds buffer size, n = %d nr = %d\n",
+                       __func__, n, nr);
+               return NULL;
+       }
+
+       memset(path, 0, sizeof(path));
+       sprintf(path, "%s/%s", fpath, rel_path);
+
+       return realpath(path, NULL);
+}