RPMSG character driver on Apalis IMX8QM


I have create a communication protocol between core A53 and M40 on apalis IMX8 (using rpmsg character driver and using linux toradex 5.7.2). I created the endpoints successfully like the picture, but when I send message from A53, M4 cannot receive this message.
Here are my code on A53 and M4:

A53:

void IPCStateMachine::run(QQueue<IPCEventData> &eventQueue, QMutex &mutex)
{
    msg_data_t data_buf = {
        "Hello World!"
    };

    fprintf(stderr, "==========================\n");
    fprintf(stderr, "* RPMsg Hello World demo *\n");
    fprintf(stderr, "==========================\n");

    fprintf(stderr, "Creating endpoint...\n");
    struct rpmsg_endpoint_info ept_info = {"rpmsg-openamp-demo-channel", 0x2, 0x1e};
    int fd = open("/dev/rpmsg_ctrl0", O_RDWR);
    fprintf(stderr, "File descriptor created '%d'\n", fd);
    fprintf(stderr, "Channel info: '%s' [ '%x' --> '%x' ]\n", ept_info.name, ept_info.src, ept_info.dst);

    /* create endpoint interface */
    int ret = ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);  // /dev/rpmsg0 is created
    fprintf(stderr, "Endpoint created\n");

    if(ret < 0) qDebug() << "Failed to create ept!!!";
    else qDebug() << "Success to create ept!!!";
    /* create endpoint */
    int fd_ept = open("/dev/rpmsg0", O_RDWR); // backend creates endpoint

    if(fd_ept < 0)
    {
        qDebug() << "End point cannot created!";
    }

    fprintf(stderr, "Endpoint file descriptor created '%d'\n", fd_ept);

    /* send data to remote device */
    fprintf(stderr, "Sending...\n");
    write(fd_ept, &data_buf, sizeof(data_buf));

    /* receive data from remote device */
//     fprintf(stderr, "Receiving...\n");
//     read(fd_ept, &data_buf, sizeof(data_buf));

    /* destroy endpoint */
    fprintf(stderr, "Destroying endpoint...\n");
    ioctl(fd_ept, RPMSG_DESTROY_EPT_IOCTL);
    fprintf(stderr, "Endpoint destroyed\n");

    close(fd_ept);
    close(fd);

}

M4:

/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rpmsg_lite.h"
#include "rpmsg_queue.h"
#include "rpmsg_ns.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_debug_console.h"
#include "FreeRTOS.h"
#include "task.h"

#include "fsl_lpuart.h"
#include "fsl_irqsteer.h"
#include "rsc_table.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define RPMSG_LITE_SHMEM_BASE         (VDEV1_VRING_BASE)
#define RPMSG_LITE_LINK_ID            (RL_PLATFORM_IMX8QM_M4_A_USER_LINK_ID)
#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-openamp-demo-channel"
#define APP_TASK_STACK_SIZE (256)
#ifndef LOCAL_EPT_ADDR
#define LOCAL_EPT_ADDR (30)
#endif

/* Globals */
static char app_buf[512]; /* Each RPMSG buffer can carry less than 512 payload */

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/
static TaskHandle_t app_task_handle = NULL;

static void ipcIf_nameservice_isr_cb(uint32_t new_ept, const char *new_ept_name, uint32_t flags, void *user_data)
{
}

static char helloMsg[13];

static void app_task(void *param)
{
    volatile uint32_t remote_addr;
    struct rpmsg_lite_endpoint *volatile my_ept;
    volatile rpmsg_queue_handle my_queue;
    struct rpmsg_lite_instance *volatile my_rpmsg;
    void *rx_buf;
    uint32_t len;
    int32_t result;
    void *tx_buf;
    uint32_t size;

    /* Print the initial banner */
    PRINTF("\r\n...RPMSG String Echo FreeRTOS RTOS API Demo...v227\r\n");

#ifdef MCMGR_USED
    uint32_t startupData;

    /* Get the startup data */
    (void)MCMGR_GetStartupData(kMCMGR_Core1, &startupData);

    my_rpmsg = rpmsg_lite_remote_init((void *)startupData, RPMSG_LITE_LINK_ID, RL_NO_FLAGS);

    /* Signal the other core we are ready */
    (void)MCMGR_SignalReady(kMCMGR_Core1);
#else
    my_rpmsg = rpmsg_lite_remote_init((void *)RPMSG_LITE_SHMEM_BASE, RPMSG_LITE_LINK_ID, RL_NO_FLAGS);
#endif /* MCMGR_USED */

    while (0 == rpmsg_lite_is_link_up(my_rpmsg))
        ;

    my_queue = rpmsg_queue_create(my_rpmsg);
    my_ept   = rpmsg_lite_create_ept(my_rpmsg, LOCAL_EPT_ADDR, rpmsg_queue_rx_cb, my_queue);
    rpmsg_ns_bind(my_rpmsg, ipcIf_nameservice_isr_cb, NULL);
    (void)rpmsg_ns_announce(my_rpmsg, my_ept, RPMSG_LITE_NS_ANNOUNCE_STRING, RL_NS_CREATE);

    PRINTF("\r\nNameservice sent, ready for incoming messages, wait wait...\r\n");

    for (;;)
    {
        // (void)rpmsg_ns_announce(my_rpmsg, my_ept, "Waiting for new data...", RL_NS_CREATE);
        PRINTF("\nM4 Running.....");
        /* Get RPMsg rx buffer with message */
        /*result =*/ (void)rpmsg_queue_recv(my_rpmsg, my_queue, (uint32_t )&remote_addr, helloMsg, sizeof(helloMsg), ((void *)0),
                           RL_BLOCK + 2000);
            // rpmsg_queue_recv_nocopy(my_rpmsg, my_queue, (uint32_t *)&remote_addr, (char **)&rx_buf, &len, RL_BLOCK + 2000);

            PRINTF("\nRemote addr: %x", remote_addr);

        if (result != 0)
        {
            // assert(false);
            PRINTF("\nresult != 0");
            continue;
        }

        PRINTF("\nM4 Running...1");

        /* Copy string from RPMsg rx buffer */
        // assert(len < sizeof(app_buf));
        memcpy(app_buf, rx_buf, len);
        app_buf[len] = 0; /* End string by '\0' */

        if ((len == 2) && (app_buf[0] == 0xd) && (app_buf[1] == 0xa))
            PRINTF("Get New Line From Master Side\r\n");
        else
            PRINTF("Get Message From Master Side : \"%s\" [len : %d]\r\n", app_buf, len);

        /* Get tx buffer from RPMsg */
        tx_buf = rpmsg_lite_alloc_tx_buffer(my_rpmsg, &size, RL_BLOCK);
        // assert(tx_buf);
        /* Copy string to RPMsg tx buffer */
        memcpy(tx_buf, app_buf, len);

        PRINTF("\nM4 Running...2");
        /* Echo back received message with nocopy send */
        result = rpmsg_lite_send_nocopy(my_rpmsg, my_ept, remote_addr, tx_buf, len);
        if (result != 0)
        {
            // assert(false);
            PRINTF("\nresult != 0 .. 2");
            continue;
        }

        PRINTF("\nM4 Running...3");
        /* Release held RPMsg rx buffer */
        result = rpmsg_queue_nocopy_free(my_rpmsg, rx_buf);
        if (result != 0)
        {
            // assert(false);
            PRINTF("\nresult != 0 .. 3");
            continue;
        }
    }
}

/*!
 * @brief Main function
 */
int main(void)
{
    /* Initialize standard SDK demo application pins */
    sc_ipc_t ipc;
    ipc = BOARD_InitRpc();

    BOARD_InitPins(ipc);
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    BOARD_InitMemory();

    /* Power up the MU used for RPMSG */
    if (sc_pm_set_resource_power_mode(ipc, SC_R_MU_5B, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
    {
        PRINTF("Error: Failed to power on MU!\r\n");
    }
    if (sc_pm_set_resource_power_mode(ipc, SC_R_MU_7A, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
    {
        PRINTF("Error: Failed to power on MU!\r\n");
    }

    if (sc_pm_set_resource_power_mode(ipc, SC_R_IRQSTR_M4_0, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
    {
        PRINTF("Error: Failed to power on IRQSTEER!\r\n");
    }

    IRQSTEER_Init(IRQSTEER);
    copyResourceTable();

#ifdef MCMGR_USED
    /* Initialize MCMGR before calling its API */
    (void)MCMGR_Init();
#endif /* MCMGR_USED */

    if (xTaskCreate(app_task, "APP_TASK", APP_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &app_task_handle) != pdPASS)
    {
        PRINTF("\r\nFailed to create application task\r\n");
        for (;;)
            ;
    }

    vTaskStartScheduler();

    PRINTF("Failed to start FreeRTOS on core0.\n");
    for (;;)
        ;
}

Please help me this problem

Hi @victorduong , was the following message printed on M4 debug console? Or was it only the message sent from your Qt application that M4 couldn’t read?

PRINTF("\r\n...RPMSG String Echo FreeRTOS RTOS API Demo...v227\r\n");

Will sending a message by command work? For example,

echo "this is a test from Linux" > /dev/ttyRPMSG30

This message was printed on M4 debug console. But M4 did not receive any message (“Hello world!”) from A53.

PRINTF("\r\n...RPMSG String Echo FreeRTOS RTOS API Demo...v227\r\n");

I tried to config rpmsg using character driver on A53 (not rpmsg with tty driver), so I cannot not do this way

echo "this is a test from Linux" > /dev/ttyRPMSG30

In addition, when I config A53 using rpmsg using tty driver, it worked correctly. But when I change to character driver, it did not work.

Hi @victorduong , I can reproduce this problem here. Please allow me to investigate.

Hi @victorduong , I have not brought rpmsg_char up on Apalis iMX8 even with kernel v5.15. Does the rpmsg_tty work for you? This driver is working out-of-the-box.

Hi,

I have had similar experiences with the RPMSG Character device drivers.
In my case I found that the corresponding devices ‘/dev/rpmsg_ctrl0’ and ‘/dev/rpmsg0’ could only be used by root.
A udev_rule was missing or you have to set the right permissions manually.
You can also use a udev rule to link a small program that creates the respective device (rpmsg0). Otherwise it will be difficult to access it from a Docker container once you create it from there.
Unfortunately, I haven’t progressed that far yet. But maybe it will help you.

Hi @victorduong , We found the reason the char driver uses virtio0 when communicating with rpmsg_string_echo demo on M4 core. In your code, int fd = open("/dev/rpmsg_ctrl0", O_RDWR); will be changed to int fd = open("/dev/rpmsg_ctrl1", O_RDWR); to make char driver work.
Here is another demo we tested.
rpmsg_char_linux_demo.c (2.0 KB)