| /* |
| * This file contains MCC library API functions implementation |
| * |
| * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved. |
| * |
| * |
| * SPDX-License-Identifier: GPL-2.0+ and/or BSD-3-Clause |
| * The GPL-2.0+ license for this file can be found in the COPYING.GPL file |
| * included with this distribution or at |
| * http://www.gnu.org/licenses/gpl-2.0.html |
| * The BSD-3-Clause License for this file can be found in the COPYING.BSD file |
| * included with this distribution or at |
| * http://opensource.org/licenses/BSD-3-Clause |
| */ |
| |
| #include "mcc_config.h" |
| #if (MCC_OS_USED == MCC_MQX) |
| #include <string.h> |
| #include "mcc_config.h" |
| #include "mcc_common.h" |
| #include "mcc_api.h" |
| #include "mcc_mqx.h" |
| #elif (MCC_OS_USED == MCC_LINUX) |
| #include <linux/mcc_api.h> |
| #include <linux/mcc_imx6sx.h> |
| #include <linux/mcc_linux.h> |
| #include <linux/imx_sema4.h> |
| #endif |
| |
| const char * const init_string = MCC_INIT_STRING; |
| const char * const version_string = MCC_VERSION_STRING; |
| |
| static int mcc_recv_common_part(MCC_ENDPOINT *endpoint, unsigned int timeout_ms, MCC_RECEIVE_LIST **list); |
| static int mcc_get_buffer_internal(void **buffer, MCC_MEM_SIZE *buf_size, unsigned int timeout_ms); |
| static int mcc_free_buffer_internal(void *buffer); |
| /*! |
| * \brief This function initializes the Multi Core Communication subsystem for a given node. |
| * |
| * This function should only be called once per node (once in MQX, once per a process in Linux). |
| * It tries to initialize the bookkeeping structure when the init_string member of this structure |
| * is not equal to MCC_INIT_STRING, i.e. when no other core had performed the initialization yet. |
| * Note, that this way of bookkeeping data re-initialization protection is not powerful enough and |
| * the user application should not rely on this method. Instead, the application should be designed |
| * to unambiguously assign the core that will perform the MCC initialization. |
| * Clear the shared memory before the first core is attempting to initialize the MCC |
| * (in some cases MCC_INIT_STRING remains in the shared memory after the application reset and could |
| * cause that the bookkeeping data structure is not initialized correctly). |
| * |
| * \param[in] node Node number that will be used in endpoints created by this process. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_INT (interrupt registration error) |
| * \return MCC_ERR_VERSION (incorrect MCC version used - compatibility issue) |
| * \return MCC_ERR_OSSYNC (OS synchronization module(s) initialization failed) |
| * |
| * \see mcc_destroy |
| * \see MCC_BOOKEEPING_STRUCT |
| */ |
| int mcc_initialize(MCC_NODE node) |
| { |
| int i,j = 0; |
| int return_value = MCC_SUCCESS; |
| MCC_SIGNAL tmp_signals_received = {(MCC_SIGNAL_TYPE)0, {(MCC_CORE)0, (MCC_NODE)0, (MCC_PORT)0}}; |
| |
| /* Initialize synchronization module for shared data protection */ |
| return_value = mcc_init_semaphore(MCC_SHMEM_SEMAPHORE_NUMBER); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Initialize all necessary OS synchronization module(s) |
| (for unblocking tasks waiting for new received data and for unblocking tasks waiting for a free buffer) */ |
| return_value = mcc_init_os_sync(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Register CPU-to-CPU interrupt for inter-core signaling */ |
| //mcc_register_cpu_to_cpu_isr(MCC_CORE0_CPU_TO_CPU_VECTOR); |
| return_value = mcc_register_cpu_to_cpu_isr(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Initialize the bookeeping structure */ |
| bookeeping_data = (MCC_BOOKEEPING_STRUCT *)mcc_get_bookeeping_data(); |
| MCC_DCACHE_INVALIDATE_MLINES(bookeeping_data, sizeof(MCC_BOOKEEPING_STRUCT)); |
| if(strcmp(bookeeping_data->init_string, init_string) != 0) { |
| /* MCC not initialized yet, do it now */ |
| /* Zero it all - no guarantee Linux or uboot didnt touch it before it was reserved */ |
| memset((void*) bookeeping_data, 0, sizeof(struct mcc_bookeeping_struct)); |
| |
| /* Set init_string in case it has not been set yet by another core */ |
| mcc_memcpy((void*)init_string, bookeeping_data->init_string, (unsigned int)sizeof(bookeeping_data->init_string)); |
| |
| /* Set version_string */ |
| mcc_memcpy((void*)version_string, bookeeping_data->version_string, (unsigned int)sizeof(bookeeping_data->version_string)); |
| |
| /* Initialize the free list */ |
| bookeeping_data->free_list.head = (MCC_RECEIVE_BUFFER*)MCC_MEM_VIRT_TO_PHYS(&bookeeping_data->r_buffers[0]); |
| bookeeping_data->free_list.tail = (MCC_RECEIVE_BUFFER*)MCC_MEM_VIRT_TO_PHYS(&bookeeping_data->r_buffers[MCC_ATTR_NUM_RECEIVE_BUFFERS-1]); |
| |
| /* Initialize receive buffers */ |
| for(i=0; i<MCC_ATTR_NUM_RECEIVE_BUFFERS-1; i++) { |
| bookeeping_data->r_buffers[i].next = (MCC_RECEIVE_BUFFER*)MCC_MEM_VIRT_TO_PHYS(&bookeeping_data->r_buffers[i+1]); |
| } |
| bookeeping_data->r_buffers[MCC_ATTR_NUM_RECEIVE_BUFFERS-1].next = null; |
| |
| /* Initialize signal queues */ |
| for(i=0; i<MCC_NUM_CORES; i++) { |
| for(j=0; j<MCC_MAX_OUTSTANDING_SIGNALS; j++) { |
| bookeeping_data->signals_received[i][j] = tmp_signals_received; |
| } |
| bookeeping_data->signal_queue_head[i] = 0; |
| bookeeping_data->signal_queue_tail[i] = 0; |
| } |
| |
| /* Mark all endpoint ports as free */ |
| for(i=0; i<MCC_ATTR_MAX_RECEIVE_ENDPOINTS; i++) { |
| bookeeping_data->endpoint_table[i].endpoint.port = MCC_RESERVED_PORT_NUMBER; |
| } |
| } |
| else { |
| /* MCC already initialized - check the major number of the version string to ensure compatibility */ |
| if(strncmp(bookeeping_data->version_string, version_string, 4) != 0) { |
| return_value = MCC_ERR_VERSION; |
| } |
| } |
| |
| MCC_DCACHE_FLUSH_MLINES(bookeeping_data, sizeof(MCC_BOOKEEPING_STRUCT)); |
| return return_value; |
| } |
| |
| /*! |
| * \brief This function de-initializes the Multi Core Communication subsystem for a given node. |
| * |
| * The function frees all resources of the node. Deletes all endpoints and frees any buffers that may have been queued there. |
| * |
| * \param[in] node Node number to be deinitialized. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_OSSYNC (OS synchronization module(s) deinitialization failed) |
| * |
| * \see mcc_initialize |
| */ |
| int mcc_destroy(MCC_NODE node) |
| { |
| int i = 0, return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* All endpoints of the particular node have to be removed from the endpoint table */ |
| MCC_DCACHE_INVALIDATE_MLINES(&bookeeping_data->endpoint_table[0], MCC_ATTR_MAX_RECEIVE_ENDPOINTS * sizeof(MCC_ENDPOINT_MAP_ITEM)); |
| for(i = 0; i < MCC_ATTR_MAX_RECEIVE_ENDPOINTS; i++) { |
| if (bookeeping_data->endpoint_table[i].endpoint.node == node) { |
| /* Remove the endpoint from the table */ |
| mcc_remove_endpoint(bookeeping_data->endpoint_table[i].endpoint); |
| } |
| } |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Deinitialize synchronization module */ |
| mcc_deinit_semaphore(MCC_SHMEM_SEMAPHORE_NUMBER); |
| |
| /* De-initialize OS synchronization module(s) |
| (for unblocking tasks waiting for new data and for unblocking tasks waiting for a free buffer */ |
| return_value = mcc_deinit_os_sync(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| |
| /*! |
| * \brief This function creates an endpoint. |
| * |
| * The function creates an endpoint on the local node with the specified port number. |
| * The core and node provided in the endpoint must match the caller's core and |
| * node, and the port argument must match the endpoint port. |
| * |
| * \param[out] endpoint Pointer to the endpoint triplet to be created. |
| * \param[in] port Port number. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_NOMEM (maximum number of endpoints exceeded) |
| * \return MCC_ERR_ENDPOINT (invalid value for port or endpoint already registered) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see mcc_destroy_endpoint |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_create_endpoint(MCC_ENDPOINT *endpoint, MCC_PORT port) |
| { |
| int return_value = MCC_SUCCESS; |
| |
| /* Fill the endpoint structure */ |
| endpoint->core = (MCC_CORE)MCC_CORE_NUMBER; |
| endpoint->node = (MCC_NODE)MCC_NODE_NUMBER; |
| endpoint->port = (MCC_PORT)port; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Add new endpoint data into the book-keeping structure */ |
| return_value = mcc_register_endpoint(*endpoint); |
| if(return_value != MCC_SUCCESS) { |
| mcc_release_semaphore(); |
| return return_value; |
| } |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| |
| /*! |
| * \brief This function destroys an endpoint. |
| * |
| * The function destroys an endpoint on the local node and frees any buffers that may be queued. |
| * |
| * \param[in] endpoint Pointer to the endpoint triplet to be deleted. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint doesn't exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see mcc_create_endpoint |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_destroy_endpoint(MCC_ENDPOINT *endpoint) |
| { |
| int return_value = MCC_SUCCESS; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Remove the endpoint data from the book-keeping structure */ |
| return_value = mcc_remove_endpoint(*endpoint); |
| if(return_value != MCC_SUCCESS) { |
| mcc_release_semaphore(); |
| return return_value; |
| } |
| |
| /* Clear OS synchronization module parts associated with the endpoint to be destroyed */ |
| mcc_clear_os_sync_for_ep(endpoint); |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| |
| /*! |
| * \brief This function sends a message to an endpoint. |
| * |
| * The message is copied into the MCC buffer and the destination core is signaled. |
| * |
| * \param[in] src_endpoint Pointer to the local endpoint identifying the source endpoint. |
| * \param[in] dest_endpoint Pointer to the destination endpoint to send the message to. |
| * \param[in] msg Pointer to the message to be sent. |
| * \param[in] msg_size Size of the message to be sent in bytes. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_INVAL (the msg_size exceeds the size of a data buffer) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a buffer became available) |
| * \return MCC_ERR_NOMEM (no free buffer available and timeout_ms set to 0) |
| * \return MCC_ERR_SQ_FULL (signal queue is full) |
| * |
| * \see mcc_recv |
| * \see mcc_recv_nocopy |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_send(MCC_ENDPOINT *src_endpoint, MCC_ENDPOINT *dest_endpoint, void *msg, MCC_MEM_SIZE msg_size, unsigned int timeout_ms) |
| { |
| int return_value; |
| MCC_RECEIVE_LIST *list; |
| MCC_RECEIVE_BUFFER * buf; |
| MCC_SIGNAL affiliated_signal; |
| MCC_MEM_SIZE buffer_size; |
| |
| /* Reuse the mcc_get_buffer_internal() function to get the MCC buffer pointer. */ |
| return_value = mcc_get_buffer_internal((void**)&buf, &buffer_size, timeout_ms); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Check if the size of the message to be sent does not exceed the size of the mcc buffer */ |
| if(msg_size > buffer_size) { |
| while(MCC_SUCCESS != mcc_free_buffer_internal(buf)) {}; |
| return MCC_ERR_INVAL; |
| } |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* As the mcc_get_buffer_internal() returns the pointer to the data field, it |
| is necessary to adjust the pointer to point at the MCC buffer structure beginning. */ |
| buf = (MCC_RECEIVE_BUFFER *)((unsigned int)buf - (unsigned int)(&(((MCC_RECEIVE_BUFFER*)0)->data))); |
| |
| /* Copy the message into the MCC receive buffer */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)buf, sizeof(MCC_RECEIVE_BUFFER)); |
| mcc_memcpy(msg, (void*)buf->data, (unsigned int)msg_size); |
| mcc_memcpy((void*)src_endpoint, (void*)&buf->source, sizeof(MCC_ENDPOINT)); |
| buf->data_len = msg_size; |
| MCC_DCACHE_FLUSH_MLINES((void*)buf, sizeof(MCC_RECEIVE_BUFFER)); |
| |
| /* Get list of buffers kept by the particular endpoint */ |
| list = mcc_get_endpoint_list(*dest_endpoint); |
| |
| if(list == null) { |
| /* The endpoint does not exists (has not been registered so far), |
| free the buffer and return immediately - error */ |
| /* Enqueue the buffer back into the free list */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); |
| mcc_queue_buffer(&bookeeping_data->free_list, buf); |
| |
| mcc_release_semaphore(); |
| return MCC_ERR_ENDPOINT; |
| } |
| |
| /* Write the signal type into the signal queue of the particular core */ |
| affiliated_signal.type = BUFFER_QUEUED; |
| affiliated_signal.destination = *dest_endpoint; |
| return_value = mcc_queue_signal(dest_endpoint->core, affiliated_signal); |
| if(return_value != MCC_SUCCESS) { |
| /* Signal queue is full, free the buffer and return immediately - error */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); |
| mcc_queue_buffer(&bookeeping_data->free_list, buf); |
| |
| mcc_release_semaphore(); |
| return return_value; |
| } |
| |
| /* Enqueue the buffer into the endpoint buffer list */ |
| mcc_queue_buffer(list, buf); |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Signal the other core by generating the CPU-to-CPU interrupt */ |
| return_value = mcc_generate_cpu_to_cpu_interrupt(); |
| |
| return return_value; |
| } |
| |
| /*! |
| * \private |
| * |
| * \brief This function dequeues a buffer from the free list. |
| * |
| * This is an internal implementation of the mcc_get_buffer() function. It is called |
| * either from the mcc_send() or from mcc_get_buffer() when the non-copy-send |
| * mechanism is enabled by the MCC_SEND_RECV_NOCOPY_API_ENABLED macro in mcc_config.h. |
| * |
| * \param[out] buffer Pointer to the MCC buffer dequeued from the free list. |
| * \param[out] buf_size Pointer to an MCC_MEM_SIZE that is used for passing the size of the dequeued MCC buffer to the application. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a buffer became available) |
| * \return MCC_ERR_NOMEM (no free buffer available and timeout_ms set to 0) |
| * |
| * \see mcc_send_nocopy |
| * \see mcc_send |
| * \see mcc_get_buffer |
| */ |
| static int mcc_get_buffer_internal(void **buffer, MCC_MEM_SIZE *buf_size, unsigned int timeout_ms) |
| { |
| int return_value; |
| MCC_RECEIVE_BUFFER * buf = null; |
| |
| *buffer = null; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Dequeue the buffer from the free list */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); |
| buf = mcc_dequeue_buffer(&bookeeping_data->free_list); |
| |
| /* Semaphore-protected section end */ |
| mcc_release_semaphore(); |
| |
| if(buf == null) { |
| /* Non-blocking call */ |
| if(timeout_ms == 0) { |
| return MCC_ERR_NOMEM; |
| } else { |
| /* Wait for the buffer freed event */ |
| return_value = mcc_wait_for_buffer_freed((MCC_RECEIVE_BUFFER **)&buf, timeout_ms); |
| if(MCC_SUCCESS != return_value) { |
| return return_value; |
| } |
| } |
| } |
| |
| /* Return the MCC buffer size and the pointer to the dequeued MCC buffer */ |
| *buf_size = (MCC_MEM_SIZE)sizeof(bookeeping_data->r_buffers[0].data); |
| *buffer = (void*)buf->data; |
| return MCC_SUCCESS; |
| } |
| |
| #if MCC_SEND_RECV_NOCOPY_API_ENABLED |
| /*! |
| * \brief This function dequeues a buffer from the free list. |
| * |
| * The application has take the responsibility for MCC buffer de-allocation and |
| * filling the data to be sent into the pre-allocated MCC buffer. |
| * |
| * \param[out] buffer Pointer to the MCC buffer dequeued from the free list. |
| * \param[out] buf_size Pointer to an MCC_MEM_SIZE that is used for passing the size of the dequeued MCC buffer to the application. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a buffer became available) |
| * \return MCC_ERR_NOMEM (no free buffer available and timeout_ms set to 0) |
| * |
| * \see mcc_send_nocopy |
| */ |
| int mcc_get_buffer(void **buffer, MCC_MEM_SIZE *buf_size, unsigned int timeout_ms) |
| { |
| return mcc_get_buffer_internal(buffer, buf_size, timeout_ms); |
| } |
| |
| /*! |
| * \brief This function sends a message to an endpoint. The data is NOT copied |
| * from the user-app. buffer but the pointer to already filled message buffer is |
| * provided. |
| * |
| * The application has to take the responsibility for: |
| * 1. MCC buffer de-allocation |
| * 2. filling the data to be sent into the pre-allocated MCC buffer |
| * 3. not exceeding the buffer size when filling the data (MCC_ATTR_BUFFER_SIZE_IN_BYTES) |
| * |
| * Once the data cache is used on the target platform it is good to have MCC buffers |
| * in shared RAM aligned to the cache line size in order not to corrupt entities placed |
| * just before and just after the MCC buffer when flushing the MCC buffer content into |
| * the shared RAM. It is also the application responsibility to flush the data in |
| * that case. If the alignment condition is not fulfilled the application |
| * has to take care about the data cache coherency. |
| * The following scenarios can happen: |
| * A. Data cache is OFF: |
| * - No cache operation needs to be done, the application just |
| * 1. calls the mcc_get_buffer() function, |
| * 2. fills data into the provided MCC buffer, |
| * 3. and finally issues the mcc_send_nocopy() function. |
| * B. Data cache is ON, shared RAM MCC buffers ALIGNED to the cache line size: |
| * - The application has to perform following steps: |
| * 1. call the mcc_get_buffer() to get the pointer to a free message buffer |
| * 2. copy data to be sent into the message buffer |
| * 3. flush all cache lines occupied by the message buffer new data |
| * (maximum of MCC_ATTR_BUFFER_SIZE_IN_BYTES bytes). |
| * 4. call the mcc_send_nocopy() with the correct buffer pointer and the message size passed |
| * C. Data cache is ON, shared RAM MCC buffers NOT ALIGNED: |
| * - The application has to perform following steps: |
| * 1. call the mcc_get_buffer() to get the pointer to a free message buffer |
| * 2. grab the hw semaphore by calling the mcc_get_semaphore() low level MCC function. |
| * 3. invalidate all cache lines occupied by data to be filled into the free message buffer. |
| * (maximum of MCC_ATTR_BUFFER_SIZE_IN_BYTES bytes). |
| * 4. copy data to be sent into the message buffer. |
| * 5. flush all cache lines occupied by the message buffer new data |
| * (maximum of MCC_ATTR_BUFFER_SIZE_IN_BYTES bytes). |
| * 6. release the hw semaphore by calling the mcc_release_semaphore() low level MCC function. |
| * 7. call the mcc_send_nocopy() with the correct buffer pointer and the message size passed. |
| * |
| * After the mcc_send_nocopy() function is issued the message buffer is no more owned |
| * by the sending task and must not be touched anymore unless the mcc_send_nocopy() |
| * function fails and returns an error. In that case the application should try |
| * to re-issue the mcc_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 |
| * (for instance the MCC_ERR_ENDPOINT error is returned meaning the endpoint |
| * has not been created yet) the mcc_free_buffer() function could be called, |
| * passing the pointer to the buffer to be freed as a parameter. |
| * |
| * \param[in] src_endpoint Pointer to the local endpoint identifying the source endpoint. |
| * \param[in] dest_endpoint Pointer to the destination endpoint to send the message to. |
| * \param[in] buffer_p Pointer to the MCC buffer of the shared memory where the |
| * data to be sent is stored. |
| * \param[in] msg_size Size of the message to be sent in bytes. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_INVAL (the msg_size exceeds the size of a data buffer) |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_SQ_FULL (signal queue is full) |
| * |
| * \see mcc_send |
| * \see mcc_get_buffer |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_send_nocopy(MCC_ENDPOINT *src_endpoint, MCC_ENDPOINT *dest_endpoint, void *buffer_p, MCC_MEM_SIZE msg_size) |
| { |
| int return_value; |
| MCC_RECEIVE_BUFFER * buf; |
| MCC_RECEIVE_LIST *list; |
| MCC_SIGNAL affiliated_signal; |
| |
| /* Check if the size of the message to be sent does not exceed the size of the mcc buffer */ |
| if(msg_size > sizeof(bookeeping_data->r_buffers[0].data)) { |
| return MCC_ERR_INVAL; |
| } |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Get list of buffers kept by the particular endpoint */ |
| list = mcc_get_endpoint_list(*dest_endpoint); |
| |
| if(list == null) { |
| /* The endpoint does not exists (has not been registered so far) */ |
| mcc_release_semaphore(); |
| return MCC_ERR_ENDPOINT; |
| } |
| |
| /* Store the message size and the source endpoint in the MCC_RECEIVE_BUFFER structure */ |
| buf = (MCC_RECEIVE_BUFFER *)((unsigned int)buffer_p - (unsigned int)(&(((MCC_RECEIVE_BUFFER*)0)->data))); |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&buf->source, sizeof(MCC_ENDPOINT) + sizeof(MCC_MEM_SIZE)); |
| ((MCC_RECEIVE_BUFFER*)buf)->data_len = msg_size; |
| mcc_memcpy((void*)src_endpoint, (void*)&buf->source, sizeof(MCC_ENDPOINT)); |
| MCC_DCACHE_FLUSH_MLINES((void*)(void*)&buf->source, sizeof(MCC_ENDPOINT) + sizeof(MCC_MEM_SIZE)); |
| |
| |
| /* Write the signal type into the signal queue of the particular core */ |
| affiliated_signal.type = BUFFER_QUEUED; |
| affiliated_signal.destination = *dest_endpoint; |
| return_value = mcc_queue_signal(dest_endpoint->core, affiliated_signal); |
| if(return_value != MCC_SUCCESS) { |
| /* Signal queue is full - error */ |
| mcc_release_semaphore(); |
| return return_value; |
| } |
| |
| /* Enqueue the buffer into the endpoint buffer list */ |
| mcc_queue_buffer(list, (MCC_RECEIVE_BUFFER*)buf); |
| |
| /* Semaphore-protected section end */ |
| mcc_release_semaphore(); |
| |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Signal the other core by generating the CPU-to-CPU interrupt */ |
| return_value = mcc_generate_cpu_to_cpu_interrupt(); |
| |
| return return_value; |
| } |
| #endif /* MCC_SEND_RECV_NOCOPY_API_ENABLED */ |
| |
| /*! |
| * \brief This function receives a message from the specified endpoint if one is available. |
| * The data is copied from the receive buffer into the user supplied buffer. |
| * |
| * This is the "receive with copy" version of the MCC 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[out] src_endpoint Pointer to the MCC_ENDPOINT structure to be filled by the endpoint identifying the message sender. |
| * \param[in] dest_endpoint Pointer to the local receiving endpoint to receive from. |
| * \param[in] buffer Pointer to the user-app. buffer where data will be copied into. |
| * \param[in] buffer_size The maximum number of bytes to copy. |
| * \param[out] recv_size Pointer to an MCC_MEM_SIZE that will contain the number of bytes actually copied into the buffer. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a new message came) |
| * |
| * \see mcc_send |
| * \see mcc_recv_nocopy |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_recv(MCC_ENDPOINT *src_endpoint, MCC_ENDPOINT *dest_endpoint, void *buffer, MCC_MEM_SIZE buffer_size, MCC_MEM_SIZE *recv_size, unsigned int timeout_ms) |
| { |
| MCC_RECEIVE_LIST *list = null; |
| MCC_RECEIVE_BUFFER * buf; |
| MCC_SIGNAL affiliated_signal; |
| MCC_ENDPOINT tmp_destination = {(MCC_CORE)0, (MCC_NODE)0, (MCC_PORT)0}; |
| int return_value, i = 0; |
| |
| return_value = mcc_recv_common_part(dest_endpoint, timeout_ms, (MCC_RECEIVE_LIST**)&list); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST*)); |
| |
| if(list->head == (MCC_RECEIVE_BUFFER*)0) { |
| /* Buffer not dequeued before the timeout */ |
| mcc_release_semaphore(); |
| return MCC_ERR_TIMEOUT; |
| } |
| |
| /* Copy the message from the MCC receive buffer into the user-app. buffer */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&list->head->source, sizeof(MCC_ENDPOINT) + sizeof(MCC_MEM_SIZE)); |
| mcc_memcpy((void*)&list->head->source, (void*)src_endpoint, sizeof(MCC_ENDPOINT)); |
| if (list->head->data_len > buffer_size) { |
| list->head->data_len = buffer_size; |
| } |
| *recv_size = (MCC_MEM_SIZE)(list->head->data_len); |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&list->head->data, list->head->data_len); |
| mcc_memcpy((void*)list->head->data, buffer, list->head->data_len); |
| |
| /* Dequeue the buffer from the endpoint list */ |
| list->head = (MCC_RECEIVE_BUFFER*)MCC_MEM_VIRT_TO_PHYS(list->head); |
| buf = mcc_dequeue_buffer(list); |
| |
| /* Enqueue the buffer into the free list */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); |
| mcc_queue_buffer(&bookeeping_data->free_list, buf); |
| |
| /* Notify all cores (except of itself) via CPU-to-CPU interrupt that a buffer has been freed */ |
| affiliated_signal.type = BUFFER_FREED; |
| affiliated_signal.destination = tmp_destination; |
| for (i=0; i<MCC_NUM_CORES; i++) { |
| if(i != MCC_CORE_NUMBER) { |
| mcc_queue_signal(i, affiliated_signal); |
| } |
| } |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| |
| mcc_generate_cpu_to_cpu_interrupt(); |
| |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| |
| #if MCC_SEND_RECV_NOCOPY_API_ENABLED |
| /*! |
| * \brief This function receives a message from the specified endpoint if one is available. The data is NOT copied into the user-app. buffer. |
| * |
| * This is the "zero-copy receive" version of the MCC 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 mcc_free_buffer. |
| * |
| * \param[out] src_endpoint Pointer to the MCC_ENDPOINT structure to be filled by the endpoint identifying the message sender. |
| * \param[in] dest_endpoint Pointer to the local receiving endpoint to receive from. |
| * \param[out] buffer_p Pointer to the MCC buffer of the shared memory where the received data is stored. |
| * \param[out] recv_size Pointer to an MCC_MEM_SIZE that will contain the number of valid bytes in the buffer. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a new message came) |
| * |
| * \see mcc_send |
| * \see mcc_recv |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_recv_nocopy(MCC_ENDPOINT *src_endpoint, MCC_ENDPOINT *dest_endpoint, void **buffer_p, MCC_MEM_SIZE *recv_size, unsigned int timeout_ms) |
| { |
| MCC_RECEIVE_LIST *list = null; |
| int return_value; |
| |
| return_value = mcc_recv_common_part(dest_endpoint, timeout_ms, (MCC_RECEIVE_LIST**)&list); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST*)); |
| |
| if(list->head == (MCC_RECEIVE_BUFFER*)0) { |
| /* Buffer not dequeued before the timeout */ |
| mcc_release_semaphore(); |
| return MCC_ERR_TIMEOUT; |
| } |
| |
| /* Get the message pointer from the head of the receive buffer list */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&list->head->data, list->head->data_len); |
| *buffer_p = (void*)&list->head->data; |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&list->head->source, sizeof(MCC_ENDPOINT) + sizeof(MCC_MEM_SIZE)); |
| mcc_memcpy((void*)&list->head->source, (void*)src_endpoint, sizeof(MCC_ENDPOINT)); |
| *recv_size = (MCC_MEM_SIZE)(list->head->data_len); |
| |
| /* Dequeue the buffer from the endpoint list */ |
| mcc_dequeue_buffer(list); |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| #endif /* MCC_SEND_RECV_NOCOPY_API_ENABLED */ |
| |
| /*! |
| * \private |
| * |
| * \brief This function is common part for mcc_recv() and mcc_recv_nocopy() function. |
| * |
| * It tries to get the list of buffers kept by the particular endpoint. If the list is empty |
| * it waits for a new message until the timeout expires. |
| * |
| * \param[in] endpoint Pointer to the receiving endpoint to receive from. |
| * \param[in] timeout_ms Timeout, in milliseconds, to wait for a free buffer. A value of 0 means don't wait (non-blocking call). A value of 0xffffffff means wait forever (blocking call). |
| * \param[out] list Pointer to the list of buffers kept by the particular endpoint. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * \return MCC_ERR_TIMEOUT (timeout exceeded before a new message came) |
| * |
| * \see mcc_recv |
| * \see mcc_recv_nocopy |
| * \see MCC_ENDPOINT |
| */ |
| static int mcc_recv_common_part(MCC_ENDPOINT *endpoint, unsigned int timeout_ms, MCC_RECEIVE_LIST **list) |
| { |
| MCC_RECEIVE_LIST *tmp_list; |
| int return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Get list of buffers kept by the particular endpoint */ |
| tmp_list = mcc_get_endpoint_list(*endpoint); |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* The endpoint is not valid */ |
| if(tmp_list == null) { |
| return MCC_ERR_ENDPOINT; |
| } |
| |
| if(tmp_list->head == (MCC_RECEIVE_BUFFER*)0) { |
| /* Non-blocking call */ |
| if(timeout_ms == 0) { |
| return MCC_ERR_TIMEOUT; |
| } |
| /* Blocking call */ |
| else { |
| /* Wait for the buffer queued event */ |
| return_value = mcc_wait_for_buffer_queued(endpoint, timeout_ms); |
| if(MCC_SUCCESS != return_value) { |
| return return_value; |
| } |
| } |
| } |
| else { |
| tmp_list->head = (MCC_RECEIVE_BUFFER*)MCC_MEM_PHYS_TO_VIRT(tmp_list->head); |
| } |
| /* Clear event bit specified for the particular endpoint */ |
| mcc_clear_os_sync_for_ep(endpoint); |
| |
| *list = (MCC_RECEIVE_LIST*)tmp_list; |
| return MCC_SUCCESS; |
| } |
| |
| /*! |
| * \brief This function returns the number of buffers currently queued at the endpoint. |
| * |
| * The function checks if messages are available on a receive endpoint. While the call only checks the |
| * availability of messages, it does not dequeue them. |
| * |
| * \param[in] endpoint Pointer to the endpoint structure. |
| * \param[out] num_msgs Pointer to an unsigned int that will contain the number of buffers queued. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_ENDPOINT (the endpoint does not exist) |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see mcc_recv |
| * \see mcc_recv_nocopy |
| * \see MCC_ENDPOINT |
| */ |
| int mcc_msgs_available(MCC_ENDPOINT *endpoint, unsigned int *num_msgs) |
| { |
| unsigned int count = 0; |
| MCC_RECEIVE_LIST *list; |
| MCC_RECEIVE_BUFFER * buf; |
| int return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Get list of buffers kept by the particular endpoint */ |
| list = mcc_get_endpoint_list(*endpoint); |
| if(list == null) { |
| /* The endpoint does not exists (has not been registered so far), return immediately - error */ |
| mcc_release_semaphore(); |
| return MCC_ERR_ENDPOINT; |
| } |
| |
| buf = list->head; |
| while(buf != (MCC_RECEIVE_BUFFER*)0) { |
| count++; |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&buf->next, sizeof(MCC_RECEIVE_BUFFER*)); |
| buf = (MCC_RECEIVE_BUFFER*)buf->next; |
| } |
| *num_msgs = count; |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| |
| /*! |
| * \private |
| * |
| * \brief This function frees a buffer previously returned by mcc_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] buffer Pointer to the buffer to be freed. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see mcc_recv_nocopy |
| */ |
| static int mcc_free_buffer_internal(void *buffer) |
| { |
| MCC_SIGNAL affiliated_signal; |
| MCC_ENDPOINT tmp_destination = {(MCC_CORE)0, (MCC_NODE)0, (MCC_PORT)0}; |
| int return_value, i = 0; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| /* Enqueue the buffer into the free list */ |
| MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); |
| mcc_queue_buffer(&bookeeping_data->free_list, (MCC_RECEIVE_BUFFER *)((unsigned int)buffer - (unsigned int)(&(((MCC_RECEIVE_BUFFER*)0)->data)))); |
| |
| /* Notify all cores (except of itself) via CPU-to-CPU interrupt that a buffer has been freed */ |
| affiliated_signal.type = BUFFER_FREED; |
| affiliated_signal.destination = tmp_destination; |
| for (i=0; i<MCC_NUM_CORES; i++) { |
| if(i != MCC_CORE_NUMBER) { |
| mcc_queue_signal(i, affiliated_signal); |
| } |
| } |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| |
| mcc_generate_cpu_to_cpu_interrupt(); |
| |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |
| #if MCC_SEND_RECV_NOCOPY_API_ENABLED |
| /*! |
| * \brief This function frees a buffer previously returned by mcc_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] buffer Pointer to the buffer to be freed. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see mcc_recv_nocopy |
| */ |
| int mcc_free_buffer(void *buffer) |
| { |
| return mcc_free_buffer_internal(buffer); |
| } |
| #endif /* MCC_SEND_RECV_NOCOPY_API_ENABLED */ |
| |
| /*! |
| * \brief This function returns information about the MCC sub system. |
| * |
| * The function returns implementation-specific information. |
| * |
| * \param[in] node Node number. |
| * \param[out] info_data Pointer to the MCC_INFO_STRUCT structure to hold returned data. |
| * |
| * \return MCC_SUCCESS |
| * \return MCC_ERR_SEMAPHORE (semaphore handling error) |
| * |
| * \see MCC_INFO_STRUCT |
| */ |
| int mcc_get_info(MCC_NODE node, MCC_INFO_STRUCT* info_data) |
| { |
| int return_value; |
| |
| /* Semaphore-protected section start */ |
| return_value = mcc_get_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| mcc_memcpy(bookeeping_data->version_string, (void*)info_data->version_string, (unsigned int)sizeof(bookeeping_data->version_string)); |
| |
| /* Semaphore-protected section end */ |
| return_value = mcc_release_semaphore(); |
| if(return_value != MCC_SUCCESS) |
| return return_value; |
| |
| return return_value; |
| } |