Get Rpmsg character devices running

Hello everyone,

I have to address the topic ‘RPMSG uses char device driver’ again.
The solution stated in this Thread is that e.g. /dev/rpmsg_ctrl0 can be used to create an endpoint.
This then appears as device /dev/rpmsg0 … #.
With the firmware for the M4 specified in the thread, it should then react to a ‘write()’ from the user space, etc.

@nickvo uses a verdin-imx8mm board.
If the code works on this board, it should also work (adapted) on an imx8qm. Or am I wrong there?
How important is it that remoteproc is available? (I start the m4 code using the u-boot method. But I can also load the FW onto the M4 using the segger jlink and start it.)

The PingPong kernel module works. The ttyRPMSG example also works.
I can access the /dev/rpmsg0 via ‘ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);’ created. However, the m4 FW does not respond.

What are the requirements for the example to work?
At what point does the M4 FW have to be started?
Do I have to use the m4_0? I’m using m4_1.

On userspace have to config endpoint like this :

struct rpmsg_endpoint_info ept_info = {"rpmsg-openamp-demo-channel", 0x2, 0x1f}; instead of ... 0x1e

I find it strange that an endpoint is created in both the M4 and the userspace application.

Best regards
Gerald

Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           5.15.77-6.4.0-devel+git.ddc6ca4d76ea #1-TorizonCore SMP PREEMPT Thu Jun 29 10:14:22 UTC 2023
Kernel command line:      pci=nomsi root=LABEL=otaroot rootfstype=ext4 quiet logo.nologo vt.global_cursor_default=0 plymouth.ignore-serial-consoles splash fbcon=map:3 ostree=/ostree/boot.0/torizon/2c66392a498e47f9df3418452c3d3c3e2297815ae70d2a780cab6fc18b1f70bc/0
Distro name:              NAME="TorizonCore"
Distro version:           VERSION_ID=6.4.0-devel-20231006084430-build.0
Hostname:                 apalis-imx8-07202014
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 Toradex Apalis iMX8QM V1.1 on Apalis Ixora V1.2 Carrier Board
Toradex version:          0037 V1.1E
Serial number:            07202014
Processor arch:           aarch64
------------------------------------------------------------

my adjustments in the m4_1 code

/*******************************************************************************
* Definitions
******************************************************************************/

#define RPMSG_LITE_LINK_ID (RL_PLATFORM_IMX8QM_M4_A_USER_LINK_ID)
#define RPMSG_LITE_SHMEM_BASE (VDEV1_VRING_BASE)
#define LOCAL_EPT_ADDR (31) /* CM4_1 uses different EPT ADDR with CM4_0 */
#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-openamp-demo-channel-1"
#define RPMSG_LITE_MASTER_IS_LINUX
#define APP_TASK_STACK_SIZE (256U)

#ifndef LOCAL_EPT_ADDR
#define LOCAL_EPT_ADDR (30U)
#endif

#define APP_RPMSG_READY_EVENT_DATA (1U)
#define RPMSG_DATA_SIZE 256

...

Hi @gerko

How do your device tree and the full source code of the M4 look like? Did you add the right addresses for the shared memory? Unfortunately, we don’t have an overlay available yet for the Apalis iMX8QM but maybe you can find some inspiration by having a look at the Colibri iMX8X:

https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/colibri-imx8x_hmp_overlay.dts?h=toradex_5.15-2.2.x-imx

Regards,
Stefan

Hi @stefan_e.tx

@hfranco.tx sent this to me a while ago: https://community.toradex.com/t/rpmsg-character-device-driver-in-torizon-core-6-2/19464/5?u=gerko

I did it with the original code from the MCUXpresso SDK … pingpong examples and with the slightly modified code from this thread: https://community.toradex.com/t/rpmsg-char-driver-not-sending-data-on-imx8mm/18466

Hi @gerko,

Can you share it anyway? It makes it easier to reproduce. You can upload it to:
https://share.toradex.com/

Also did you verify that VDEV1_VRING_BASE matches the address in the devicetree?

Regards,
Stefan

Hi @stefan_e.tx ,

Yes, of course, and a few screenshots to show that the PingPong example works:


and in a 2nd terminal:

and a screenshot from Segger RTT from M4 debug session (So I don’t necessarily have to set up a UART debug interface on the hardware side),

The screenshots should also answer the question about the VDEV1_VRING_BASE

Here the rpmsg overlay dts and the m4_1 source https://share.toradex.com/zmsph61iwpijwdd

Next I send the example with the character device …

Next scenario:
The pingpong driver is out, and the character device drivers are in there:

In order to test the connection, here is the code for a C console program … then executed in a Docker container.
I have to start the program twice and comment out the relevant parts, otherwise I will have access-/ permission-problems.

#include <linux/rpmsg.h>

#include <sys/ioctl.h>
#include <sys/stat.h>   // chmod

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define RPMSG_DATA_SIZE 256
#define RPMSG_CTRL_DEVICE "/dev/rpmsg_ctrl0"
#define RPMSG_CHARACTER_DEVICE "/dev/rpmsg1"

typedef struct {
    char data[RPMSG_DATA_SIZE];
} msg_data_t;

char* debug_out;

int main(void)
{
    msg_data_t data_buf = {
        "Hello World!"
    };
    setbuf(stdout, NULL);
    debug_out = (char *)malloc(256 * sizeof(char));

    printf("==========================\n");
    printf("* RPMsg Hello World demo *\n");
    printf("==========================\n");

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

    // /* create endpoint interface */
    // ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info);  // /dev/rpmsg0 is created
    printf("Endpoint created\n");

    // int err = chown(RPMSG_CHARACTER_DEVICE, 1000, 1000);

    /* create endpoint */
    int fd_ept = open(RPMSG_CHARACTER_DEVICE, O_RDWR); // backend creates endpoint
    printf("Endpoint file descriptor created '%d'\n", fd_ept);

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

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

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

    close(fd_ept);
    close(fd);

    return 0;
}

here the docker-compose.yml file:

version: "3.9"
services:
  rpmsg_fp_tester-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/rpmsg_fp_tester-debug:${TAG}
    ports:
      - 2230:2230
    devices:
      - "/dev/rpmsg_ctrl0:/dev/rpmsg_ctrl0"
      - "/dev/rpmsg_ctrl1:/dev/rpmsg_ctrl1"
      - "/dev/rpmsg_ctrl2:/dev/rpmsg_ctrl2"
      - "/dev/rpmsg_ctrl3:/dev/rpmsg_ctrl3"
      - "/dev/rpmsg0:/dev/rpmsg0"
      - "/dev/rpmsg1:/dev/rpmsg1"
      # - "/dev/mychardev-0:/dev/mychardev-0"
      # - "/dev/mychardev-1:/dev/mychardev-1"
    privileged: true

and here is a screenshot when the program is waiting for incoming data. A previous write(…) has no effect anywhere

I hope I made myself clear

Hi @gerko,

What if you change the RPMSG_LITE_NS_ANNOUNCE_STRING to “rpmsg-raw”? If I understand the chardev driver correctly it is expected to receive this string from the M4 and not “rpmsg-openamp-demo-channel-1”. The openamp demo is only for the ping pong example.

Regards,
Stefan

no …
this change has no effect

my first try was to chnge the row to

#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-raw" // "rpmsg-openamp-demo-channel-1"

and do a change at userspace:

struct rpmsg_endpoint_info ept_info = {"rpmsg-raw", 0x2, 0x1f};
used here:
ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &ept_info); // /dev/rpmsg# created

Nothing has any effect.

a sequence from the rpmsg.h:

/**
* struct rpmsg_endpoint_info - endpoint info representation
* @name: name of service
* @src: local address. To set to RPMSG_ADDR_ANY if not used.
* @dst: destination address. To set to RPMSG_ADDR_ANY if not used.
*/

struct rpmsg_endpoint_info {
    char name[32];
    __u32 src;
    __u32 dst;
};

in our case:
name: “rpmsg-raw”,
src: 0x2,
dst: 0x1f

How to know the value of src?
It’s poking in the fog.


I have to correct myself:
The:
#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-raw"
in the m4 apparently causes a character device (rpmsg0) to be created.

So I no longer have to do this in user space, but can use this device.
I need to do more tests.
But we are on the right track!

Hi @gerko

I tested today on the Cortex M4 0 and it works for me. I took the normal rpmsg_lite_str_echo_rtos demo, set the RPMSG_LITE_NS_ANNOUNCE_STRING to “rpmsg-raw” and then when I do:
echo hello > /dev/rpmsg0 && cat /dev/rpmsg0
I get hello in return.

root@apalis-imx8-07107223:~# echo hello > /dev/rpmsg0 && cat /dev/rpmsg0
hello

I modified your test program so that it also works with my setup:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define RPMSG_DATA_SIZE 256
#define RPMSG_CHARACTER_DEVICE "/dev/rpmsg0"

typedef struct {
    char data[RPMSG_DATA_SIZE];
} msg_data_t;

char* debug_out;

int main(void)
{
    msg_data_t data_buf = {
        "Hello World!"
    };
    setbuf(stdout, NULL);
    debug_out = (char *)malloc(256 * sizeof(char));

    printf("==========================\n");
    printf("* RPMsg Hello World demo *\n");
    printf("==========================\n");

    /* create endpoint */
    int fd_ept = open(RPMSG_CHARACTER_DEVICE, O_RDWR); // backend creates endpoint
    printf("Endpoint file descriptor created '%d'\n", fd_ept);

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

    // // /* receive data from remote device */
    printf("Receiving...\n");
    read(fd_ept, &data_buf, sizeof(data_buf));
    printf("%s received\n", data_buf.data);

    // /* destroy endpoint */

    close(fd_ept);

    return 0;
}

Here the output of the example:

root@apalis-imx8-07107223:~# ./test-sw
==========================
* RPMsg Hello World demo *
==========================
Endpoint file descriptor created '3'
Sending Hello World!...
Receiving...
Hello World! received

Regards,
Stefan

Thank you,
To complete the thread you could adapt the printf’s:
here is the edited code:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define RPMSG_DATA_SIZE 256
#define RPMSG_CHARACTER_DEVICE "/dev/rpmsg0"

typedef struct {
    char data[RPMSG_DATA_SIZE];
} msg_data_t;

char* debug_out;

int main(void)
{
    msg_data_t data_buf = {
        "Hello World!"
    };
    setbuf(stdout, NULL);
    debug_out = (char *)malloc(256 * sizeof(char));

    printf("==========================\n");
    printf("* RPMsg Hello World demo *\n");
    printf("==========================\n");

    /* open endpoint */
    int fd_ept = open(RPMSG_CHARACTER_DEVICE, O_RDWR); 
    printf("RPMSG device '%s' opened fd is '%d'\n",RPMSG_CHARACTER_DEVICE, fd_ept);

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

    // // /* receive data from remote device */
    printf("Receiving...\n");
    read(fd_ept, &data_buf, sizeof(data_buf));
    printf("%s received\n", data_buf.data);

    close(fd_ept);

    return 0;
}

The end point is already created when booting up. Maybe it’s important to know for understanding if you’re dealing with it for the first time.
Don’t want to be a wisenheimer … :slight_smile: