diff options
17 files changed, 1716 insertions, 155 deletions
diff --git a/middleware/multicore/open-amp/common/hil/hil.c b/middleware/multicore/open-amp/common/hil/hil.c index e4b0bf3..31e1a71 100644 --- a/middleware/multicore/open-amp/common/hil/hil.c +++ b/middleware/multicore/open-amp/common/hil/hil.c @@ -291,7 +291,7 @@ int hil_enable_vring_notifications(int vring_index, struct virtqueue *vq) { vring_hw->vq = vq; if (proc_hw->ops->enable_interrupt) { - proc_hw->ops->enable_interrupt(vring_hw); /*_enable_interrupt*/ + proc_hw->ops->enable_interrupt(vring_hw); } return 0; @@ -312,7 +312,7 @@ void hil_vring_notify(struct virtqueue *vq) { struct proc_vring *vring_hw = &proc_hw->vdev.vring_info[vq->vq_queue_index]; if (proc_hw->ops->notify) { - proc_hw->ops->notify(proc_hw->cpu_id, &vring_hw->intr_info); /*_notify*/ + proc_hw->ops->notify(proc_hw->cpu_id, &vring_hw->intr_info); } } diff --git a/middleware/multicore/open-amp/common/shm/sh_mem.h b/middleware/multicore/open-amp/common/shm/sh_mem.h index 4ba830b..abfe6b7 100644 --- a/middleware/multicore/open-amp/common/shm/sh_mem.h +++ b/middleware/multicore/open-amp/common/shm/sh_mem.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -75,7 +76,7 @@ struct sh_mem_pool { int total_buffs; int used_buffs; int bmp_size; - unsigned long bitmap[0]; + unsigned long bitmap[1]; }; /* APIs */ diff --git a/middleware/multicore/open-amp/porting/env/env.h b/middleware/multicore/open-amp/porting/env/env.h index 05b9e62..c01e227 100644 --- a/middleware/multicore/open-amp/porting/env/env.h +++ b/middleware/multicore/open-amp/porting/env/env.h @@ -68,6 +68,10 @@ * env_sleep_msec * env_disable_interrupts * env_restore_interrupts + * env_create_queue + * env_delete_queue + * env_put_queue + * env_get_queue * **************************************************************************/ #ifndef _ENV_H_ @@ -428,4 +432,55 @@ void env_disable_cache(void); typedef void LOCK; +/** + * env_create_queue + * + * Creates a message queue. + * + * @param queue - pointer to created queue + * @param length - maximum number of elements in the queue + * @param item_size - queue element size in bytes + * + * @return - status of function execution + */ +int env_create_queue(void **queue, int length , int element_size); + +/** + * env_delete_queue + * + * Deletes the message queue. + * + * @param queue - queue to delete + */ + +void env_delete_queue(void *queue); + +/** + * env_put_queue + * + * Put an element in a queue. + * + * @param queue - queue to put element in + * @param msg - pointer to the message to be put into the queue + * @param timeout_ms - timeout in ms + * + * @return - status of function execution + */ + +int env_put_queue(void *queue, void* msg, int timeout_ms); + +/** + * env_get_queue + * + * Get an element out of a queue. + * + * @param queue - queue to get element from + * @param msg - pointer to a memory to save the message + * @param timeout_ms - timeout in ms + * + * @return - status of function execution + */ + +int env_get_queue(void *queue, void* msg, int timeout_ms); + #endif /* _ENV_H_ */ diff --git a/middleware/multicore/open-amp/porting/env/freertos_env.c b/middleware/multicore/open-amp/porting/env/freertos_env.c index 18c2afc..b54fd6f 100644 --- a/middleware/multicore/open-amp/porting/env/freertos_env.c +++ b/middleware/multicore/open-amp/porting/env/freertos_env.c @@ -44,6 +44,7 @@ #include "env.h" #include "../config/config.h" +#include "semphr.h" #include <stdlib.h> #include <string.h> @@ -59,6 +60,7 @@ /* * function decalaration for platform provided facility */ +extern int platform_in_isr(void); extern void platform_interrupt_enable(void); extern void platform_interrupt_disable(void); @@ -69,6 +71,17 @@ struct isr_info isr_table[ISR_COUNT]; int Intr_Count = 0; /** + * env_in_isr + * + * @returns - true, if currently in ISR + * + */ +int env_in_isr(void) +{ + return platform_in_isr(); +} + +/** * env_init * * Initializes OS/BM environment. @@ -229,7 +242,15 @@ void *env_map_patova(unsigned long address) */ int env_create_mutex(void **lock, int count) { - return 0; + *lock = xSemaphoreCreateCounting(10, count); + if(*lock) + { + return 0; + } + else + { + return -1; + } } /** @@ -240,6 +261,7 @@ int env_create_mutex(void **lock, int count) */ void env_delete_mutex(void *lock) { + vSemaphoreDelete(lock); } /** @@ -253,7 +275,12 @@ void env_delete_mutex(void *lock) */ void env_lock_mutex(void *lock) { - env_disable_interrupts(); + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t)lock; + if(!env_in_isr()) + { + xSemaphoreTake( xSemaphore, portMAX_DELAY); + env_disable_interrupts(); + } } /** @@ -267,7 +294,12 @@ void env_lock_mutex(void *lock) void env_unlock_mutex(void *lock) { - env_restore_interrupts(); + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t)lock; + if(!env_in_isr()) + { + env_restore_interrupts(); + xSemaphoreGive( xSemaphore ); + } } @@ -280,7 +312,7 @@ void env_unlock_mutex(void *lock) */ int env_create_sync_lock(void **lock , int state) { - return 0; + return env_create_mutex(lock, state); /* state=1 .. initially free */ } /** @@ -291,6 +323,8 @@ int env_create_sync_lock(void **lock , int state) */ void env_delete_sync_lock(void *lock) { + if(lock) + env_delete_mutex(lock); } /** @@ -301,6 +335,17 @@ void env_delete_sync_lock(void *lock) */ void env_acquire_sync_lock(void *lock) { + BaseType_t xTaskWokenByReceive = pdFALSE; + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t)lock; + if(env_in_isr()) + { + xSemaphoreTakeFromISR(xSemaphore, &xTaskWokenByReceive); + portEND_SWITCHING_ISR( xTaskWokenByReceive ); + } + else + { + xSemaphoreTake( xSemaphore, portMAX_DELAY); + } } /** @@ -311,6 +356,17 @@ void env_acquire_sync_lock(void *lock) void env_release_sync_lock(void *lock) { + BaseType_t xTaskWokenByReceive = pdFALSE; + SemaphoreHandle_t xSemaphore = (SemaphoreHandle_t)lock; + if(env_in_isr()) + { + xSemaphoreGiveFromISR(xSemaphore, &xTaskWokenByReceive); + portEND_SWITCHING_ISR( xTaskWokenByReceive ); + } + else + { + xSemaphoreGive( xSemaphore); + } } /** @@ -437,7 +493,14 @@ void env_disable_cache() */ unsigned long long env_get_timestamp(void) { - return 0; + if(env_in_isr()) + { + return (unsigned long long) xTaskGetTickCountFromISR(); + } + else + { + return (unsigned long long) xTaskGetTickCount(); + } } /*========================================================= */ @@ -459,3 +522,106 @@ void freertos_env_isr(int vector) { } } } + +/* + * env_create_queue + * + * Creates a message queue. + * + * @param queue - pointer to created queue + * @param length - maximum number of elements in the queue + * @param item_size - queue element size in bytes + * + * @return - status of function execution + */ +int env_create_queue(void **queue, int length , int element_size) +{ + *queue = xQueueCreate(length, element_size); + if(*queue) + { + return 0; + } + else + { + return -1; + } +} + +/** + * env_delete_queue + * + * Deletes the message queue. + * + * @param queue - queue to delete + */ + +void env_delete_queue(void *queue) +{ + vQueueDelete(queue); +} + +/** + * env_put_queue + * + * Put an element in a queue. + * + * @param queue - queue to put element in + * @param msg - pointer to the message to be put into the queue + * @param timeout_ms - timeout in ms + * + * @return - status of function execution + */ + +int env_put_queue(void *queue, void* msg, int timeout_ms) +{ + BaseType_t xHigherPriorityTaskWoken; + if(env_in_isr()) + { + if(xQueueSendFromISR(queue, msg, &xHigherPriorityTaskWoken) == pdPASS) + { + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + return 1; + } + } + else + { + if(xQueueSend(queue, msg, ((portMAX_DELAY == timeout_ms) ? portMAX_DELAY : timeout_ms / portTICK_PERIOD_MS)) == pdPASS) + { + return 1; + } + } + return 0; +} + +/** + * env_get_queue + * + * Get an element out of a queue. + * + * @param queue - queue to get element from + * @param msg - pointer to a memory to save the message + * @param timeout_ms - timeout in ms + * + * @return - status of function execution + */ + +int env_get_queue(void *queue, void* msg, int timeout_ms) +{ + BaseType_t xHigherPriorityTaskWoken; + if(env_in_isr()) + { + if(xQueueReceiveFromISR(queue, msg, &xHigherPriorityTaskWoken) == pdPASS) + { + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + return 1; + } + } + else + { + if(xQueueReceive(queue, msg, ((portMAX_DELAY == timeout_ms) ? portMAX_DELAY : timeout_ms / portTICK_PERIOD_MS)) == pdPASS) + { + return 1; + } + } + return 0; +} diff --git a/middleware/multicore/open-amp/porting/vf6xx_m4/platform.c b/middleware/multicore/open-amp/porting/vf6xx_m4/platform.c index 6c9fffa..46287ad 100644 --- a/middleware/multicore/open-amp/porting/vf6xx_m4/platform.c +++ b/middleware/multicore/open-amp/porting/vf6xx_m4/platform.c @@ -95,6 +95,11 @@ void _shutdown_cpu(int cpu_id) { } +int platform_in_isr(void) +{ + return ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0); +} + void platform_isr(int vect_id, void *data) { hil_isr(((struct proc_vring *) data)); diff --git a/middleware/multicore/open-amp/rpmsg/remote_device.c b/middleware/multicore/open-amp/rpmsg/remote_device.c index f07faab..4635a50 100644 --- a/middleware/multicore/open-amp/rpmsg/remote_device.c +++ b/middleware/multicore/open-amp/rpmsg/remote_device.c @@ -2,6 +2,7 @@ * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -132,18 +133,15 @@ int rpmsg_rdev_init(struct remote_device **rdev, int dev_id, int role, virt_dev->device = proc; virt_dev->func = &rpmsg_rdev_config_ops; if (virt_dev->func->set_features != RPMSG_NULL) { - virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures); /* rpmsg_rdev_set_feature */ + virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures); } - /* - * Linux don't use this way to manage memory pool - */ if (rdev_loc->role == RPMSG_REMOTE) { /* * Since device is RPMSG Remote so we need to manage the * shared buffers. Create shared memory pool to handle buffers. */ - shm = hil_get_shm_info(proc); /*proc_table*/ + shm = hil_get_shm_info(proc); rdev_loc->mem_pool = sh_mem_create_pool(shm->start_addr, shm->size, RPMSG_BUFFER_SIZE); @@ -195,7 +193,7 @@ void rpmsg_rdev_deinit(struct remote_device *rdev) { /* Delete default endpoint for channel */ if (rp_chnl->rp_ept) { - rpmsg_destroy_ept(rp_chnl->rp_ept); + rpmsg_destroy_ept(rp_chnl->rp_ept); } _rpmsg_delete_channel(rp_chnl); @@ -342,7 +340,7 @@ int rpmsg_rdev_notify(struct remote_device *rdev) { * communication. */ if (!status) - virtqueue_kick(rdev->rvq); /*will triggle rpmsg_tx_callback in the REMOTE side*/ + virtqueue_kick(rdev->rvq); } else { status = hil_set_status(rdev->proc); @@ -372,24 +370,22 @@ int rpmsg_rdev_init_channels(struct remote_device *rdev) { if (rdev->role == RPMSG_MASTER) { - chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls); /*proc_table*/ + chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls); for (idx = 0; idx < num_chnls; idx++) { rp_chnl = _rpmsg_create_channel(rdev, chnl_info[idx].name, 0x00, - RPMSG_NS_EPT_ADDR); /*the channel is put to "rp_channels" field of remote_device data structure*/ + RPMSG_NS_EPT_ADDR); if (!rp_chnl) { return RPMSG_ERR_NO_MEM; } - rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, - RPMSG_ADDR_ANY); /*the endpoint is put ot "rp_endpoints" field of remote_device data structure*/ + rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, RPMSG_ADDR_ANY); if (!rp_chnl->rp_ept) { return RPMSG_ERR_NO_MEM; } - rp_chnl->src = rp_chnl->rp_ept->addr; /*channel source is the default endpoint address, destination is NS endpoint address*/ - + rp_chnl->src = rp_chnl->rp_ept->addr; } } @@ -402,7 +398,7 @@ int rpmsg_rdev_init_channels(struct remote_device *rdev) { * by the virtio.h file. *------------------------------------------------------------------------ */ -int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, // invoked by virtio_device->create_virtqueues +int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, const char *names[], vq_callback *callbacks[], struct virtqueue *vqs_[]) { struct remote_device *rdev; @@ -419,7 +415,6 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, vring_table = hil_get_vring_info(&rdev->proc->vdev, &num_vrings); - if (num_vrings > nvqs) { return RPMSG_ERR_MAX_VQ; } @@ -427,13 +422,7 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, /* Create virtqueue for each vring. */ for (idx = 0; idx < num_vrings; idx++) { - INIT_VRING_ALLOC_INFO( ring_info, vring_table[idx]); /*ring_info comes from proc_table*/ - - /* - * - phy_addr - * - align - * - num_descs - */ + INIT_VRING_ALLOC_INFO( ring_info, vring_table[idx]); if (rdev->role == RPMSG_REMOTE) { env_memset((void*) ring_info.phy_addr, 0x00, @@ -441,9 +430,6 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, vring_table[idx].align)); } - /* - * created virtqueue should be in vqs[0], vqs[1] - */ status = virtqueue_create(dev, idx, (char *) names[idx], &ring_info, callbacks[idx], hil_vring_notify, &vqs[idx]); @@ -454,11 +440,6 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, } //FIXME - a better way to handle this , tx for master is rx for remote and vice versa. - /* - * MASTER REMOTE - * vqs[0] RX TX - * vqs[1] TX RX - */ if (rdev->role == RPMSG_MASTER) { rdev->tvq = vqs[0]; rdev->rvq = vqs[1]; @@ -472,7 +453,6 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, && (idx < rdev->mem_pool->total_buffs / 2)); idx++) { - /* Initialize TX virtqueue buffers for remote device */ buffer = sh_mem_get_buffer(rdev->mem_pool); @@ -485,7 +465,7 @@ int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, node.next = RPMSG_NULL; env_memset(buffer, 0x00, RPMSG_BUFFER_SIZE); - status = virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); /* this is where vq_ring.avial.idx is updated by "vq_ring_update_avail"*/ + status = virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); if (status != RPMSG_SUCCESS) { return status; @@ -509,7 +489,7 @@ uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev) { } void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature) { - dev->features |= feature; /*refer to name service endpoint creation logic in the caller rpmsg_start_ipc*/ + dev->features |= feature; } uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev, diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.c b/middleware/multicore/open-amp/rpmsg/rpmsg.c index 66db1ad..f2f66f2 100644 --- a/middleware/multicore/open-amp/rpmsg/rpmsg.c +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -44,7 +45,7 @@ * RPMSG driver represents each processor/core to which it communicates with * remote_device control block. * Each remote device(processor) defines its role in the communication i.e - * whether it is RPMSG Master or Remote. If the device (processor) to which + * whether it is RPMSG Master or Remote. If the device(processor) to which * driver is talking is RPMSG master then RPMSG driver implicitly behaves as * Remote and vice versa. * RPMSG Master is responsible for initiating communications with the Remote @@ -80,7 +81,6 @@ int rpmsg_init(int dev_id, struct remote_device **rdev, rpmsg_rx_cb_t default_cb, int role) { int status; - PRINTF("init M4 as %s\r\n", role?"REMOTE":"MASTER"); /* Initialize IPC environment */ status = env_init(); if (status == RPMSG_SUCCESS) { @@ -194,6 +194,7 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, unsigned long src, rp_hdr->dst = dst; rp_hdr->src = src; rp_hdr->len = size; + rp_hdr->flags = 0; /* Copy data to rpmsg buffer. */ env_memcpy(rp_hdr->data, data, size); @@ -214,6 +215,21 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, unsigned long src, /* Do cleanup in case of error.*/ if (status != RPMSG_SUCCESS) { rpmsg_free_buffer(rdev, buffer); + // in case of error ask master + // to release buffer + if (rdev->role == RPMSG_MASTER && status == RPMSG_ERR_BUFF_SIZE) + { + rp_hdr = (struct rpmsg_hdr *) buffer; + rp_hdr->dst = dst; + rp_hdr->src = src; + rp_hdr->len = 0; + rp_hdr->flags = RPMSG_DROP_HDR_FLAG; + int tmp_status = rpmsg_enqueue_buffer(rdev, buffer, buff_len, idx); + if (tmp_status == RPMSG_SUCCESS) { + /* Let the other side know that there is a job to process. */ + virtqueue_kick(rdev->tvq); + } + } } return status; @@ -283,15 +299,6 @@ int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl) { struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl, rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { - /* - * Note : When calling rpmsg_create_ept to a channel, the endpoint - * is put into info field of remote_device, not the channel - * - * - * CHANNEL ---> RDEV ---> CHANNELs - * - * EPT ---> RDEV ---> EPTs - */ struct remote_device *rdev = RPMSG_NULL; struct rpmsg_endpoint *rp_ept = RPMSG_NULL; @@ -365,8 +372,7 @@ struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, } /* Create default endpoint for the channel */ - rp_ept = rpmsg_create_ept(rp_chnl , rdev->default_cb, rdev, - RPMSG_ADDR_ANY); + rp_ept = rpmsg_create_ept(rp_chnl , rdev->default_cb, rdev, RPMSG_ADDR_ANY); if (!rp_ept) { _rpmsg_delete_channel(rp_chnl); diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.h b/middleware/multicore/open-amp/rpmsg/rpmsg.h index 746b792..b0bc9b7 100644 --- a/middleware/multicore/open-amp/rpmsg/rpmsg.h +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.h @@ -4,6 +4,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,6 +42,9 @@ #define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ #define RPMSG_NAME_SIZE 32 +#if defined(__IAR_SYSTEMS_ICC__) +__packed +#endif /** * struct rpmsg_hdr - common header for all rpmsg messages * @src: source address @@ -53,14 +57,33 @@ * Every message sent(/received) on the rpmsg bus begins with this header. */ struct rpmsg_hdr { - unsigned long src; - unsigned long dst; - unsigned long reserved; - unsigned short len; - unsigned short flags; - unsigned char data[0]; -} /*__attribute__((packed))*/; + unsigned long src; + unsigned long dst; + unsigned long reserved; + unsigned short len; + unsigned short flags; + unsigned char data[1]; +#if defined(__IAR_SYSTEMS_ICC__) +}; +#else +}__attribute__((packed)); +#endif + +#define RPMSG_DROP_HDR_FLAG 1 + + +struct rpmsg_hdr_reserved +{ + short int idx; + short int totlen; +}; + +#define RPMSG_BUF_HELD (1U << 31) + +#if defined(__IAR_SYSTEMS_ICC__) +__packed +#endif /** * struct rpmsg_ns_msg - dynamic name service announcement message * @name: name of remote service that is published @@ -77,7 +100,11 @@ struct rpmsg_ns_msg { char name[RPMSG_NAME_SIZE]; unsigned long addr; unsigned long flags; -} /*__attribute__((packed))*/; +#if defined(__IAR_SYSTEMS_ICC__) +}; +#else +}__attribute__((packed)); +#endif /** * enum rpmsg_ns_flags - dynamic name service announcement flags @@ -262,8 +289,9 @@ static inline int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len) { - if (!rpdev || !data) + if (!rpdev || !data) { return RPMSG_ERR_PARAM; + } return rpmsg_send_offchannel_raw(rpdev, rpdev->src, rpdev->dst, (char *)data, len, RPMSG_FALSE); } @@ -399,13 +427,4 @@ struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, char *nam */ void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl); -/** - * - * rpmsg_handler - * - * Provide platform specific interrupt handler to application layer - * - */ -void rpmsg_handler(void); - #endif /* _RPMSG_H_ */ diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.c b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c index c8cb82f..c7c20d7 100644 --- a/middleware/multicore/open-amp/rpmsg/rpmsg_core.c +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c @@ -2,6 +2,7 @@ * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -46,6 +47,7 @@ * **************************************************************************/ #include "rpmsg.h" +#include <assert.h> /* Internal functions */ static void rpmsg_rx_callback(struct virtqueue *vq); @@ -75,12 +77,6 @@ int rpmsg_start_ipc(struct remote_device *rdev) { virt_dev = &rdev->virt_dev; /* Initialize names and callbacks based on the device role */ - /* - * virtqueue[0] virtqueue[1] - * MASTER "tx_vq", "rpmsg_tx_callback" "rx_vq", "rpmsg_rx_callback" - * - * REMOTE "rx_vq", "rpmsg_rx_callback" "tx_vq", "rpmsg_tx_callback" - */ if (rdev->role == RPMSG_MASTER) { vq_names[0] = "tx_vq"; vq_names[1] = "rx_vq"; @@ -94,30 +90,21 @@ int rpmsg_start_ipc(struct remote_device *rdev) { } /* Create virtqueues for remote device */ - /* - * the 2 created virtqueues are assigned to rdev's tvq and rvq respectively - * regarding on the MATER/REMOTE role - * - * REMOTE: proc_table.vring_info[0] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[0]" - * proc_table.vring_info[1] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[1]" - * MASTER: proc_table.vring_info[0] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[0]" - * proc_table.vring_info[1] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[1]" - */ - status = virt_dev->func->create_virtqueues(virt_dev, 0, /*rpmsg_rdev_create_virtqueues*/ + status = virt_dev->func->create_virtqueues(virt_dev, 0, RPMSG_MAX_VQ_PER_RDEV, vq_names, callback, RPMSG_NULL); if (status != RPMSG_SUCCESS) { return status; } - dev_features = virt_dev->func->get_features(virt_dev); /*rpmsg_rdev_get_feature*/ + dev_features = virt_dev->func->get_features(virt_dev); /* * Create name service announcement endpoint if device supports name * service announcement feature. */ - if ((dev_features & (1<<VIRTIO_RPMSG_F_NS))) { - rdev->support_ns = RPMSG_TRUE; - ns_ept = _create_endpoint(rdev, rpmsg_ns_callback, rdev, /* Is this necessary for a remote? */ + if ((dev_features & (1 << VIRTIO_RPMSG_F_NS))) { + rdev->support_ns = RPMSG_TRUE; + ns_ept = _create_endpoint(rdev, rpmsg_ns_callback, rdev, RPMSG_NS_EPT_ADDR); if (!ns_ept) { return RPMSG_ERR_NO_MEM; @@ -177,7 +164,7 @@ struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, } node->data = rp_chnl; env_lock_mutex(rdev->lock); - add_to_list(&rdev->rp_channels , node); + add_to_list(&rdev->rp_channels, node); env_unlock_mutex(rdev->lock); } @@ -231,6 +218,7 @@ struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, if (!rp_ept) { return RPMSG_NULL ; } + env_memset(rp_ept, 0x00, sizeof(struct rpmsg_endpoint)); node = env_allocate_memory(sizeof(struct llist)); if (!node) { @@ -325,7 +313,10 @@ void rpmsg_send_ns_message(struct remote_device *rdev, /* Get Tx buffer. */ rp_hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &len, &idx); if (!rp_hdr) + { + env_unlock_mutex(rdev->lock); return; + } /* Fill out name service data. */ rp_hdr->dst = RPMSG_NS_EPT_ADDR; @@ -369,10 +360,8 @@ int rpmsg_enqueue_buffer(struct remote_device *rdev, void *buffer, node.prev = RPMSG_NULL; if (rdev->role == RPMSG_REMOTE) { - /*MASTER*/ status = virtqueue_add_buffer(rdev->tvq, &node, 0, 1, buffer); } else { - /*REMOTE*/ status = virtqueue_add_consumed_buffer(rdev->tvq, idx, len); } @@ -401,10 +390,8 @@ void rpmsg_return_buffer(struct remote_device *rdev, void *buffer, node.prev = RPMSG_NULL; if (rdev->role == RPMSG_REMOTE) { - /*master*/ virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); } else { - /*remote*/ virtqueue_add_consumed_buffer(rdev->rvq, idx, len); } } @@ -425,19 +412,16 @@ void *rpmsg_get_tx_buffer(struct remote_device *rdev, int *len, void *data; if (rdev->role == RPMSG_REMOTE) { - /* MASTER */ data = virtqueue_get_buffer(rdev->tvq, (uint32_t *) len); if (data == RPMSG_NULL) { - /*Here is why Master don't need to pre link memory to vring*/ data = sh_mem_get_buffer(rdev->mem_pool); *len = RPMSG_BUFFER_SIZE; } } else { - /* REMOTE */ data = virtqueue_get_available_buffer(rdev->tvq, idx, (uint32_t *) len); } - return ((void *) env_map_vatopa(data)); + return ((void*) env_map_vatopa(data)); } /** @@ -457,11 +441,9 @@ void *rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, void *data; if (rdev->role == RPMSG_REMOTE) { - /*MASTER*/ - data = virtqueue_get_buffer(rdev->rvq, (uint32_t *)len); + data = virtqueue_get_buffer(rdev->rvq, (uint32_t*)len); } else { - /*REMOTE*/ - data = virtqueue_get_available_buffer(rdev->rvq, idx, (uint32_t *)len); + data = virtqueue_get_available_buffer(rdev->rvq, idx, (uint32_t*)len); } return ((void *) env_map_vatopa(data)); } @@ -477,7 +459,7 @@ void *rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, */ void rpmsg_free_buffer(struct remote_device *rdev, void *buffer) { if (rdev->role == RPMSG_REMOTE) { - sh_mem_free_buffer(rdev->mem_pool, buffer); + sh_mem_free_buffer(buffer, rdev->mem_pool); } } @@ -512,20 +494,16 @@ static void rpmsg_tx_callback(struct virtqueue *vq) { */ while (chnl_hd != RPMSG_NULL) { rp_chnl = (struct rpmsg_channel *) chnl_hd->data; - if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { - if (rdev->support_ns) { rp_chnl->state = RPMSG_CHNL_STATE_NS; } else { rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; } - if (rp_chnl->state == RPMSG_CHNL_STATE_NS) { rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); } } - chnl_hd = chnl_hd->next; } } @@ -555,16 +533,16 @@ void rpmsg_rx_callback(struct virtqueue *vq) { chnl_hd = rdev->rp_channels; if ((chnl_hd != RPMSG_NULL) && (rdev->role == RPMSG_MASTER)) { - rp_chnl = (struct rpmsg_channel *) chnl_hd->data; - if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { - if (rdev->support_ns) { - rp_chnl->state = RPMSG_CHNL_STATE_NS; - rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); - } else { - rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; - } - return; - } + rp_chnl = (struct rpmsg_channel *) chnl_hd->data; + if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { + if (rdev->support_ns) { + rp_chnl->state = RPMSG_CHNL_STATE_NS; + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + } else { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + } + return; + } } env_lock_mutex(rdev->lock); @@ -574,10 +552,12 @@ void rpmsg_rx_callback(struct virtqueue *vq) { env_unlock_mutex(rdev->lock); - while(rp_hdr) { + while(rp_hdr) { + /* Clear 'rp_hdr->reserved' field that is used as 'callback' output */ + rp_hdr->reserved = 0; /* Get the channel node from the remote device channels list. */ - node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); /*in the "rp_endpoints" list, find the node whose addr equal"rp_hdr->dst"*/ + node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); if (!node) /* Fatal error no endpoint for the given dst addr. */ @@ -587,10 +567,6 @@ void rpmsg_rx_callback(struct virtqueue *vq) { rp_chnl = rp_ept->rp_chnl; - /* - * Linux will not send the null message, so the first message not only - * update the state machine, and the callback is called as well - */ if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) { /* First message from RPMSG Master, update channel * destination address and state */ @@ -602,18 +578,32 @@ void rpmsg_rx_callback(struct virtqueue *vq) { /* Notify channel creation to application */ if (rdev->channel_created) { - rdev->channel_created(rp_chnl); /* assigned by rpmsg_rdev_init*/ + rdev->channel_created(rp_chnl); + } + } else if(len <= 0xFFFF) { + if (!(rp_hdr->flags & RPMSG_DROP_HDR_FLAG)) + { + rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, + rp_ept->priv, rp_hdr->src); } + } else { + /* Any message with totlen > 65535 are dropped, no way to notify the user about it */ } - rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv, // for NS message, this will triggle rpmsg_ns_callback /*not the case*/ - rp_hdr->src); // for none NS message, this will triggle APP registered callback "rpmsg_read_cb" - env_lock_mutex(rdev->lock); - - /* Return used buffers. */ - rpmsg_return_buffer(rdev, rp_hdr, len, idx); - + /* Check whether callback wants to hold buffer */ + if (rp_hdr->reserved & RPMSG_BUF_HELD) + { + /* 'rp_hdr->reserved' field is now used as storage for + * 'idx' and 'len' to release buffer later */ + ((struct rpmsg_hdr_reserved*)&rp_hdr->reserved)->idx = idx; + ((struct rpmsg_hdr_reserved*)&rp_hdr->reserved)->totlen = len; + } + else + { + /* Return used buffers. */ + rpmsg_return_buffer(rdev, rp_hdr, len, idx); + } rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); env_unlock_mutex(rdev->lock); } @@ -631,7 +621,7 @@ void rpmsg_rx_callback(struct virtqueue *vq) { * @param priv - any private data * @param src - source address * - * @return - none + * @return void */ void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, void *data, int len, void *priv, unsigned long src) { @@ -656,17 +646,18 @@ void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, void *data, int len, if (rdev->channel_destroyed) { rdev->channel_destroyed(rp_chnl); } + rpmsg_destroy_ept(rp_chnl->rp_ept); _rpmsg_delete_channel(rp_chnl); } } else { - /*RPMSG_NS_CREATE*/ rp_chnl = _rpmsg_create_channel(rdev, ns_msg->name, 0x00, ns_msg->addr); if (rp_chnl) { rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; /* Create default endpoint for channel */ rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, RPMSG_ADDR_ANY); + if (rp_chnl->rp_ept) { rp_chnl->src = rp_chnl->rp_ept->addr; /* @@ -676,7 +667,7 @@ void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, void *data, int len, * message without waiting for any application level message from * master. */ - rpmsg_send(rp_chnl,data,len); /*Is this necessary? Infinite Echo Back ? */ + rpmsg_send(rp_chnl,data,len); if (rdev->channel_created) { rdev->channel_created(rp_chnl); } @@ -704,7 +695,7 @@ int rpmsg_get_address(unsigned long *bitmap, int size) { tmp32 = get_first_zero_bit(bitmap[i]); if (tmp32 < 32) { - addr = tmp32 + i + 1; /*This is strange*/ + addr = tmp32 + (i*32); bitmap[i] |= (1 << tmp32); break; } diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.h b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h index e70fd5c..10f4d44 100644 --- a/middleware/multicore/open-amp/rpmsg/rpmsg_core.h +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -31,6 +32,7 @@ #ifndef _RPMSG_CORE_H_ #define _RPMSG_CORE_H_ +#include <stddef.h> #include "../porting/env/env.h" #include "../virtio/virtio.h" #include "../common/hil/hil.h" @@ -78,6 +80,9 @@ #define RPMSG_ERR_DEV_ID (RPMSG_ERRORS_BASE - 7) #define RPMSG_ERR_DEV_ADDR (RPMSG_ERRORS_BASE - 8) +/* Zero-Copy extension macros */ +#define RPMSG_HDR_FROM_BUF(buf) (struct rpmsg_hdr *)((char*)buf - \ + offsetof(struct rpmsg_hdr, data)) struct rpmsg_channel; typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, unsigned long); typedef void (*rpmsg_chnl_cb_t)(struct rpmsg_channel *rp_chl); diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_ext.c b/middleware/multicore/open-amp/rpmsg/rpmsg_ext.c new file mode 100644 index 0000000..dd6fefb --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_ext.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Freescale Semiconductor 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 HOLDER 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. + */ + +/************************************************************************** + * FILE NAME + * + * rpmsg_ext.c + * + * COMPONENT + * + * OpenAMP + * + * DESCRIPTION + * + * Main file for the RPMSG driver extension. This file implements APIs for + * achieving zero copy received message process and zero copy message + * transmission. + * + **************************************************************************/ +#include "rpmsg_ext.h" + +/*! + * @brief Holds the rx buffer for usage outside the receive callback. + * + * Calling this function prevents the RPMsg receive buffer from being released back to the pool + * of shmem buffers. This API can only be called at rx callback context (rpmsg_rx_cb_t). With this API, + * the application doesn't need to copy the message in rx callback. Instead, the rx buffer base address + * is saved in application context and further processed in application process. After the message + * is processed, the application can release the rx buffer for future reuse in vring by calling the + * rpmsg_release_rx_buffer() function. + * + * @param[in] rpdev The rpmsg channel + * @param[in] rxbuf RX buffer with message payload + * + * @see rpmsg_release_rx_buffer +*/ +void rpmsg_hold_rx_buffer(struct rpmsg_channel *rpdev, void *rxbuf) { + struct rpmsg_hdr *rp_hdr = NULL; + if (!rpdev || !rxbuf) + return; + + rp_hdr = RPMSG_HDR_FROM_BUF(rxbuf); + + /* set held status to keep buffer */ + rp_hdr->reserved |= RPMSG_BUF_HELD; +} + +/*! + * @brief Releases the rx buffer for future reuse in vring. + * + * This API can be called at process context when the message in rx buffer is processed. + * + * @param rpdev - the rpmsg channel + * @param rxbuf - rx buffer with message payload + * + * @see rpmsg_hold_rx_buffer + */ +void rpmsg_release_rx_buffer(struct rpmsg_channel *rpdev, void *rxbuf) { + struct rpmsg_hdr *hdr; + struct remote_device *rdev; + struct rpmsg_hdr_reserved * reserved = NULL; + + if (!rpdev || !rxbuf) + return; + + rdev = rpdev->rdev; + hdr = RPMSG_HDR_FROM_BUF(rxbuf); + + /* Get the pointer to the reserved field that contains buffer size and the index */ + reserved = (struct rpmsg_hdr_reserved*)&hdr->reserved; + + env_lock_mutex(rdev->lock); + + /* Return used buffer, with total length (header length + buffer size). */ + rpmsg_return_buffer(rdev, hdr, (unsigned long)reserved->totlen, reserved->idx); + + env_unlock_mutex(rdev->lock); +} + +/*! + * @brief Allocates the tx buffer for message payload. + * + * This API can only be called at process context to get the tx buffer in vring. By this way, the + * application can directly put its message into the vring tx buffer without copy from an application buffer. + * It is the application responsibility to correctly fill the allocated tx buffer by data and passing correct + * parameters to the rpmsg_send_nocopy() or rpmsg_sendto_nocopy() function to perform data no-copy-send mechanism. + * + * @param[in] rpdev Pointer to rpmsg channel + * @param[in] size Pointer to store tx buffer size + * @param[in] wait Boolean, wait or not for buffer to become available + * + * @return The tx buffer address on success and NULL on failure + * + * @see rpmsg_send_offchannel_nocopy + * @see rpmsg_sendto_nocopy + * @see rpmsg_send_nocopy + */ +void *rpmsg_alloc_tx_buffer(struct rpmsg_channel *rpdev, unsigned long *size, int wait) { + struct rpmsg_hdr *hdr; + struct remote_device *rdev; + unsigned short idx; + int buff_len, tick_count = 0; + + if (!rpdev || !size) + return NULL; + + rdev = rpdev->rdev; + + env_lock_mutex(rdev->lock); + + /* Get tx buffer from vring */ + hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + + env_unlock_mutex(rdev->lock); + + if (!hdr && !wait) { + return NULL; + } else { + while (!hdr) { + /* + * Wait parameter is true - pool the buffer for + * 15 secs as defined by the APIs. + */ + env_sleep_msec(RPMSG_TICKS_PER_INTERVAL); + env_lock_mutex(rdev->lock); + hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + env_unlock_mutex(rdev->lock); + tick_count += RPMSG_TICKS_PER_INTERVAL; + if (tick_count >= (RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL)) { + return NULL; + } + } + + /* Store buffer size and the index into the reserved field to be used when sending */ + ((struct rpmsg_hdr_reserved*)&hdr->reserved)->idx = idx; + ((struct rpmsg_hdr_reserved*)&hdr->reserved)->totlen = buff_len; + + /* Actual data buffer size is vring buffer size minus rpmsg header length */ + *size = buff_len - offsetof(struct rpmsg_hdr, data); + return hdr->data; + } +} + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_alloc_tx_buffer() + * using explicit src/dst addresses. + * + * This function sends txbuf of length len to the remote dst address, + * and uses src as the source address. + * The message will be sent to the remote processor which the rpdev + * channel belongs to. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_send_offchannel_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_send_offchannel_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_send_offchannel_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_release_rx_buffer function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] rpdev The rpmsg channel + * @param[in] src Source address + * @param[in] dst Destination address + * @param[in] txbuf TX buffer with message filled + * @param[in] len Length of payload + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_alloc_tx_buffer + * @see rpmsg_sendto_nocopy + * @see rpmsg_send_nocopy + */ +int rpmsg_send_offchannel_nocopy(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *txbuf, int len) { + struct rpmsg_hdr *hdr; + struct remote_device *rdev; + struct rpmsg_hdr_reserved * reserved = NULL; + int status; + + if (!rpdev || !txbuf) + return RPMSG_ERR_PARAM; + + rdev = rpdev->rdev; + hdr = RPMSG_HDR_FROM_BUF(txbuf); + + /* Initialize RPMSG header. */ + hdr->dst = dst; + hdr->src = src; + hdr->len = len; + hdr->flags = 0; + + /* Get the pointer to the reserved field that contains buffer size and the index */ + reserved = (struct rpmsg_hdr_reserved*)&hdr->reserved; + + env_lock_mutex(rdev->lock); + + status = rpmsg_enqueue_buffer(rdev, hdr, (unsigned long)reserved->totlen, reserved->idx); + if (status == RPMSG_SUCCESS) { + /* Let the other side know that there is a job to process. */ + virtqueue_kick(rdev->tvq); + } + + env_unlock_mutex(rdev->lock); + + return status; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_ext.h b/middleware/multicore/open-amp/rpmsg/rpmsg_ext.h new file mode 100644 index 0000000..546ad99 --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_ext.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Freescale Semiconductor 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 HOLDER 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. + */ + + /************************************************************************** + * FILE NAME + * + * rpmsg_ext.h + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * This file provides RPMsg extension that allows: + * - allocation/release of the virtio tx buffer + * - zero-copy send functionality + * + * DATA STRUCTURES + * + * none + * + * FUNCTIONS + * + * rpmsg_hold_rx_buffer + * rpmsg_release_rx_buffer + * rpmsg_alloc_tx_buffer + * rpmsg_sendto_nocopy + * rpmsg_send_nocopy + * + **************************************************************************/ +#ifndef _RPMSG_EXT_H_ +#define _RPMSG_EXT_H_ + +#include "../rpmsg/rpmsg.h" + +//! @addtogroup rpmsg_ext +//! @{ + +/*! + * @brief Holds the rx buffer for usage outside the receive callback. + * + * Calling this function prevents the RPMsg receive buffer from being released back to the pool + * of shmem buffers. This API can only be called at rx callback context (rpmsg_rx_cb_t). With this API, + * the application doesn't need to copy the message in rx callback. Instead, the rx buffer base address + * is saved in application context and further processed in application process. After the message + * is processed, the application can release the rx buffer for future reuse in vring by calling the + * rpmsg_release_rx_buffer() function. + * + * @param[in] rpdev The rpmsg channel + * @param[in] rxbuf RX buffer with message payload + * + * @see rpmsg_release_rx_buffer +*/ +void rpmsg_hold_rx_buffer(struct rpmsg_channel *rpdev, void *rxbuf); + +/*! + * @brief Releases the rx buffer for future reuse in vring. + * + * This API can be called at process context when the message in rx buffer is processed. + * + * @param rpdev - the rpmsg channel + * @param rxbuf - rx buffer with message payload + * + * @see rpmsg_hold_rx_buffer + */ +void rpmsg_release_rx_buffer(struct rpmsg_channel *rpdev, void *rxbuf); + +/*! + * @brief Allocates the tx buffer for message payload. + * + * This API can only be called at process context to get the tx buffer in vring. By this way, the + * application can directly put its message into the vring tx buffer without copy from an application buffer. + * It is the application responsibility to correctly fill the allocated tx buffer by data and passing correct + * parameters to the rpmsg_send_nocopy() or rpmsg_sendto_nocopy() function to perform data no-copy-send mechanism. + * + * @param[in] rpdev Pointer to rpmsg channel + * @param[in] size Pointer to store tx buffer size + * @param[in] wait Boolean, wait or not for buffer to become available + * + * @return The tx buffer address on success and NULL on failure + * + * @see rpmsg_send_offchannel_nocopy + * @see rpmsg_sendto_nocopy + * @see rpmsg_send_nocopy + */ +void *rpmsg_alloc_tx_buffer(struct rpmsg_channel *rpdev, unsigned long *size, int wait); + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_alloc_tx_buffer() + * using explicit src/dst addresses. + * + * This function sends txbuf of length len to the remote dst address, + * and uses src as the source address. + * The message will be sent to the remote processor which the rpdev + * channel belongs to. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_send_offchannel_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_send_offchannel_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_send_offchannel_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_release_rx_buffer function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] rpdev The rpmsg channel + * @param[in] src Source address + * @param[in] dst Destination address + * @param[in] txbuf TX buffer with message filled + * @param[in] len Length of payload + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_alloc_tx_buffer + * @see rpmsg_sendto_nocopy + * @see rpmsg_send_nocopy + */ +int rpmsg_send_offchannel_nocopy(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *txbuf, int len); + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_alloc_tx_buffer() + * across to the remote processor, specify dst. + * + * This function sends txbuf of length len to the remote dst address. + * The message will be sent to the remote processor which the rpdev + * channel belongs to, using rpdev's source address. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_sendto_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_sendto_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_sendto_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_release_rx_buffer function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] rpdev The rpmsg channel + * @param[in] txbuf TX buffer with message filled + * @param[in] len Length of payload + * @param[in] dst Destination address + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_alloc_tx_buffer + * @see rpmsg_send_offchannel_nocopy + * @see rpmsg_send_nocopy + */ +static inline +int rpmsg_sendto_nocopy(struct rpmsg_channel *rpdev, void *txbuf, int len, unsigned long dst) +{ + return rpmsg_send_offchannel_nocopy(rpdev, rpdev->src, dst, txbuf, len); +} + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_alloc_tx_buffer() + * across to the remote processor. + * + * This function sends txbuf of length len on the rpdev channel. + * The message will be sent to the remote processor which the rpdev + * channel belongs to, using rpdev's source and destination addresses. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_send_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_send_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_send_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_release_rx_buffer function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] rpdev The rpmsg channel + * @param[in] txbuf TX buffer with message filled + * @param[in] len Length of payload + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_alloc_tx_buffer + * @see rpmsg_send_offchannel_nocopy + * @see rpmsg_sendto_nocopy + */ +static inline +int rpmsg_send_nocopy(struct rpmsg_channel *rpdev, void *txbuf, int len) +{ + return rpmsg_send_offchannel_nocopy(rpdev, rpdev->src, rpdev->dst, txbuf, len); +} + +//! @} + + +#endif /* _RPMSG_EXT_H_ */ diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.c b/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.c new file mode 100644 index 0000000..eff0d67 --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Freescale Semiconductor 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 HOLDER 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. + */ + +/************************************************************************** + * FILE NAME + * + * rpmsg_rtos.c + * + * COMPONENT + * + * OpenAMP + * + * DESCRIPTION + * + * This file provides RTOS adaptation layer that allows: + * - handling of received messages outside the interrupt context + * - the implementation of blocking API for the RPMsg receive side + * - provides zero-copy receive functionality + * - provides zero-copy send functionality + * + **************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <assert.h> +#include "rpmsg.h" +#include "rpmsg_ext.h" +#include "rpmsg_rtos.h" + +#define RPMSG_RECV_NOCOPY_CHECK_PTRS (0) + +typedef struct +{ + unsigned long src; + void* data; + short int len; +} rpmsg_callback_message_t; + +#if RPMSG_RECV_NOCOPY_CHECK_PTRS + +typedef struct rpmsg_ptr_llist +{ + void* ptr; + struct rpmsg_ptr_llist* next; + +} rpmsg_ptr_llist_t; + +static rpmsg_ptr_llist_t* rpmsg_in_app_buffers_head = NULL; +static rpmsg_ptr_llist_t* rpmsg_in_app_buffers_tail = NULL; + +#endif + +static struct rpmsg_channel *default_chnl = NULL; +static void *default_q = NULL; +static void *callback_sync = NULL; +static int rpmsg_queue_size; + +extern int platform_get_processor_info(struct hil_proc *proc, int cpu_id); + +/* This callback gets invoked when the remote chanl is created */ +static void rpmsg_channel_created_rtos(struct rpmsg_channel *rp_chnl) +{ + assert(rp_chnl && rp_chnl->rp_ept); + /* message queue for channel default endpoint should be available */ + assert(default_q); + rp_chnl->rp_ept->priv = default_q; + + default_chnl = rp_chnl; + + /* Unblock the task by releasing the semaphore. */ + env_release_sync_lock(callback_sync); +} + +/* This callback gets invoked when the remote channel is deleted */ +static void rpmsg_channel_deleted_rtos(struct rpmsg_channel *rp_chnl) +{ + assert(rp_chnl); + + default_chnl = NULL; +} + +/** + * rpmsg_read_rtos_cb + * + * This is the RPMsg receive callback function used in an RTOS environment. + * + * @param rp_chnl - pointer to the RPMsg channel on which data is received + * @param data - pointer to the buffer containing received data + * @param len - size of data received, in bytes + * @param priv - private data provided during endpoint creation + * @param src - pointer to address of the endpoint from which data is received + * + * @return void + */ +static void rpmsg_read_rtos_cb(struct rpmsg_channel *rp_chnl, void *data, int len, + void * priv, unsigned long src) +{ + rpmsg_callback_message_t msg; + + assert(priv); + assert(rp_chnl); + + msg.data = data; + msg.len = len; + msg.src = src; + + /* if message is successfully added into queue then hold rpmsg buffer */ + if(env_put_queue(priv, &msg, 0)) + { + /* hold the rx buffer */ + rpmsg_hold_rx_buffer(rp_chnl, data); + } +} + +/*! + * @brief + * This function allocates and initializes the rpmsg driver resources for + * given device ID (cpu id). The successful return from this function leaves + * fully enabled IPC link. RTOS aware version. + * + * @param[in] dev_id Remote device for which driver is to be initialized + * @param[out] rdev Pointer to newly created remote device + * @param[in] role Role of the other device, Master or Remote + * @param[out] def_chnl Pointer to rpmsg channel + * + * @return Status of function execution + * + * @see rpmsg_rtos_deinit + */ +int rpmsg_rtos_init(int dev_id, struct remote_device **rdev, int role, struct rpmsg_channel **def_chnl) +{ + int status; + struct hil_proc proc; + + /* single instance allowed! */ + if(callback_sync != NULL) + { + return RPMSG_ERR_NO_MEM; + } + + /* get HW specific info */ + status = platform_get_processor_info(&proc, dev_id); + if(status) + { + return RPMSG_ERR_DEV_ID; + } + + /* create synchronization object used during the initialization process */ + status = env_create_sync_lock(&callback_sync, 0); + if((status) || (callback_sync == NULL)) + { + return RPMSG_ERR_NO_MEM; + } + + /* get rpmsg vring rx buffer count */ + rpmsg_queue_size = role == RPMSG_MASTER ? proc.vdev.vring_info[1].num_descs : proc.vdev.vring_info[0].num_descs; + /* create message queue for channel default endpoint */ + status = env_create_queue(&default_q, rpmsg_queue_size, sizeof(rpmsg_callback_message_t)); + if((status) || (default_q == NULL)) + { + env_delete_sync_lock(callback_sync); + return RPMSG_ERR_NO_MEM; + } + +#if RPMSG_RECV_NOCOPY_CHECK_PTRS + rpmsg_in_app_buffers_head = (rpmsg_ptr_llist_t*)env_allocate_memory(sizeof(rpmsg_ptr_llist_t)); + assert(rpmsg_in_app_buffers_head != NULL); + memset((void*)rpmsg_in_app_buffers_head, 0, sizeof(rpmsg_ptr_llist_t)); + rpmsg_in_app_buffers_tail = rpmsg_in_app_buffers_head; +#endif + + /* initialize the RPMsg communication */ + status = rpmsg_init(dev_id, rdev, rpmsg_channel_created_rtos, rpmsg_channel_deleted_rtos, rpmsg_read_rtos_cb, role); + + /* wait until the channel is established (rpmsg_channel_created callback is issued) */ + env_acquire_sync_lock(callback_sync); + + /* delete synchronization object used during the initialization process */ + env_delete_sync_lock(callback_sync); + callback_sync = NULL; + + if(default_chnl == NULL) + { + return RPMSG_ERR_NO_MEM; + } + else + { + *def_chnl = default_chnl; + return status; + } +} + +/*! + * @brief + * This function frees rpmsg driver resources for given remote device. RTOS aware version. + * + * @param[in] rdev Pointer to device to de-init + * + * @see rpmsg_rtos_init + */ +void rpmsg_rtos_deinit(struct remote_device *rdev) +{ + assert(rdev != NULL); + +#if RPMSG_RECV_NOCOPY_CHECK_PTRS + rpmsg_ptr_llist_t * iterator = NULL; + rpmsg_ptr_llist_t * next = NULL; + + env_lock_mutex(rdev->lock); + + assert(rpmsg_in_app_buffers_head != NULL); + + for(iterator = rpmsg_in_app_buffers_head; iterator != NULL; iterator = next) + { + next = iterator->next; + env_free_memory((void*)iterator); + } + + rpmsg_in_app_buffers_head = NULL; + rpmsg_in_app_buffers_tail = NULL; + + env_unlock_mutex(rdev->lock); +#endif + /* de-initialize the RPMsg communication */ + rpmsg_deinit(rdev); + + /* delete message queue used by the channel default endpoint */ + if (default_q) + { + env_delete_queue(default_q); + default_q = NULL; + } +} + +/*! + * @brief + * This function creates rpmsg endpoint for the rpmsg channel. RTOS aware version. + * + * @param[in] rp_chnl Pointer to rpmsg channel + * @param[in] addr Endpoint src address + * + * @return Pointer to endpoint control block + * + * @see rpmsg_rtos_destroy_ept + */ +struct rpmsg_endpoint * rpmsg_rtos_create_ept(struct rpmsg_channel *rp_chnl, unsigned long addr) +{ + struct rpmsg_endpoint * retval; + void *q = NULL; + + if (!rp_chnl) return NULL; + if (!rp_chnl->rdev) return NULL; + + /* create message queue for the specified endpoint */ + env_create_queue(&q, rpmsg_queue_size, sizeof(rpmsg_callback_message_t)); + if(q == NULL) + { + return NULL; + } + + /* create the RPMsg endpoint */ + retval = rpmsg_create_ept(rp_chnl, rpmsg_read_rtos_cb, (void*)q, addr); + + if(retval == NULL) + { + env_delete_queue(q); + return NULL; + } + else + { + return retval; + } +} + +/*! + * @brief + * This function deletes rpmsg endpoint and performs cleanup. RTOS aware version. + * + * @param[in] rp_ept Pointer to endpoint to destroy + * + * @see rpmsg_rtos_create_ept + */ +void rpmsg_rtos_destroy_ept(struct rpmsg_endpoint *rp_ept) +{ + void *q = rp_ept->priv; + + /* delete the RPMsg endpoint */ + rpmsg_destroy_ept(rp_ept); + + /* delete message queue used by the specified endpoint */ + env_delete_queue(q); +} + +/*! + * @brief + * RTOS receive function - blocking version of the received function that can be called from an RTOS task. + * The data is copied from the receive buffer into the user supplied buffer. + * + * This is the "receive with copy" version of the RPMsg receive function. This version is simple + * to use but it requires copying data from shared memory into the user space buffer. + * The user has no obligation or burden to manage the shared memory buffers. + * + * @param[in] ept Pointer to the RPMsg endpoint on which data is received + * @param[in] data Pointer to the user buffer the received data are copied to + * @param[out] len Pointer to an int variable that will contain the number of bytes actually copied into the buffer + * @param[in] maxlen Maximum number of bytes to copy (received buffer size) + * @param[out] src Pointer to address of the endpoint from which data is received + * @param[in] timeout_ms Timeout, in milliseconds, to wait for a message. A value of 0 means don't wait (non-blocking call). + * A value of 0xffffffff means wait forever (blocking call). + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy + */ +int rpmsg_rtos_recv(struct rpmsg_endpoint *ept, void *data, int* len, int maxlen, unsigned long* src, int timeout_ms) +{ + rpmsg_callback_message_t msg; + int retval = RPMSG_SUCCESS; + + if (!data) return RPMSG_ERR_PARAM; + if (!ept) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + + /* Get an element out of the message queue for the selected endpoint */ + if(env_get_queue(ept->priv, &msg, timeout_ms)) + { + if(src != NULL) *src = msg.src; + if(len != NULL) *len = msg.len; + + if(maxlen >= msg.len) + { + memcpy(data, msg.data, maxlen); + } + else + { + retval = RPMSG_ERR_BUFF_SIZE; + } + + /* Return used buffers. */ + rpmsg_release_rx_buffer(ept->rp_chnl, msg.data); + + return retval; + } + else + { + return RPMSG_ERR_NO_BUFF; /* failed */ + } +} + +/*! + * @brief + * RTOS receive function - blocking version of the received function that can be called from an RTOS task. + * The data is NOT copied into the user-app. buffer. + * + * This is the "zero-copy receive" version of the RPMsg receive function. No data is copied. + * Only the pointer to the data is returned. This version is fast, but it requires the user to manage + * buffer allocation. Specifically, the user must decide when a buffer is no longer in use and + * make the appropriate API call to free it, see rpmsg_rtos_recv_nocopy_free(). + * + * @param[in] ept Pointer to the RPMsg endpoint on which data is received + * @param[out] data Pointer to the RPMsg buffer of the shared memory where the received data is stored + * @param[out] len Pointer to an int variable that that will contain the number of valid bytes in the RPMsg buffer + * @param[out] src Pointer to address of the endpoint from which data is received + * @param[in] timeout_ms Timeout, in milliseconds, to wait for a message. A value of 0 means don't wait (non-blocking call). + * A value of 0xffffffff means wait forever (blocking call). + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy_free + * @see rpmsg_rtos_recv + */ +int rpmsg_rtos_recv_nocopy(struct rpmsg_endpoint *ept, void **data, int* len, unsigned long* src, int timeout_ms) +{ + rpmsg_callback_message_t msg; + + if (!data) return RPMSG_ERR_PARAM; + if (!ept) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + + /* Get an element out of the message queue for the selected endpoint */ + if(env_get_queue(ept->priv, &msg, timeout_ms)) + { + if(src != NULL) *src = msg.src; + if(len != NULL) *len = msg.len; + + *data = msg.data; + +#if RPMSG_RECV_NOCOPY_CHECK_PTRS + { + struct remote_device * rdev = NULL; + + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + rdev = ept->rp_chnl->rdev; + + env_lock_mutex(rdev->lock); + assert(rpmsg_in_app_buffers_tail != NULL); + assert(msg.data != NULL); + rpmsg_in_app_buffers_tail->ptr = msg.data; + rpmsg_in_app_buffers_tail->next = (rpmsg_ptr_llist_t*)env_allocate_memory(sizeof(rpmsg_ptr_llist_t)); + assert(rpmsg_in_app_buffers_tail->next); + rpmsg_in_app_buffers_tail = rpmsg_in_app_buffers_tail->next; + memset((void*)rpmsg_in_app_buffers_tail, 0, sizeof(rpmsg_ptr_llist_t)); + env_unlock_mutex(rdev->lock); + } +#endif + + return RPMSG_SUCCESS; /* success */ + + } + + return RPMSG_ERR_NO_BUFF; /* failed */ +} + +/*! + * @brief This function frees a buffer previously returned by rpmsg_rtos_recv_nocopy(). + * + * Once the zero-copy mechanism of receiving data is used, this function + * has to be called to free a buffer and to make it available for the next data + * transfer. + * + * @param[in] ept Pointer to the RPMsg endpoint that has consumed received data + * @param[in] data Pointer to the RPMsg buffer of the shared memory that has to be freed + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy + */ +int rpmsg_rtos_recv_nocopy_free(struct rpmsg_endpoint *ept, void* data) +{ + if (!data) return RPMSG_ERR_PARAM; + if (!ept) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + +#if RPMSG_RECV_NOCOPY_CHECK_PTRS + { + /* Allow only previously allocated buffers to be freed, + * invalid pointer values could influence the destination core, + * which is a security issue. */ + struct remote_device * rdev = NULL; + rpmsg_ptr_llist_t * iterator = NULL; + rpmsg_ptr_llist_t * prev = NULL; + + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + rdev = ept->rp_chnl->rdev; + + env_lock_mutex(rdev->lock); + + assert(rpmsg_in_app_buffers_head != NULL); + + for(iterator = rpmsg_in_app_buffers_head, prev = NULL; iterator->ptr != NULL; iterator = iterator->next) + { + if(iterator->ptr == data) + { + assert(iterator->next != NULL); + if(prev != NULL) + { + prev->next = iterator->next; + if(prev->next->ptr == NULL) + { + rpmsg_in_app_buffers_tail = prev->next; + } + } + else + { + rpmsg_in_app_buffers_head = iterator->next; + if(rpmsg_in_app_buffers_head->ptr == NULL) + { + rpmsg_in_app_buffers_tail = rpmsg_in_app_buffers_head; + } + } + + env_unlock_mutex(rdev->lock); + + env_free_memory((void*)iterator); + + /* Return used buffer. */ + rpmsg_release_rx_buffer(ept->rp_chnl, data); + + return RPMSG_SUCCESS; + } + prev = iterator; + } + + env_unlock_mutex(rdev->lock); + } + return RPMSG_ERR_PARAM; +#else + + /* Return used buffer. */ + rpmsg_release_rx_buffer(ept->rp_chnl, data); + + return RPMSG_SUCCESS; +#endif +} + +/*! + * @brief Allocates the tx buffer for message payload. + * + * This API can only be called at process context to get the tx buffer in vring. By this way, the + * application can directly put its message into the vring tx buffer without copy from an application buffer. + * It is the application responsibility to correctly fill the allocated tx buffer by data and passing correct + * parameters to the rpmsg_rtos_send_nocopy() function to perform data no-copy-send mechanism. + * + * @param[in] ept Pointer to the RPMsg endpoint that requests tx buffer allocation + * @param[out] size Pointer to store tx buffer size + * + * @return The tx buffer address on success and NULL on failure + * + * @see rpmsg_rtos_send_nocopy + */ +void *rpmsg_rtos_alloc_tx_buffer(struct rpmsg_endpoint *ept, unsigned long *size) +{ + if (!size) return NULL; + if (!ept) return NULL; + if (!ept->rp_chnl) return NULL; + if (!ept->rp_chnl->rdev) return NULL; + + return rpmsg_alloc_tx_buffer(ept->rp_chnl, size, RPMSG_TRUE); +} + +/*! + * @brief Sends a message across to the remote processor. + * + * This function sends data of length len to the remote dst address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * @param[in] ept Pointer to the RPMsg endpoint + * @param[in] data Pointer to the application buffer containing data to be sent + * @param[in] len Size of the data, in bytes, to transmit + * @param[in] dst Destination address of the message + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_rtos_send_nocopy + */ +int rpmsg_rtos_send(struct rpmsg_endpoint *ept, void *data, int len, unsigned long dst) +{ + if (!data) return RPMSG_ERR_PARAM; + if (!ept) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(ept->rp_chnl, ept->addr, dst, (char *)data, len, RPMSG_TRUE); +} + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_rtos_alloc_tx_buffer() + * to the remote processor. + * + * This function sends txbuf of length len to the remote dst address. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_rtos_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_rtos_send_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_rtos_send_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_rtos_send_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_rtos_recv_nocopy_free function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] ept Pointer to the RPMsg endpoint + * @param[in] txbuf Tx buffer with message filled + * @param[in] len Size of the data, in bytes, to transmit + * @param[in] dst Destination address of the message + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_rtos_alloc_tx_buffer + * @see rpmsg_rtos_send + */ +int rpmsg_rtos_send_nocopy(struct rpmsg_endpoint *ept, void *txbuf, int len, unsigned long dst) +{ + if (!txbuf) return RPMSG_ERR_PARAM; + if (!ept) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl) return RPMSG_ERR_PARAM; + if (!ept->rp_chnl->rdev) return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_nocopy(ept->rp_chnl, ept->addr, dst, txbuf, len); +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.h b/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.h new file mode 100644 index 0000000..6a01d65 --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_rtos.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Freescale Semiconductor 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 HOLDER 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. + */ + + /************************************************************************** + * FILE NAME + * + * rpmsg_rtos.h + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * This file provides RTOS adaptation layer that allows: + * - handling of received messages outside the interrupt context + * - the implementation of blocking API for the RPMsg receive side + * - provides zero-copy receive functionality + * - provides zero-copy send functionality + * + * DATA STRUCTURES + * + * none + * + * FUNCTIONS + * + * rpmsg_rtos_init + * rpmsg_rtos_deinit + * rpmsg_rtos_create_ept + * rpmsg_rtos_destroy_ept + * rpmsg_rtos_recv + * rpmsg_rtos_recv_nocopy + * rpmsg_rtos_recv_nocopy_free + * rpmsg_rtos_alloc_tx_buffer + * rpmsg_rtos_send + * rpmsg_rtos_send_nocopy + * + **************************************************************************/ +#ifndef __RPMSG_RTOS_H +#define __RPMSG_RTOS_H + +#include "../rpmsg/rpmsg.h" + +//! @addtogroup rpmsg_rtos +//! @{ + +/*! + * @brief + * This function allocates and initializes the rpmsg driver resources for + * given device ID (cpu id). The successful return from this function leaves + * fully enabled IPC link. RTOS aware version. + * + * @param[in] dev_id Remote device for which driver is to be initialized + * @param[out] rdev Pointer to newly created remote device + * @param[in] role Role of the other device, Master or Remote + * @param[out] def_chnl Pointer to rpmsg channel + * + * @return Status of function execution + * + * @see rpmsg_rtos_deinit + */ +int rpmsg_rtos_init(int dev_id, struct remote_device **rdev, int role, struct rpmsg_channel **def_chnl); + +/*! + * @brief + * This function frees rpmsg driver resources for given remote device. RTOS aware version. + * + * @param[in] rdev Pointer to device to de-init + * + * @see rpmsg_rtos_init + */ +void rpmsg_rtos_deinit(struct remote_device *rdev); + +/*! + * @brief + * This function creates rpmsg endpoint for the rpmsg channel. RTOS aware version. + * + * @param[in] rp_chnl Pointer to rpmsg channel + * @param[in] addr Endpoint src address + * + * @return Pointer to endpoint control block + * + * @see rpmsg_rtos_destroy_ept + */ +struct rpmsg_endpoint * rpmsg_rtos_create_ept(struct rpmsg_channel *rp_chnl, unsigned long addr); + +/*! + * @brief + * This function deletes rpmsg endpoint and performs cleanup. RTOS aware version. + * + * @param[in] rp_ept Pointer to endpoint to destroy + * + * @see rpmsg_rtos_create_ept + */ +void rpmsg_rtos_destroy_ept(struct rpmsg_endpoint *rp_ept); + +/*! + * @brief + * RTOS receive function - blocking version of the received function that can be called from an RTOS task. + * The data is copied from the receive buffer into the user supplied buffer. + * + * This is the "receive with copy" version of the RPMsg receive function. This version is simple + * to use but it requires copying data from shared memory into the user space buffer. + * The user has no obligation or burden to manage the shared memory buffers. + * + * @param[in] ept Pointer to the RPMsg endpoint on which data is received + * @param[in] data Pointer to the user buffer the received data are copied to + * @param[out] len Pointer to an int variable that will contain the number of bytes actually copied into the buffer + * @param[in] maxlen Maximum number of bytes to copy (received buffer size) + * @param[out] src Pointer to address of the endpoint from which data is received + * @param[in] timeout_ms Timeout, in milliseconds, to wait for a message. A value of 0 means don't wait (non-blocking call). + * A value of 0xffffffff means wait forever (blocking call). + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy + */ +int rpmsg_rtos_recv(struct rpmsg_endpoint *ept, void *data, int* len, int maxlen, unsigned long* src, int timeout_ms); + +/*! + * @brief + * RTOS receive function - blocking version of the received function that can be called from an RTOS task. + * The data is NOT copied into the user-app. buffer. + * + * This is the "zero-copy receive" version of the RPMsg receive function. No data is copied. + * Only the pointer to the data is returned. This version is fast, but it requires the user to manage + * buffer allocation. Specifically, the user must decide when a buffer is no longer in use and + * make the appropriate API call to free it, see rpmsg_rtos_recv_nocopy_free(). + * + * @param[in] ept Pointer to the RPMsg endpoint on which data is received + * @param[out] data Pointer to the RPMsg buffer of the shared memory where the received data is stored + * @param[out] len Pointer to an int variable that that will contain the number of valid bytes in the RPMsg buffer + * @param[out] src Pointer to address of the endpoint from which data is received + * @param[in] timeout_ms Timeout, in milliseconds, to wait for a message. A value of 0 means don't wait (non-blocking call). + * A value of 0xffffffff means wait forever (blocking call). + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy_free + * @see rpmsg_rtos_recv + */ +int rpmsg_rtos_recv_nocopy(struct rpmsg_endpoint *ept, void **data, int* len, unsigned long* src, int timeout_ms); + +/*! + * @brief This function frees a buffer previously returned by rpmsg_rtos_recv_nocopy(). + * + * Once the zero-copy mechanism of receiving data is used, this function + * has to be called to free a buffer and to make it available for the next data + * transfer. + * + * @param[in] ept Pointer to the RPMsg endpoint that has consumed received data + * @param[in] data Pointer to the RPMsg buffer of the shared memory that has to be freed + * + * @return Status of function execution + * + * @see rpmsg_rtos_recv_nocopy + */ +int rpmsg_rtos_recv_nocopy_free(struct rpmsg_endpoint *ept, void* data); + +/*! + * @brief Allocates the tx buffer for message payload. + * + * This API can only be called at process context to get the tx buffer in vring. By this way, the + * application can directly put its message into the vring tx buffer without copy from an application buffer. + * It is the application responsibility to correctly fill the allocated tx buffer by data and passing correct + * parameters to the rpmsg_rtos_send_nocopy() function to perform data no-copy-send mechanism. + * + * @param[in] ept Pointer to the RPMsg endpoint that requests tx buffer allocation + * @param[out] size Pointer to store tx buffer size + * + * @return The tx buffer address on success and NULL on failure + * + * @see rpmsg_rtos_send_nocopy + */ +void *rpmsg_rtos_alloc_tx_buffer(struct rpmsg_endpoint *ept, unsigned long *size); + +/*! + * @brief Sends a message across to the remote processor. + * + * This function sends data of length len to the remote dst address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * @param[in] ept Pointer to the RPMsg endpoint + * @param[in] data Pointer to the application buffer containing data to be sent + * @param[in] len Size of the data, in bytes, to transmit + * @param[in] dst Destination address of the message + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_rtos_send_nocopy + */ +int rpmsg_rtos_send(struct rpmsg_endpoint *ept, void *data, int len, unsigned long dst); + +/*! + * @brief Sends a message in tx buffer allocated by rpmsg_rtos_alloc_tx_buffer() + * to the remote processor. + * + * This function sends txbuf of length len to the remote dst address. + * The application has to take the responsibility for: + * 1. tx buffer allocation (rpmsg_rtos_alloc_tx_buffer() ) + * 2. filling the data to be sent into the pre-allocated tx buffer + * 3. not exceeding the buffer size when filling the data + * 4. data cache coherency + * + * After the rpmsg_rtos_send_nocopy() function is issued the tx buffer is no more owned + * by the sending task and must not be touched anymore unless the rpmsg_rtos_send_nocopy() + * function fails and returns an error. In that case the application should try + * to re-issue the rpmsg_rtos_send_nocopy() again and if it is still not possible to send + * the message and the application wants to give it up from whatever reasons + * the rpmsg_rtos_recv_nocopy_free function could be called, + * passing the pointer to the tx buffer to be released as a parameter. + * + * @param[in] ept Pointer to the RPMsg endpoint + * @param[in] txbuf Tx buffer with message filled + * @param[in] len Size of the data, in bytes, to transmit + * @param[in] dst Destination address of the message + * + * @return 0 on success and an appropriate error value on failure + * + * @see rpmsg_rtos_alloc_tx_buffer + * @see rpmsg_rtos_send + */ +int rpmsg_rtos_send_nocopy(struct rpmsg_endpoint *ept, void *txbuf, int len, unsigned long dst); + +//! @} + +#endif /* __RPMSG_RTOS_H */ diff --git a/middleware/multicore/open-amp/virtio/virtio_ring.h b/middleware/multicore/open-amp/virtio/virtio_ring.h index fc3eb08..7a861f3 100644 --- a/middleware/multicore/open-amp/virtio/virtio_ring.h +++ b/middleware/multicore/open-amp/virtio/virtio_ring.h @@ -38,7 +38,7 @@ /* This marks a buffer as write-only (otherwise read-only). */ #define VRING_DESC_F_WRITE 2 /* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 /*not used?*/ +#define VRING_DESC_F_INDIRECT 4 /* The Host uses this in used->flags to advise the Guest: don't kick me * when you add a buffer. It's unreliable, so it's simply an @@ -65,7 +65,7 @@ struct vring_desc { struct vring_avail { uint16_t flags; uint16_t idx; - uint16_t ring[0]; + uint16_t ring[1]; }; /* uint32_t is used here for ids for padding reasons. */ @@ -79,7 +79,7 @@ struct vring_used_elem { struct vring_used { uint16_t flags; uint16_t idx; - struct vring_used_elem ring[0]; + struct vring_used_elem ring[1]; }; struct vring { @@ -121,7 +121,7 @@ struct vring { * versa. They are at the end for backwards compatibility. */ #define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) -#define vring_avail_event(vr) ((vr)->used->ring[(vr)->num].id & 0xFFFF) +#define vring_avail_event(vr) ((vr)->used->ring[(vr)->num].id & 0xFFFF) static inline int vring_size(unsigned int num, unsigned long align) @@ -145,7 +145,7 @@ vring_init(struct vring *vr, unsigned int num, uint8_t *p, vr->desc = (struct vring_desc *) p; vr->avail = (struct vring_avail *) (p + num * sizeof(struct vring_desc)); - vr->used = (void *) + vr->used = (struct vring_used *) (((unsigned long) &vr->avail->ring[num] + align-1) & ~(align-1)); } diff --git a/middleware/multicore/open-amp/virtio/virtqueue.c b/middleware/multicore/open-amp/virtio/virtqueue.c index 2ea87cd..46b2af7 100644 --- a/middleware/multicore/open-amp/virtio/virtqueue.c +++ b/middleware/multicore/open-amp/virtio/virtqueue.c @@ -74,22 +74,20 @@ int virtqueue_create(struct virtio_device *virt_dev, unsigned short id, char *na + (ring->num_descs) * sizeof(struct vq_desc_extra); vq = (struct virtqueue *) env_allocate_memory(vq_size); - if (vq == VQ_NULL) { return (ERROR_NO_MEM); } env_memset(vq, 0x00, vq_size); - vq->vq_dev = virt_dev; env_strncpy(vq->vq_name, name, VIRTQUEUE_MAX_NAME_SZ); vq->vq_queue_index = id; - vq->vq_alignment = ring->align; /*ring info comes from proc_table*/ + vq->vq_alignment = ring->align; vq->vq_nentries = ring->num_descs; vq->vq_free_cnt = vq->vq_nentries; - vq->callback = callback; /*rpmsg_tx_callback, rpmsg_rx_callback*/ - vq->notify = notify; /*hil_vring_notify*/ + vq->callback = callback; + vq->notify = notify; //TODO : Whether we want to support indirect addition or not. vq->vq_ring_size = vring_size(ring->num_descs, ring->align); @@ -134,7 +132,7 @@ int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer, uint16_t idx; int needed; - needed = readable + writable; /*only one can be set to 1*/ + needed = readable + writable; VQ_PARAM_CHK(vq == VQ_NULL, status, ERROR_VQUEUE_INVLD_PARAM); VQ_PARAM_CHK(needed < 1, status, ERROR_VQUEUE_INVLD_PARAM); @@ -160,7 +158,7 @@ int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer, dxp->ndescs = needed; /* Enqueue buffer onto the ring. */ - idx = vq_ring_add_buffer(vq, vq->vq_ring.desc, head_idx, buffer, /*idx now "desc.next"*/ + idx = vq_ring_add_buffer(vq, vq->vq_ring.desc, head_idx, buffer, readable, writable); vq->vq_desc_head_idx = idx; @@ -196,7 +194,7 @@ int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer, * * @return - Function status */ -int virtqueue_add_single_buffer(struct virtqueue *vq, void *cookie, /*this function is not used at all*/ +int virtqueue_add_single_buffer(struct virtqueue *vq, void *cookie, void *buffer_addr, uint_t len, int writable, boolean has_next) { struct vq_desc_extra *dxp; @@ -326,7 +324,8 @@ void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx, uint16_t head_idx = 0; void *buffer; - if (vq->vq_available_idx == vq->vq_ring.avail->idx) { /*vq->vq_ring.avial->idx is updated in "rpmsg_rdev_create_virtqueues" "virtqueue_add_buffer" */ + /*vq->vq_ring.avial->idx is updated in "rpmsg_rdev_create_virtqueues" "virtqueue_add_buffer" */ + if (vq->vq_available_idx == vq->vq_ring.avail->idx) { return (VQ_NULL); } @@ -673,7 +672,7 @@ static int vq_ring_must_notify_host(struct virtqueue *vq) { static void vq_ring_notify_host(struct virtqueue *vq) { if (vq->notify != VQ_NULL) - vq->notify(vq); /*hil_vring_notify*/ + vq->notify(vq); } /** diff --git a/middleware/multicore/open-amp/virtio/virtqueue.h b/middleware/multicore/open-amp/virtio/virtqueue.h index 4671f6e..3e11d39 100644 --- a/middleware/multicore/open-amp/virtio/virtqueue.h +++ b/middleware/multicore/open-amp/virtio/virtqueue.h @@ -95,7 +95,7 @@ struct virtqueue { uint16_t vq_nentries; uint32_t vq_flags; int vq_alignment; - int vq_ring_size; /*Seems not used at all*/ + int vq_ring_size; boolean vq_inuse; void *vq_ring_mem; void (*callback)(struct virtqueue *vq); @@ -138,7 +138,7 @@ struct virtqueue { struct vring_desc *indirect; uint32_t indirect_paddr; uint16_t ndescs; - } vq_descx[0]; + } vq_descx[1]; }; /* struct to hold vring specific information */ |