M4 - RPMSG buffer handling

I need to transfer some MB of data from the M4 to the Linux OS. Therefore i took the RPMSG “string echo” example and adopted that code for my program. Now i noticed a strange buffer allocation behavior and wrote a little program to clarify my issue. According to the documentation there are 256 buffers á 512 bytes with a payload length of 496. The documentation says that “rpmsg_rtos_send” will block if no free buffer is available. So when you take a look at my example program you will see that within the endless loop the same data is sent. This loop never produces an error… The RPMSG message is written to somewhere, maybe the module “imx_rpmsg_tty” takes the data and stores it? However after some time the error “imx_rpmsg_tty virtio0.rpmsg-openamp-demo-channel.-1.0: RX copy to tty layer failed” shows up on my console. And in fact data is missing. Is this behavior to be expected? I would expect the transmit to be stalled until i read out some data within Linux but i never saw this behaviour. Please note that “rpmsg_rtos_send_nocopy” behaves identically. Thanks in advance!

#include "rpmsg/rpmsg_rtos.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "string.h"
#include "board.h"
#include "mu_imx.h"
#include "debug_console_imx.h"

////////////////////////////////////////////////////////////////////////////////
// Definitions
////////////////////////////////////////////////////////////////////////////////
#define APP_TASK_STACK_SIZE 256

/*
 * APP decided interrupt priority
 */
#define APP_MU_IRQ_PRIORITY 3

char dummy_data[512];

/*!
 * @brief A basic RPMSG task
 */
static void StrEchoTask(void *pvParameters)
{
    int result;
    struct remote_device *rdev = NULL;
    struct rpmsg_channel *app_chnl = NULL;
    void *rx_buf;
    int len;
    unsigned long src;
    void *tx_buf;
    unsigned long size;


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

    // RPMSG Init as REMOTE
    PRINTF("RPMSG Init as Remote\r\n");
    result = rpmsg_rtos_init(0 /*REMOTE_CPU_ID*/, &rdev, RPMSG_MASTER, &app_chnl);
    assert(result == 0);
    PRINTF("Name service handshake is done, M4 has setup a rpmsg channel [%d ---> %d]\r\n", app_chnl->src, app_chnl->dst);

    PRINTF("Please enter something to start...\r\n");
    result = rpmsg_rtos_recv_nocopy(app_chnl->rp_ept, &rx_buf, &len, &src, 0xFFFFFFFF);
    assert(result == 0);
    result = rpmsg_rtos_recv_nocopy_free(app_chnl->rp_ept, rx_buf);
    assert(result == 0);
    PRINTF("Starting.\r\n");

    for (;;)
    {
        size = 460;
        memset(dummy_data, 0, 512);
        memset(dummy_data, 'a', size);
        dummy_data[size-2] = '\r';
        dummy_data[size-1] = '\n';
        result = rpmsg_rtos_send(app_chnl->rp_ept, dummy_data, size, src);
        if (result != 0)
        {
            PRINTF("Got error code:. %d.\r\n", result);
            continue;
        }

        /*
        // There should exist 256 buffers with 512 bytes.
        // 16 bytes are header and 496 bytes can be used for data.
        // Get tx buffer from RPMsg.
        tx_buf = rpmsg_rtos_alloc_tx_buffer(app_chnl->rp_ept, &size);
        assert(tx_buf);
        assert(size > 0);
        // Fill the buffer with junk data.
        memset(tx_buf, 0xFF, size);

        // Echo back received message with nocopy send.
        result = rpmsg_rtos_send_nocopy(app_chnl->rp_ept, tx_buf, size, src);
        assert(result == 0);*/
        
        PRINTF("Sent %d data bytes\r\n", size);
    }
}

/*
 * MU Interrrupt ISR
 */
void BOARD_MU_HANDLER(void)
{
    /*
     * calls into rpmsg_handler provided by middleware
     */
    rpmsg_handler();
}

int main(void)
{
    hardware_init();

    /*
     * Prepare for the MU Interrupt
     *  MU must be initialized before rpmsg init is called
     */
    MU_Init(BOARD_MU_BASE_ADDR);
    NVIC_SetPriority(BOARD_MU_IRQ_NUM, APP_MU_IRQ_PRIORITY);
    NVIC_EnableIRQ(BOARD_MU_IRQ_NUM);

    /* Create a demo task. */
    xTaskCreate(StrEchoTask, "String Echo Task", APP_TASK_STACK_SIZE,
                NULL, tskIDLE_PRIORITY+1, NULL);

    /* Start FreeRTOS scheduler. */
    vTaskStartScheduler();

    /* Should never reach this point. */
    while (true);
}

Hi

‘RX copy to tty layer failed’ is printed in the kernel module.

My guess is that that module is loaded and the module reads every message it gets from the m4 over rpmsg and pushes the payload into the linux tty queue which represents this virtual tty. If the linux tty queue is full the behaviour is to drop data and print the error message.

The reads every message part of the above comes from generic part, drivers/rpmsg/imx_rpmsg.c. And it looks to me that you have no ‘easy’ way of not picking up messages. I guess that driver needs to be loaded or the M4 code ‘rpmsg_rtos_init()’ will fail.

Max