当前位置:网站首页>[semidrive source code analysis] [x9 chip startup process] 25 - Introduction to mailbox inter core communication mechanism (code analysis) rpmsg-ipcc RTOS & QNX

[semidrive source code analysis] [x9 chip startup process] 25 - Introduction to mailbox inter core communication mechanism (code analysis) rpmsg-ipcc RTOS & QNX

2022-06-12 13:44:00 CielleeX


Ben SemiDrive Source code analysis And Yocto Source code analysis The series is summarized below :

  1. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】01 - yocto/base Directory source code analysis ( Compilation environment initialization process )
  2. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】02 - yocto/meta-openembedded Directory source code analysis
  3. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】03 - yocto/meta-semidrive Contents and Yocto Kernel Compile process analysis ( On )
  4. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】04 - yocto/meta-semidrive Contents and Yocto Kernel Compile process analysis ( Next )
  5. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】05 - Look for it Yocto Kernel All during compilation Task Where is the source code defined ?
  6. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】06 - Kernel Compile generated Image.bin、Image_nobt.dtb、modules.tgz How are these three files generated ?
  7. 【SemiDrive Source code analysis 】【Yocto Source code analysis 】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 How the file system is generated
  8. 【SemiDrive Source code analysis 】【X9 Chip startup process 】08 - X9 platform lk Directory source code analysis And catalogue
  9. 【SemiDrive Source code analysis 】【X9 Chip startup process 】09 - X9 Platform system startup process analysis
  10. 【SemiDrive Source code analysis 】【X9 Chip startup process 】10 - BareMetal_Suite Catalog R5 DIL.bin Bootstrap source code analysis
  11. 【SemiDrive Source code analysis 】【X9 Chip startup process 】11 - freertos_safetyos Catalog Cortex-R5 DIL2.bin Bootstrap source code analysis
  12. 【SemiDrive Source code analysis 】【X9 Chip startup process 】12 - freertos_safetyos Catalog Cortex-R5 DIL2.bin And sdm_display_init Display initialization source code analysis
  13. 【SemiDrive Source code analysis 】【X9 Chip driver debugging 】13 - GPIO Configuration method
  14. 【SemiDrive Source code analysis 】【X9 Chip startup process 】14 - freertos_safetyos Catalog Cortex-R5 SafetyOS/RTOS Workflow analysis
  15. 【SemiDrive Source code analysis 】【X9 Chip startup process 】15 - freertos_safetyos Catalog R5 SafetyOS And tcpip_init() Code flow analysis
  16. 【SemiDrive Source code analysis 】【X9 Audio Audio module analysis 】16 - Audio module block diagram and hardware schematic diagram analysis
  17. 【SemiDrive Source code analysis 】【X9 Chip startup process 】17 - R5 SafetyOS And LK_INIT_LEVEL_PLATFORM Stage code flow analysis ( On )dcf_init Inter core communication initialization
  18. 【SemiDrive Source code analysis 】【X9 Chip startup process 】18 - R5 SafetyOS And LK_INIT_LEVEL_PLATFORM Stage code flow analysis ( Next )
  19. 【SemiDrive Source code analysis 】【X9 Chip startup process 】19 - MailBox Introduction to inter core communication mechanism ( Theory Chapter )
  20. 【SemiDrive Source code analysis 】【X9 Chip startup process 】20 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And MailBox for RTOS piece
  21. 【SemiDrive Source code analysis 】【X9 Chip startup process 】21 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And Mailbox for Linux piece
  22. 【SemiDrive Source code analysis 】【X9 Chip startup process 】22 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPMSG-VIRTIO Kernel piece
  23. 【SemiDrive Source code analysis 】【X9 Chip startup process 】23 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPMSG-IPCC Kernel piece
  24. 【SemiDrive Source code analysis 】【X9 Chip startup process 】24 - MailBox Inter core communication mechanism related register introduction
  25. 【SemiDrive Source code analysis 】【X9 Chip startup process 】25 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPMSG-IPCC RTOS & QNX piece
  26. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】26 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And Property piece 》
  27. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】27 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPCall piece 》
  28. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】28 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And Notify piece 》
  29. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】29 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And Socket piece 》
  30. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】30 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And /dev/vircan piece 》
  31. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】31 - freertos_safetyos Catalog R5 SafetyOS And LK_INIT_LEVEL_TARGET Stage code flow analysis 》
  32. 《【SemiDrive Source code analysis 】【X9 Chip startup process 】32 - freertos_safetyos Catalog R5 SafetyOS And .apps Application startup code flow analysis 》


In the previous article , We mainly introduce... From the theoretical aspect , be based on MailBox Of various scenes API,
Its characteristics are as follows :

RTOSLinux KernelLinux UserDom0 LinuxDomU LinuxQNXTX/RX Buffer size Features and usage scenarios
Mailbox Support Support I won't support it Support Support Support RX/TX Are all 512 byte Interrupt context callback , Low latency , No message queue , It is not recommended that users directly use
RPMSG-VIRTIO Support Support Support Support I won't support it Support RX/TX Two loop queues
Each queue supports a maximum of 512 block Buffer, Minimum 256 block
each Buffer by 512 byte
Memory is mainly allocated DMA Low address continuous physical memory
Large data block transmission , Use DDR Shared memory data , High throughput ,MTU The adjustable
RPMSG-IPCC Support Support Support Support I won't support it Support Maximum number of transmission targets 16 individual
Each transfer destination defaults to 512 byte , Maximum transmission 512-16=496 byte ( Can be configured as 1024 byte )
Memory uses GFP_KERNEL Part of the memory
Control command , event , Notice, etc , Interactive data is small , Less delay
RPCall Support Support I won't support it Support Support Support To be analyzed and summarized be based on request / reply Distributed data exchange mode of the model
Property Support Support Support Support Support Support To be analyzed and summarized A distributed 、 Runtime heterogeneous data storage mechanism , You can save the running status or execution results , Convenient heterogeneous OS Share certain resources between
Notify Support Support I won't support it Support Support I won't support it To be analyzed and summarized Short data , Low latency , One way transmission
Socket No support Support Support Support Support I won't support it To be analyzed and summarized Support AF_RPMSG agreement , The user layer uses , The disadvantage is that it needs to be modified Linux uapi The header file
/dev/vircanNANA Support Support Support I won't support it To be analyzed and summarized Linux interview ask Safety CAN

In the foreword 《【SemiDrive Source code analysis 】【X9 Chip startup process 】23 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPMSG-IPCC Kernel piece 》 in ,
We analyzed and learned RPMSG-VIRTIO Kernel Partial implementation ,
But there is a problem left , It hasn't been solved yet , Namely Kernel Medium RPMSG-IPCC The only way to receive is to go echo cb This road , There is no way to see its reception ,
So it's strange , With this question , Let's analyze it today RTOSQNX These two pieces of RPMSG-IPCC How to achieve ,
Maybe after understanding these two pieces , We'll know the answer .



RPMSG yes Remote Processor Messaging System, Remote processor message transmission system .

  • RPMSG It is a data transmission protocol between heterogeneous systems .
    RPMSG The standard does not specify the underlying transmission medium and method , Because of the universality of shared memory , And less dependence on specific hardware differences ,VIRTIO As a kind of RPMSG Common implementation forms , It has been widely used .

  • VIRTIO Is based on Shared Memory A data transmission standard , Including buffer and message queue management , Not only for heterogeneous multiprocessor communication , It is also commonly used in KVM Virtualized device .

  • VirtQueue It's a data receiving and sending queue , Contains a pair of send queues and receive queues Vring.

  • VRing yes VirtQueue The queue in , A method with Buffer Descriptor Implementation method of index quantization circular buffer , Every VRing There is only one sender and one receiver , Two way communication is required VRing Make a couple , The index structure and buffer of the whole queue exist in the shared memory area , Accessed by processors at both ends of the communication . in order to
    Ensure the data access consistency of heterogeneous processors , Shared memory is configured as a device memory model ,Strong Order,Non-Cacheable

  • Notification mechanism of standard provisions (notify) It's about the platform , Used on Xinchi platform Mailbox Hardware implementation .

One 、RPMSG Interface

Xinchi fully follows the standard RPMSG Realization , On this basis , For ease of use , Put forward RPMSG Channel Concept and a set of interface implementation .
meanwhile , In order to take advantage of Mailbox Chip characteristics of on-chip memory , Put forward IPCC Channel The concept of , That is to keep RPMSG Interface logic ,
Follow the standards RPMSG On the basis of , To replace the VIRTIO The bottom part of . These two functions can coexist , Users can choose one of them according to their needs .

IPCC Make the most of it Mailbox Features of hardware , It has independent buffer queue management and thread model , and IPCC No need for external DDR As shared memory , High for safety MCU It has obvious advantages , Recommended priority IPCC Interface functions perform the same functions .

in addition , in consideration of VIRTIO The concept of master-slave equipment is required , Usually Linux perhaps QNX The terminal is the main equipment , be responsible for Virtqueue Initialization of buffer queue , Lead to MCU If you want to use VIRTIO, The timing depends on the initialization of the master device , It will inevitably affect the start-up .

and IPCC All ends are equal , There is a mobile phone holding system during startup to ensure normal connection between both parties , It also supports abnormal situations such as restart and disconnection of one party , More in line with the requirements of functional safety .

When the user needs to use large memory transfer , Or when there is a more radical demand for bandwidth , Consider using RPMSG VIRTIO Implementation version .
increase MTU Greater single transmission capability can be obtained , Improve transmission bandwidth .



1.1 Linux Kernel Interface

1.2 Linux Kernel Sample code analysis :rpmsg-vdev.c be based on virtio Bus mode implementation RPMSG

1.3 Linux Kernel Sample code analysis :semidrive_ipcc.c be based on IPCC Bus mode implementation RPMSG

1.4 Linux User layer interface

See... For the first four sections :《【SemiDrive Source code analysis 】【X9 Chip startup process 】23 - MailBox Introduction to inter core communication mechanism ( Code analysis ) And RPMSG-IPCC Kernel piece


1.5 RTOS API

Zaixin Chi RTOS SDK Use in rpmsg virtio The function needs to include the following header files : #include <rpmsg_rtos.h>

rpmsg channel Based on the open source community rpmsg lite Do further encapsulation , Use virtio As data transmission , Use Mailbox As a notification of a cross nuclear event .
When using rpmsg channel Interface created endpoint After success ,Linux At the end, the corresponding virtio0( If security The domain is virtio1) Prefixed rpmsg device

rpmsg lite The code for is located in ${RTOS_SDK_TOP}/3rd/rpmsg-lite
rpmsg channel The encapsulation layer is located at ${RTOS_SDK_TOP}/framework/service/rpmsg

rpmsg channel API Definition :

struct rpmsg_channel * rpmsg_channel_create(int rproc, int dst, const char *name);
int rpmsg_channel_start(struct rpmsg_channel *rpchn, rpmsg_msg_handler handler);
void rpmsg_channel_destroy(struct rpmsg_channel *rpchn);
status_t rpmsg_channel_stop(struct rpmsg_channel *rpchn);
void rpmsg_channel_listall(struct rpmsg_device *dev);
status_t rpmsg_channel_sendmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int len, lk_time_t timeout);
status_t rpmsg_channel_recvmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int msglen, lk_time_t timeout);
status_t rpmsg_channel_recvfrom(struct rpmsg_channel *rpchn, unsigned long *src, char *data, int maxlen, int *len, unsigned long timeout);
status_t rpmsg_channel_sendto(struct rpmsg_channel *rpchn, unsigned long dst, char *data, unsigned long size, unsigned long timeout);

Zaixin Chi RTOS SDK Use in IPCC The function needs to include the following header files :#include <dcf.h>

IPCC Channel API The definition is similar to the above , Please refer to the interface statement for details :
${RTOS_SDK_TOP}/framework/communication/include/ipcc_device.h

Interface implementation C file :
${RTOS_SDK_TOP}/framework/communication/ipcc_device.c

Reference resources SDK Sample code included in
RPMSG VIRTIO: ${RTOS_SDK_TOP}/framework/test/dcf/test_rpmsg_linux.c
RPMSG IPCC: ${RTOS_SDK_TOP}/framework/test/dcf/dcf_sample.c

below , Let's first learn about the example program , Let's see how to use :


1.6 【RTOS_IPCC example 】dcf\dcf_sample.c With IPCC How to achieve

stay RTOS in , function ipccc ping You will enter dcf_sample.c Source code function do_ipcc_ping() function

# buildsystem\rtos\lk_boot\framework\test\dcf\dcf_sample.c
int do_ipcc_cmd(int argc, const cmd_args *argv)
{
    
    if (!strcmp(argv[1].str, "ping")) {
     do_ipcc_ping(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "perf")) {
     do_ipcc_perf(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "iperf")) {
     do_ipcc_iperf(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "reset")) {
     do_ipcc_reset_dev(argc-1, &argv[1]);  }
    if (!strcmp(argv[1].str, "list")) {
     do_ipcc_listdev(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "rpc_echo")) {
     do_dcf_rpc_ping(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "rpc_reg_svc")) {
     do_ipcc_rpc_customize(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "rpc_gettimeofday")) {
     do_dcf_rpc_gettimeofday(argc-1, &argv[1]);   }
    if (!strcmp(argv[1].str, "rpc_uname")) {
     do_dcf_rpc_get_uname(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "rpc_failsafe")) {
     do_dcf_rpc_failsafe(argc-1, &argv[1]); }
#if CONFIG_USE_SYS_PROPERTY
    if (!strcmp(argv[1].str, "setprop")) {
     do_set_property(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "getprop")) {
     do_get_property(argc-1, &argv[1]); }
#endif
#if CONFIG_RPC_UNITTEST
    if (!strcmp(argv[1].str, "rpc_ut")) {
     do_rpc_ut_cmd(argc-1, &argv[1]); }
#endif
    return 0;
}

STATIC_COMMAND_START
STATIC_COMMAND("ipcc", "ipcc toolbox", do_ipcc_cmd)
STATIC_COMMAND_END(dcf_sample);

Let's see do_ipcc_ping() How functions are implemented :

  1. analysis IPCC Arguments to the command : ipcc ping [remote Distal ] [round frequency ] [len Packet size ]
  2. The size of the transfer cannot be greater than 512 - 16 = 496 byte
  3. take tx_bufrx_buf Formatted as struct dcf_ccm_hdr Type of structure
  4. The message format is COMM_MSG_CCM_ECHO, The opposite end will pass echo endpoint call ept->echo_cb To receive parsing messages
  5. initialization tx_hdr->time[4] And tx_hdr->data[IPCC_MB_MTU] The data in the two parts of memory is 0
  6. obtain remote Corresponding to the far end ipcc_devices[] Structure
  7. apply ipcc_channel passageway , start-up ipcc_channel passageway
  8. Start sending , Record the current time to time[0] in , time[2] It is used to record the time after the remote end receives it
  9. adopt ipcc channel Start sending , Apply for a piece of buffer, The address is saved at struct rpmsg_ipcc_msg->data Is used to save data , Then call rpmsg_dcf_dev->chan_ops->send_data() send data
  10. receive data , Timeout time 1000ms

The whole process is relatively simple , It's easy to understand .

# buildsystem\rtos\lk_boot\framework\test\dcf\dcf_sample.c
/* Test case: ping remote cpu with payload and recv equal payload like network ping */
int do_ipcc_ping(int argc, const cmd_args *argv)
{
    
    struct ipcc_device *dev;
    struct ipcc_channel *chan;
    char tx_buf[IPCC_MB_MTU] = {
    0,};		// 512 - 16 = 496 byte
    char rx_buf[IPCC_MB_MTU] = {
    0,};		// 512 - 16 = 496 byte
    int len = TEST_IPCC_PING_PAYSZ;			// 64 byte  Every time ping The packet size of 
    unsigned long remote = 0;
    struct dcf_ccm_hdr *tx_hdr, *rx_hdr;
    u32 round = 10;							//  loop ping The number of times 
	
	// 1.  analysis  IPCC  Arguments to the command : ipcc ping [remote Distal ] [round frequency ] [len Packet size ]
    if (argc == 1) {
    
        remote = 1;
    } else {
    
        remote = argv[1].u;
        if (argc == 3) {
     round = argv[2].u; }
        if (argc == 4) {
     round = argv[2].u; len = argv[3].u; }
    }
	// 2.  The size of the transfer cannot be greater than  496 byte 
    if (len > IPCC_MB_MTU) {
     printf("len %d exceed MTU %d, trim\n", len, IPCC_MB_MTU);  len = IPCC_MB_MTU; }
	// 3.  take  tx_buf、rx_buf  Formatted as  struct dcf_ccm_hdr  Type of structure 
    tx_hdr = (struct dcf_ccm_hdr *) tx_buf;
    rx_hdr = (struct dcf_ccm_hdr *) rx_buf;
	// 4.  The message format is  COMM_MSG_CCM_ECHO, The opposite end will pass  echo endpoint  call  ept->echo_cb  To receive parsing messages 
    tx_hdr->dmsg.msg_type = COMM_MSG_CCM_ECHO;
    tx_hdr->dmsg.msg_len  = len - sizeof(struct dcf_message);
    tx_hdr->dmsg.opflags |= DCF_MSGF_TMS;
	
	// 5.  initialization  tx_hdr->time[4]  And  tx_hdr->data[IPCC_MB_MTU]  The data in the two parts of memory is  0 
    memset(&tx_hdr->time[0], 0, sizeof(tx_hdr->time));
    memset(&tx_hdr->data[0], COMM_MSG_CCM_ECHO, len -  sizeof(struct dcf_ccm_hdr));
    printf("PING rproc%d %d(%d) bytes of data\n", remote, len, len + IPCC_TRANSPORT_HDRLEN);

	// 6.  obtain  remote  Corresponding to the far end  ipcc_devices[]  Structure 
    dev = ipcc_device_gethandle(remote, 1000);
    -----> struct ipcc_device *dev = find_device_by_rproc(rproc);
    ============================>
    +	struct ipcc_device *dev = NULL;
	+   for (i = 0;i < MAX_DEVICE_NUM;i++) {
    
	+       dev = &ipcc_devices[i];
	+       if (dev->used && rproc == dev->config.rproc) {
    
	+           return dev;
	+       }
	+   }
	<============================
	
	// 7.  apply  ipcc_channel  passageway  
    chan = ipcc_channel_create(dev, RPMSG_TEST_EPT, RPMSG_TEST_EPT_NAME, false);
	============================>
	+	struct ipcc_channel * ipcc_channel_create(struct ipcc_device *dev, int endpoint, const char *name, bool announce){
    
	+		struct ipcc_channel *ichan;
	+		ichan = calloc(1, sizeof(struct ipcc_channel));
	+
	+		event_init(&ichan->initialized, false, EVENT_FLAG_AUTOUNSIGNAL);
	+		sd_rpbuf_init_queue(&ichan->rxq);
	+		sem_init(&ichan->rxq_wait, 0);
	+
	+		ichan->rproc = dev->config.rproc;
	+		ichan->addr = endpoint;
	+		ichan->state = DCF_STATE_Initializing;
	+		ichan->mtu = DCF_MSG_MAX_LEN;
	+		ichan->parent = dev;
	+		ichan->announce = announce;
	+		ichan->last_announced = 0;
	+		ichan->endpoint = rpmsg_dcf_create_ept(dev->rpmsg_dev, ichan->addr, ipcc_channel_rx_cb, ichan); //  Put the received data in the callback function &ichan->rxq in 
	+
	+		attach_channel(dev, ichan);
	+		strncpy(ichan->name, name, 16);
	+		ichan->state = DCF_STATE_Initialized;
	+		return ichan;
	+	}
	<============================

	// 8.  start-up  ipcc_channel  passageway  
    ipcc_channel_start(chan, NULL);

    do {
    
	
        start_time = current_time_hires();
		// 9.  Start sending , Record the current time to  time[0] in , time[2]  It is used to record the time after the remote end receives it 
        /* use the time[0] for the sending, time[2] for receving in remote */
        tx_hdr->time[0] = syscnt_get_cnt();
        tx_hdr->seq = tx_count;
        
        // 10.  adopt  Ipcc channel  Start sending , Apply for a piece of buffer, The address is saved at  struct rpmsg_ipcc_msg->data  Is used to save data , Then call send_data send data 
        ret = ipcc_channel_sendto(chan, IPCC_ECHO_EPT, tx_buf, len, 100);
        ------> return rpmsg_dcf_send(ichan->parent->rpmsg_dev, ichan->endpoint,  dst, data, size, (unsigned long)timeout);
        ==========> return rpmsg_dcf_format_message(rpmsg_dcf_dev, ept->addr, dst, data, size, DCF_NO_FLAGS, timeout);
        =======================>
        +	buffer = rpmsg_dcf_dev->chan_ops->send_alloc(rpmsg_dcf_dev->tvq, &buff_len, &idx);
        +	rpmsg_msg = (struct rpmsg_ipcc_msg *)buffer;
		+   /* Initialize RPMSG header. */
		+  	rpmsg_msg->hdr.dst = dst;
		+   rpmsg_msg->hdr.src = src;
		+   rpmsg_msg->hdr.len = size;
		+   rpmsg_msg->hdr.flags = flags;
		+   /* Copy data to rpmsg buffer. */
		+   memcpy(rpmsg_msg->data, data, size);
		+   /* Enqueue buffer on sd_ipcc_chan. */
		+   status = rpmsg_dcf_dev->chan_ops->send_data(rpmsg_dcf_dev->tvq, buffer, buff_len, idx);
		<========================
        
        tx_time = current_time_hires();
        tx_count++;
		
		// 11.  receive data , Timeout time  1000ms
        ret = ipcc_channel_recvfrom(chan, &src, rx_buf, &len, 1000);
        ---> rpbuf = recv_rpbuf_timed(ichan, timeout); ret = recvfrom_timed(ichan, src, data, len, timeout);
        =====> return rpmsg_dcf_copy_payload(rpmsg_dev, rpbuf, src, data, len);
        ===========> rpmsg_msg = (struct rpmsg_ipcc_msg *) rpbuf->buffer;	*len = rpmsg_msg->hdr.len;  memcpy(data, get_payload(rpbuf), *len);
        
        end_time = current_time_hires();
		......
    } while (tx_count < round);

    printf("%d packets transmitted, %d packets received, %.02f%% loss, time %ld us\n", tx_count, rx_count, 100 - 100 * (float)(rx_count/round),  rtt_total);
    printf("rtt min/avg/max = %llu/%llu/%llu us\n", rtt_min, rtt_total/rx_count, rtt_max);

    ipcc_channel_stop(chan);
    ipcc_channel_destroy(chan);
    return 0;
}

1.6.1 【RTOS_IPCC】dcf\dcf_sample.c receive data ipcc_send_data()

# buildsystem\rtos\lk_boot\framework\communication\ipcc_rpmsg.c
/* Interface used in case this processor is MASTER */
static const struct dcf_ipcc_ops master_chan_ops = {
    
    ipcc_send_data, ipcc_send_data_nocopy, send_alloc_buffer, ipcc_rx_data, recv_free_buffer,
};

static int ipcc_send_data(sd_ipcc_chan_t *tvq, void *buffer, uint32_t len, uint16_t idx){
    
    int status;
    status = sd_ipcc_chan_send_data(tvq, buffer, len);
	======> status = sd_ipcc_chan_send_data(tvq, buffer, len);
    return status;
}

Let's see sd_ipcc_chan_send_data() What did you do :

# buildsystem\rtos\lk_boot\hal\mbox_hal\sd_mbox_hal\src\mbox_hal.c
int sd_ipcc_chan_send_data(sd_ipcc_chan_t *chan, u8 *dat, u32 len)
{
    
    ret = hal_mb_send_data(chan->mb_chan, dat, len_short, timeout);
    ====> return hal_mb_send_data_detail(chan, data, len, MB_MSG_PROTO_DFT, cl->low_latency, chan->dest_addr, timeout);
    return ret;
}

Let's see hal_mb_send_data_detail() Implementation code :

# buildsystem\rtos\lk_boot\hal\mbox_hal\sd_mbox_hal\src\mbox_hal.c
int hal_mb_send_data_detail(hal_mb_chan_t *chan, u8 *data, u16 len, int proto, int priority, u8 dest, lk_time_t timeout)
{
    
    hal_mb_client_t cl = containerof(chan, struct _hal_mbox_client, mchan);
    u8 *msg_buf;
    sd_msghdr_t *msg;

    ret = __alloc_avail_buf(chan);
    msg_buf = chan->msg_data[ret];

    msg = (sd_msghdr_t *)msg_buf;
    mb_msg_init_head(msg, chan->remote_proc, proto, priority, len, dest);
    memcpy(msg->data, data, len);
    /* wait for a while, 1000 ms for hardcode */
    ret = __send_data_sync(chan, (u8 *)msg, timeout);

    __free_used_buf(chan);
    return ret;
}

Let's see __send_data_sync() What has been achieved :

# buildsystem\rtos\lk_boot\hal\mbox_hal\sd_mbox_hal\src\mbox_hal.c
static status_t __send_data_sync(hal_mb_chan_t *chan, u8 *data, lk_time_t timeout)
{
    
    hal_mb_client_t cl = containerof(chan, struct _hal_mbox_client, mchan);
    u32 spin_usec = MB_TX_MAX_ACK_TIME;
    int ret = 0;
    lk_time_t end_time;
    end_time = current_time() + timeout;
    do {
    
        ret = sd_mbox_send_data(chan->hwchan, data);
    } while ((ret == ERR_NO_MSG) && TIME_LT(current_time(), end_time));

    while (!sd_mbox_last_tx_done(chan->hwchan)) {
    
		thread_sleep(1);
    }
}

Data saved in data in , Then call sd_mbox_send_data() send data , Write register BM_TMC0_TMC0_MSG_SEND Trigger send ,
I won't go into details , The previous article introduced registers in great detail .

# buildsystem\rtos\lk_boot\chipdev\mailbox\sd_mbox\mb_controller.c
int sd_mbox_send_data(struct sd_mbox_chan *mlink, u8 *data)
{
    
    struct sd_mbox_device *mbdev = mlink->mbox;
    int prefer_msg;
    mlink->actual_size = mb_msg_parse_packet_len(data);
    mlink->priority = mb_msg_parse_packet_prio(data);
    mlink->protocol = mb_msg_parse_packet_proto(data);
    mlink->dest_addr = mb_msg_parse_addr(data);

    prefer_msg = (mlink->protocol == MB_MSG_PROTO_ROM) ? 1 : 0;

    if (!mlink->msg) {
    
        mlink->msg = sd_mu_alloc_msg(mbdev, prefer_msg, mlink->priority);
        mlink->msg->remote = mlink->target;
        mlink->msg->client = mlink->cl_data;
    }

    dprintf(SPEW, "mu: send_data proto: %d length: %d msg: %d\n",  mlink->protocol, mlink->actual_size, mlink->msg->msg_id);

    if (MB_MSG_PROTO_ROM == mlink->protocol) {
    
        ret = sd_mu_write_rom_msg(mlink, data);
    } else if (MB_MSG_PROTO_DSP == mlink->protocol) {
    
        ret = sd_mu_write_rom_msg(mlink, data);
    } else {
    
        ret = sd_mu_fill_tmh(mlink);
        sd_mu_write_msg(mlink, data);
    }
	
	sd_mu_send_msg(mlink->msg);
	=======> writel(readl(msg->tmc) | BM_TMC0_TMC0_MSG_SEND, msg->tmc);
    return ret;
}

1.7 【RTOS_VIRTIO example 】dcf\test_rpmsg_linux.c use VIRTIO How to achieve

# buildsystem\rtos\lk_boot\framework\test\dcf\test_rpmsg_linux.c

int do_rpmsg_cmd(int argc, const cmd_args *argv){
    
    if (!strcmp(argv[1].str, "start")) {
     do_start_rpmsg_service(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "reset")) {
     do_reset_rpmsg_service(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "open")) {
     do_rpmsg_open_close(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "list")) {
     do_rpmsg_listdev(argc-1, &argv[1]);}
    if (!strcmp(argv[1].str, "ping")) {
     do_rpmsg_ping(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "echo_test")) {
     do_rpmsg_echo_test(argc-1, &argv[1]); }
    if (!strcmp(argv[1].str, "iperf")) {
     do_rpmsg_iperf(argc-1, &argv[1]);  }
    if (!strcmp(argv[1].str, "perf")) {
     do_rpmsg_perf(argc-1, &argv[1]); }
}
STATIC_COMMAND_START
STATIC_COMMAND("rpmsg", "rpmsg toolbox and test", do_rpmsg_cmd)
STATIC_COMMAND_END(test_rpmsg_linux);

With iperf For example :

# buildsystem\rtos\lk_boot\framework\test\dcf\test_rpmsg_linux.c
int start_rpmsg_perf_test(int remote, int round, int size)
{
    
    struct rpmsg_channel *chan;
    char send_buf[RPMSG_PKT_MTU] = {
    0,};
    char rx_buf[RPMSG_TACK_LEN] = {
    0,};
    int len = RPMSG_PKT_MTU;
    int recvd = 4;
    printf("Buffer size rproc%d %d(%d) bytes of data\n", remote, len, len + RPMSG_PKT_HDRLEN);

    chan = rpmsg_channel_create(remote, RPMSG_TEST_EPT, RPMSG_TEST_EPT_NAME);
    rpmsg_channel_start(chan, NULL);
    memset(send_buf, COMM_MSG_CCM_ACK, len);
    do {
    
        ret = rpmsg_channel_sendto(chan, RPMSG_ECHO_EPT, send_buf, len, 100);
        tx_count++;

        recvd = RPMSG_TACK_LEN;
        ret = rpmsg_channel_recvfrom(chan, &src, rx_buf, RPMSG_PKT_MTU, &recvd, 1000);

        if (recvd != RPMSG_TACK_LEN || strncmp(rx_buf, RPMSG_TACK_CODE, recvd)) {
    	//  Check whether the received security string is  "ACK"
            break;
        }
        rx_count++;
        memset(rx_buf, 0, recvd);
    } while (tx_count < round);

    printf("%d packets transmitted, %d acked, %.02f%% loss, time %ld us\n",
                        tx_count, rx_count, 100 - 100 * (float)(rx_count/round), rtt_total);
    printf("rtt min/avg/max = %llu/%llu/%llu us\n", rtt_min, rtt_total/rx_count, rtt_max);
    printf("throughput avg. = %.2f MB/s\n", (float)(rx_count * len)/rtt_total);
    rpmsg_channel_stop(chan);
    rpmsg_channel_destroy(chan);
    return 0;
}

The code doesn't explain , A look at will understand .




1.8 【RTOS_VIRTIO】rpmsg_channel_create() Function creation rpmsg channel passageway

# rtos\lk_boot\framework\service\rpmsg\rpmsg_channel.c
struct rpmsg_channel *rpmsg_channel_create(int rproc, int endpoint, const char *name){
    
    struct rpmsg_channel *rpchn = calloc(1, sizeof(struct rpmsg_channel));
    event_init(&rpchn->initialized, false, EVENT_FLAG_AUTOUNSIGNAL);

    rpchn->rproc = rproc;
    rpchn->addr = endpoint;
    rpchn->state = DCF_STATE_Initializing;
    rpchn->mtu = DCF_MSG_MAX_LEN;

    rpchn->parent = rpmsg_device_get_handle(rpchn->rproc, RPMC_INIT_TIMEOUT);
    rpchn->rpmsg_dev = rpchn->parent->rl_instance;

	//  Apply for one  vring  ring buffer, Used to receive data , The quantity is  vq->vq_nentries = ring->num_descs 
    rpchn->msg_queue = rpmsg_queue_create(rpchn->rpmsg_dev);
    -------> status = env_create_queue(&q, rpmsg_lite_dev->rvq->vq_nentries, sizeof(rpmsg_queue_rx_cb_data_t));

    rpchn->endpoint = rpmsg_lite_create_ept(rpchn->rpmsg_dev, rpchn->addr, rpmsg_queue_rx_cb, rpchn->msg_queue);

    rpmsg_device_add_channel(rpchn->parent, rpchn);
    strncpy(rpchn->name, name, DCF_NAM_MAX_LEN);
    rpchn->state = DCF_STATE_Initialized;
    return rpchn;
}

1.9 【RTOS_VIRTIO】rpmsg_channel_start() Function implementation starts rpmsg channel passageway

# rtos\lk_boot\framework\service\rpmsg\rpmsg_channel.c
int rpmsg_channel_start(struct rpmsg_channel *rpchn, rpmsg_msg_handler handler)
{
    
    struct rpmsg_device *dev = rpchn->parent;
    /* connecting with remote side */
    if (!dev->config.is_master) {
    
        rpmsg_ns_announce(rpchn->rpmsg_dev, rpchn->endpoint, rpchn->name, RL_NS_CREATE);
    }

    /* if specify msg handler, spawn a thread to receive msg and call handler */
    if (handler) {
    
        rpchn->msg_handler = handler;
        /* start up a thread to process message queue */
        thread_resume(thread_create(rpchn->name, rpmsg_looper_thread, rpchn, THREAD_PRI_RPMSG_CHN, CONFIG_RPMSG_STACK_SIZE));
        event_wait(&rpchn->initialized);
    } else
        rpchn->state = DCF_STATE_Connected;
    dprintf(INFO, "rpmsg channel %s:%d->%d connected\n",
            rpchn->name, dcf_get_this_proc(), rpchn->rproc);

    return 0;
}

1.10 【RTOS_VIRTIO】rpmsg_channel_sendmsg /rpmsg_channel_sendto () Function to send data

# rtos\lk_boot\framework\service\rpmsg\rpmsg_channel.c
status_t rpmsg_channel_sendmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int len, lk_time_t timeout)
{
    
    return rpmsg_lite_send(rpchn->rpmsg_dev, rpchn->endpoint, rpchn->addr, (char *)msg, len, timeout);
    =====> return rpmsg_lite_format_message(rpmsg_lite_dev, ept->addr, dst, data, size, RL_NO_FLAGS, timeout);
}

int rpmsg_lite_format_message(struct rpmsg_lite_instance *rpmsg_lite_dev,  unsigned long src, unsigned long dst, char *data, unsigned long size, int flags, unsigned long timeout)
{
    
    struct rpmsg_std_msg *rpmsg_msg;
    void *buffer;
    unsigned long tick_count = 0;

    /* Get rpmsg buffer for sending message. */
    buffer = rpmsg_lite_dev->vq_ops->vq_tx_alloc(rpmsg_lite_dev->tvq, &buff_len, &idx);

    while (!buffer)
    {
    
        env_sleep_msec(RL_MS_PER_INTERVAL);
        env_lock_mutex(rpmsg_lite_dev->lock);
        buffer = rpmsg_lite_dev->vq_ops->vq_tx_alloc(rpmsg_lite_dev->tvq, &buff_len, &idx);
        env_unlock_mutex(rpmsg_lite_dev->lock);
        tick_count += RL_MS_PER_INTERVAL;
        if ((tick_count >= timeout) && (!buffer))
        {
    
            return RL_ERR_NO_MEM;
        }
    }
    rpmsg_msg = (struct rpmsg_std_msg *)buffer;

    /* Initialize RPMSG header. */
    rpmsg_msg->hdr.dst = dst;
    rpmsg_msg->hdr.src = src;
    rpmsg_msg->hdr.len = size;
    rpmsg_msg->hdr.flags = flags;

    /* Copy data to rpmsg buffer. */
    env_memcpy(rpmsg_msg->data, data, size);

    virtqueue_kick(rpmsg_lite_dev->tvq);
}

1.11 【RTOS_VIRTIO】rpmsg_channel_recvmsg / rpmsg_channel_recvfrom() Function to receive data

# rtos\lk_boot\framework\service\rpmsg\rpmsg_channel.c
status_t rpmsg_channel_recvmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int msglen, lk_time_t timeout)
{
    
    ret = rpmsg_queue_recv(rpchn->rpmsg_dev, rpchn->msg_queue, &src, (char *)msg, msglen, &len, timeout);
    return 0;
}

# rtos\lk_boot\framework\service\rpmsg\rpmsg_channel.c

/* user rx by itself, no internal loop reciever */
status_t rpmsg_channel_recvfrom(struct rpmsg_channel *rpchn, unsigned long *src, char *data, int maxlen, int *len, unsigned long timeout)
{
    
    return rpmsg_queue_recv(rpchn->rpmsg_dev, rpchn->msg_queue, src,
                    data, maxlen, len, timeout);
}

int rpmsg_queue_recv(struct rpmsg_lite_instance *rpmsg_lite_dev, rpmsg_queue_handle q,
                     unsigned long *src, char *data, int maxlen,int *len, unsigned long timeout)
{
    
    rpmsg_queue_rx_cb_data_t msg = {
    0};
    /* Get an element out of the message queue for the selected endpoint */
    if (env_get_queue((void *)q, &msg, timeout)){
    
        env_memcpy(data, msg.data, msg.len);
        /* Return used buffers. */
        rpmsg_lite_release_rx_buffer(rpmsg_lite_dev, msg.data);
    }
}
int rpmsg_lite_release_rx_buffer(struct rpmsg_lite_instance *rpmsg_lite_dev, void *rxbuf)
{
    
    struct rpmsg_std_msg *rpmsg_msg;
    struct rpmsg_hdr_reserved *reserved = RL_NULL;

#if defined(RL_DEBUG_CHECK_BUFFERS) && (RL_DEBUG_CHECK_BUFFERS == 1)
    RL_ASSERT(
            /* master check */
            ((rpmsg_lite_dev->vq_ops == &master_vq_ops) &&
            (rxbuf >= (void *)rpmsg_lite_dev->sh_mem_base) &&
            (rxbuf <= (void *)(rpmsg_lite_dev->sh_mem_base + (RL_BUFFER_COUNT*RL_BUFFER_SIZE)))) ||

            /* remote check */
            ((rpmsg_lite_dev->vq_ops == &remote_vq_ops) &&
            (rxbuf >= (void *)(rpmsg_lite_dev->sh_mem_base + (RL_BUFFER_COUNT*RL_BUFFER_SIZE))) &&
            (rxbuf <= (void *)(rpmsg_lite_dev->sh_mem_base + (2*RL_BUFFER_COUNT*RL_BUFFER_SIZE))))
        )
#endif

    rpmsg_msg = RPMSG_STD_MSG_FROM_BUF(rxbuf);

    /* Get the pointer to the reserved field that contains buffer size and the index */
    reserved = (struct rpmsg_hdr_reserved *)&rpmsg_msg->hdr.reserved;

    /* Return used buffer, with total length (header length + buffer size). */
    rpmsg_lite_dev->vq_ops->vq_rx_free(rpmsg_lite_dev->rvq, rpmsg_msg, (unsigned long)virtqueue_get_buffer_length(rpmsg_lite_dev->rvq, reserved->idx), reserved->idx);
    
    return RL_SUCCESS;
}

/* Interface used in case this processor is MASTER */
static const struct virtqueue_ops master_vq_ops = {
    
    vq_tx_master, vq_tx_alloc_master, vq_rx_master, vq_rx_free_master,
};

/* Interface used in case this processor is REMOTE */
static const struct virtqueue_ops remote_vq_ops = {
    
    vq_tx_remote, vq_tx_alloc_remote, vq_rx_remote, vq_rx_free_remote,
};

static void *vq_rx_master(struct virtqueue *rvq, unsigned long *len, unsigned short *idx)
{
    
    return virtqueue_get_buffer(rvq, (uint32_t *)len, idx);
}


1.12 QNX API

from X9 PTG3.9 Publish start , Inter core communication is officially supported QNX, Reference code echo_test.c Through the and Linux RPMSG Similar interfaces interact with other cores .
at present QNX API Support character type and traditional type .

  1. Character device type :
    compatible POSIX File operations , adopt open, close, write,read Interface opens inter core communication endpoint.

  2. Traditional type :
    adopt RPMSG Peculiar API,buffer Processing data receiving and sending , It can provide lower level developers with higher fine-grained control .
    Character type interface is the encapsulation of traditional types , Convenient for users , For ordinary users , Character device types are simpler and more convenient , Recommended .
    For detailed use, please refer to Semidrive QNX SDK Incidental echo_test Program .

And Linux The slightly different user layer interface is ,QNX On the system RPMSG The equipment name is modified to /dev/rpmsgX In the form of .
X It's a decimal number , The correspondence is as follows :

/dev/rpmsgXRPSMG The physical layer Peer processor remarks
0VirtioSafety
1VirtioSecure
2VirtioMP core be used for G9 series
3IPCCAP1 IVI be used for X9HP QNX Meter
10IPCCSafety
11IPCCSecure
12IPCCMP Core

For the time being, we will not engage in QNX , therefore QNX Code , Just don't look at it first , Interested brothers , Go by yourself ,

I can tell you ,QNX710 Mailbox Relevant codes are located in :
QNX710\bsp\BSP_SemiDrive_X9H_br-710_be-710_SVN954750_JBN32\src\hardware\support\semidrive

The screenshot is as follows :
 Insert picture description here

Learning words , Suggestion or from test Start to look at , Code is located :
QNX710\bsp\BSP_SemiDrive_X9H_br-710_be-710_SVN954750_JBN32\src\hardware\support\semidrive\rpmsg\test\rpmsg-test.c



of MailBox Code , Read some , The principles are quite different , Continue to look at It will take a long time to finish .
But the current project , I guess I have the most 10 Days preparation time , It's too late to read the code slowly .

of MailBox , In principle , I've almost understood , It doesn't make much sense to keep looking at the code ,
want With specific drive , Write according to the reference example , The basic problem is not big .

Next , Let's go on with the analysis RTOS After starting the process .

原网站

版权声明
本文为[CielleeX]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121335506000.html