/*! * \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) { 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); } } mcc_generate_cpu_to_cpu_interrupt(); /* Semaphore-protected section end */ return_value = mcc_release_semaphore(); if(return_value != MCC_SUCCESS) return return_value; 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 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) * * \see mcc_initialize */ int mcc_destroy(MCC_NODE node) { int i = 0, return_value; #if (MCC_OS_USED == MCC_MQX) for(i=0; i<MCC_MQX_LWEVENT_COMPONENTS_COUNT; i++) { _lwevent_destroy(&lwevent_buffer_queued[i]); } _lwevent_destroy(&lwevent_buffer_freed); #endif /* 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); return return_value; }
/*! * \brief This function removes an endpoint. * * Removes an endpoint with specified structure / params (core, node and port). * * \param[in] endpoint Pointer to the endpoint structure. * * \return MCC_SUCCESS * \return MCC_ERR_ENDPOINT (invalid value for port or the endpoint doesn't exist) */ int mcc_remove_endpoint(MCC_ENDPOINT endpoint) { int i=0; /* must be valid */ if(endpoint.port == MCC_RESERVED_PORT_NUMBER) return MCC_ERR_ENDPOINT; 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(MCC_ENDPOINTS_EQUAL(bookeeping_data->endpoint_table[i].endpoint, endpoint)) { /* clear the queue */ MCC_RECEIVE_BUFFER * buffer = mcc_dequeue_buffer((MCC_RECEIVE_LIST *)&bookeeping_data->endpoint_table[i].list); while(buffer) { mcc_queue_buffer(&bookeeping_data->free_list, buffer); buffer = mcc_dequeue_buffer((MCC_RECEIVE_LIST *)&bookeeping_data->endpoint_table[i].list); } /* indicate free */ bookeeping_data->endpoint_table[i].endpoint.port = MCC_RESERVED_PORT_NUMBER; MCC_DCACHE_FLUSH_MLINES((void*)&bookeeping_data->endpoint_table[i].endpoint.port, sizeof(MCC_PORT)); return MCC_SUCCESS; } } return MCC_ERR_ENDPOINT; }
/*! * \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_copy * \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; }
/*! * \brief This function dequeues the buffer. * * Dequeues the buffer from the list. * * \param[in] list Pointer to the MCC_RECEIVE_LIST structure. * * \return Pointer to MCC_RECEIVE_BUFFER */ MCC_RECEIVE_BUFFER * mcc_dequeue_buffer(MCC_RECEIVE_LIST *list) { MCC_RECEIVE_BUFFER * next_buf, * next_buf_virt; MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST)); next_buf = list->head; next_buf_virt = (MCC_RECEIVE_BUFFER *)MCC_MEM_PHYS_TO_VIRT(next_buf); if(next_buf) { MCC_DCACHE_INVALIDATE_MLINES((void*)&next_buf_virt->next, sizeof(MCC_RECEIVE_BUFFER*)); list->head = next_buf_virt->next; if(list->tail == next_buf) list->tail = null; } MCC_DCACHE_FLUSH_MLINES(list, sizeof(MCC_RECEIVE_LIST)); return next_buf_virt; }
/*! * \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; }
/*! * \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; }
/*! * \brief This function dequeues a signal * * It dequeues a signal from the signal queue for the particular core. * * \param[in] core Core number. * \param[in] signal Signal to be dequeued. * * \return MCC_SUCCESS * \return MCC_ERR_SQ_EMPTY (signal queue is empty, nothing to dequeue) */ int mcc_dequeue_signal(MCC_CORE core, MCC_SIGNAL *signal) { int head; MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signal_queue_head[core], sizeof(unsigned int)); MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signal_queue_tail[core], sizeof(unsigned int)); head = bookeeping_data->signal_queue_head[core]; if(MCC_SIGNAL_QUEUE_EMPTY(core)) return MCC_ERR_SQ_EMPTY; MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signals_received[core][head], sizeof(MCC_SIGNAL)); signal->type = bookeeping_data->signals_received[core][head].type; signal->destination.core = bookeeping_data->signals_received[core][head].destination.core; signal->destination.node = bookeeping_data->signals_received[core][head].destination.node; signal->destination.port = bookeeping_data->signals_received[core][head].destination.port; bookeeping_data->signal_queue_head[core] = head == (MCC_MAX_OUTSTANDING_SIGNALS-1) ? 0 : head+1; MCC_DCACHE_FLUSH_MLINES((void*)&bookeeping_data->signal_queue_head[core], sizeof(unsigned int)); return MCC_SUCCESS; }
/*! * \brief This function queues a signal * * Signal circular queue rules: * tail points to next free slot * head points to first occupied slot * head == tail indicates empty * (tail + 1) % len = fill * This method costs 1 slot since you need to differentiate * between full and empty (if you fill the last slot it looks * like empty since h == t) * * \param[in] core Core number. * \param[in] signal Signal to be queued. * * \return MCC_SUCCESS * \return MCC_ERR_SQ_FULL (signal queue is full - no more that MCC_MAX_OUTSTANDING_SIGNALS items allowed) */ int mcc_queue_signal(MCC_CORE core, MCC_SIGNAL signal) { int tail, new_tail; MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signal_queue_head[core], sizeof(unsigned int)); MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signal_queue_tail[core], sizeof(unsigned int)); tail = bookeeping_data->signal_queue_tail[core]; new_tail = tail == (MCC_MAX_OUTSTANDING_SIGNALS-1) ? 0 : tail+1; if(MCC_SIGNAL_QUEUE_FULL(core)) return MCC_ERR_SQ_FULL; MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->signals_received[core][tail], sizeof(MCC_SIGNAL)); bookeeping_data->signals_received[core][tail].type = signal.type; bookeeping_data->signals_received[core][tail].destination.core = signal.destination.core; bookeeping_data->signals_received[core][tail].destination.node = signal.destination.node; bookeeping_data->signals_received[core][tail].destination.port = signal.destination.port; bookeeping_data->signal_queue_tail[core] = new_tail; MCC_DCACHE_FLUSH_MLINES((void*)&bookeeping_data->signal_queue_tail[core], sizeof(unsigned int)); MCC_DCACHE_FLUSH_MLINES((void*)&bookeeping_data->signals_received[core][tail], sizeof(MCC_SIGNAL)); return MCC_SUCCESS; }
/*! * \brief This function returns the endpoint list. * * Returns the MCC_RECEIVE_LIST respective to the endpoint structure provided. * * \param[in] endpoint Pointer to the MCC_ENDPOINT structure. * * \return MCC_RECEIVE_LIST pointer * \return null pointer */ MCC_RECEIVE_LIST * mcc_get_endpoint_list(MCC_ENDPOINT endpoint) { int i=0; /* must be valid */ if(endpoint.port == MCC_RESERVED_PORT_NUMBER) return null; 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(MCC_ENDPOINTS_EQUAL(bookeeping_data->endpoint_table[i].endpoint, endpoint)) { return (MCC_RECEIVE_LIST *)&bookeeping_data->endpoint_table[i].list; } } return null; }
/*! * \brief This function queues the buffer. * * Queues the buffer in the list. * * \param[in] list Pointer to the MCC_RECEIVE_LIST structure. * \param[in] r_buffer Pointer to MCC_RECEIVE_BUFFER. * * \return none */ void mcc_queue_buffer(MCC_RECEIVE_LIST *list, MCC_RECEIVE_BUFFER * r_buffer) { MCC_RECEIVE_BUFFER * last_buf; MCC_RECEIVE_BUFFER * r_buffer_phys; MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST)); last_buf = (MCC_RECEIVE_BUFFER *)MCC_MEM_PHYS_TO_VIRT(list->tail); r_buffer_phys = (MCC_RECEIVE_BUFFER *)MCC_MEM_VIRT_TO_PHYS(r_buffer); if(last_buf) { last_buf->next = r_buffer_phys; MCC_DCACHE_FLUSH_MLINES((void*)&last_buf->next, sizeof(MCC_RECEIVE_BUFFER*)); } else { list->head = r_buffer_phys; } r_buffer->next = null; list->tail = r_buffer_phys; MCC_DCACHE_FLUSH_MLINES(list, sizeof(MCC_RECEIVE_LIST)); MCC_DCACHE_FLUSH_MLINES((void*)&r_buffer->next, sizeof(MCC_RECEIVE_BUFFER*)); }
/*! * \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; }
/*! * \brief This function registers an endpoint. * * Register an endpoint with specified structure / params (core, node and port). * * \param[in] endpoint Pointer to the endpoint structure. * * \return MCC_SUCCESS * \return MCC_ERR_NOMEM (maximum number of endpoints exceeded) * \return MCC_ERR_ENDPOINT (invalid value for port or endpoint already registered) */ int mcc_register_endpoint(MCC_ENDPOINT endpoint) { int i; /* must be valid */ if(endpoint.port == MCC_RESERVED_PORT_NUMBER) return MCC_ERR_ENDPOINT; /* check not already registered */ if(mcc_get_endpoint_list(endpoint)) return MCC_ERR_ENDPOINT; 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.port == MCC_RESERVED_PORT_NUMBER) { bookeeping_data->endpoint_table[i].endpoint.core = endpoint.core; bookeeping_data->endpoint_table[i].endpoint.node = endpoint.node; bookeeping_data->endpoint_table[i].endpoint.port = endpoint.port; MCC_DCACHE_FLUSH_MLINES(&bookeeping_data->endpoint_table[i], sizeof(MCC_ENDPOINT_MAP_ITEM)); return MCC_SUCCESS; } } return MCC_ERR_NOMEM; }
/*! * \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 initializes the Multi Core Communication subsystem for a given node. * * This function should only be called once per node (once in MQX, once per process in Linux). * * \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) * * \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}; #if (MCC_OS_USED == MCC_MQX) for(i=0; i<MCC_MQX_LWEVENT_COMPONENTS_COUNT; i++) { _lwevent_create(&lwevent_buffer_queued[i],0); } _lwevent_create(&lwevent_buffer_freed,0); #endif /* Initialize synchronization module */ return_value = mcc_init_semaphore(MCC_SHMEM_SEMAPHORE_NUMBER); 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_BASE_ADDRESS; MCC_DCACHE_INVALIDATE_MLINES(bookeeping_data, sizeof(MCC_BOOKEEPING_STRUCT)); if(strcmp(bookeeping_data->init_string, init_string) != 0) { /* Zero it all - no guarantee Linux or uboot didnt touch it before it was reserved */ _mem_zero((void*) bookeeping_data, (_mem_size) 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 = &bookeeping_data->r_buffers[0]; bookeeping_data->free_list.tail = &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 = &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; } } MCC_DCACHE_FLUSH_MLINES(bookeeping_data, sizeof(MCC_BOOKEEPING_STRUCT)); return return_value; }
/*! * \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. * * \param[in] endpoint Pointer to the 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_us Timeout, in microseconds, 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_copy * \see MCC_ENDPOINT */ int mcc_recv_nocopy(MCC_ENDPOINT *endpoint, void **buffer_p, MCC_MEM_SIZE *recv_size, unsigned int timeout_us) { MCC_RECEIVE_LIST *list; int return_value; #if (MCC_OS_USED == MCC_MQX) unsigned int time_us_tmp; unsigned int lwevent_index = endpoint->port / MCC_MQX_LWEVENT_GROUP_SIZE; unsigned int lwevent_group_index = endpoint->port % MCC_MQX_LWEVENT_GROUP_SIZE; MQX_TICK_STRUCT tick_time; #endif /* 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); /* Semaphore-protected section end */ return_value = mcc_release_semaphore(); if(return_value != MCC_SUCCESS) return return_value; /* The endpoint is not valid */ if(list == null) { return MCC_ERR_ENDPOINT; } if(list->head == (MCC_RECEIVE_BUFFER*)0) { /* Non-blocking call */ if(timeout_us == 0) { return MCC_ERR_TIMEOUT; } /* Blocking call */ else { #if (MCC_OS_USED == MCC_MQX) if(timeout_us == 0xFFFFFFFF) { _lwevent_wait_ticks(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index, TRUE, 0); } /* timeout_us > 0 */ else { _time_get_ticks(&tick_time); _time_add_usec_to_ticks(&tick_time, timeout_us); _lwevent_wait_until(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index, TRUE, &tick_time); } #endif MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST*)); } } /* Clear event bit specified for the particular endpoint in the lwevent_buffer_queued lwevent group */ _lwevent_clear(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index); if(list->head == (MCC_RECEIVE_BUFFER*)0) { /* Buffer not dequeued before the timeout */ 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->data_len, sizeof(MCC_MEM_SIZE)); *recv_size = (MCC_MEM_SIZE)(list->head->data_len); /* Semaphore-protected section start */ return_value = mcc_get_semaphore(); if(return_value != MCC_SUCCESS) return return_value; /* 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; }
/*! * \brief This function receives a message from the specified endpoint if one is available. * The data will be 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[in] endpoint Pointer to the 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_us Timeout, in microseconds, 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_copy(MCC_ENDPOINT *endpoint, void *buffer, MCC_MEM_SIZE buffer_size, MCC_MEM_SIZE *recv_size, unsigned int timeout_us) { MCC_RECEIVE_LIST *list; 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; #if (MCC_OS_USED == MCC_MQX) unsigned int time_us_tmp; unsigned int lwevent_index = endpoint->port / MCC_MQX_LWEVENT_GROUP_SIZE; unsigned int lwevent_group_index = endpoint->port % MCC_MQX_LWEVENT_GROUP_SIZE; MQX_TICK_STRUCT tick_time; #endif /* 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); /* Semaphore-protected section end */ return_value = mcc_release_semaphore(); if(return_value != MCC_SUCCESS) return return_value; /* The endpoint is not valid */ if(list == null) { return MCC_ERR_ENDPOINT; } if(list->head == (MCC_RECEIVE_BUFFER*)0) { /* Non-blocking call */ if(timeout_us == 0) { return MCC_ERR_TIMEOUT; } /* Blocking call */ else { #if (MCC_OS_USED == MCC_MQX) if(timeout_us == 0xFFFFFFFF) { _lwevent_wait_ticks(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index, TRUE, 0); } /* timeout_us > 0 */ else { _time_get_ticks(&tick_time); _time_add_usec_to_ticks(&tick_time, timeout_us); _lwevent_wait_until(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index, TRUE, &tick_time); } #endif MCC_DCACHE_INVALIDATE_MLINES((void*)list, sizeof(MCC_RECEIVE_LIST*)); } } /* Clear event bit specified for the particular endpoint in the lwevent_buffer_queued lwevent group */ _lwevent_clear(&lwevent_buffer_queued[lwevent_index], 1<<lwevent_group_index); if(list->head == (MCC_RECEIVE_BUFFER*)0) { /* Buffer not dequeued before the timeout */ return MCC_ERR_TIMEOUT; } /* Copy the message from the MCC receive buffer into the user-app. buffer */ MCC_DCACHE_INVALIDATE_MLINES((void*)&list->head->data_len, sizeof(MCC_MEM_SIZE)); 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); /* Semaphore-protected section start */ return_value = mcc_get_semaphore(); if(return_value != MCC_SUCCESS) return return_value; /* Dequeue the buffer from the endpoint list */ 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); } } mcc_generate_cpu_to_cpu_interrupt(); /* 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] endpoint Pointer to the receiving endpoint to send 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_us Timeout, in microseconds, 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_us set to 0) * \return MCC_ERR_SQ_FULL (signal queue is full) * * \see mcc_recv_copy * \see mcc_recv_nocopy * \see MCC_ENDPOINT */ int mcc_send(MCC_ENDPOINT *endpoint, void *msg, MCC_MEM_SIZE msg_size, unsigned int timeout_us) { int return_value, end_time_set_flag = 0; MCC_RECEIVE_LIST *list; MCC_RECEIVE_BUFFER * buf; MCC_SIGNAL affiliated_signal; #if (MCC_OS_USED == MCC_MQX) unsigned int time_us_tmp; MQX_TICK_STRUCT tick_time; #endif /* 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; /* 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); while(buf == null) { mcc_release_semaphore(); /* Non-blocking call */ if(timeout_us == 0) { return MCC_ERR_NOMEM; } /* Blocking calls: CPU-to-CPU ISR sets the event and thus resumes tasks waiting for a free MCC buffer. * As the interrupt request is send to all cores when a buffer is freed it could happen that several * tasks from different cores/nodes are waiting for a free buffer and all of them are notified that the buffer * has been freed. This function has to check (after the wake up) that a buffer is really available and has not been already * grabbed by another "competitor task" that has been faster. If so, it has to wait again for the next notification. */ /* wait forever */ else if(timeout_us == 0xFFFFFFFF) { #if (MCC_OS_USED == MCC_MQX) _lwevent_wait_ticks(&lwevent_buffer_freed, 1, TRUE, 0); _lwevent_clear(&lwevent_buffer_freed, 1); #endif } /* timeout_us > 0 */ else { #if (MCC_OS_USED == MCC_MQX) if(!end_time_set_flag) { _time_get_ticks(&tick_time); _time_add_usec_to_ticks(&tick_time, timeout_us); end_time_set_flag = 1; } return_value = _lwevent_wait_until(&lwevent_buffer_freed, 1, TRUE, &tick_time); if(return_value == LWEVENT_WAIT_TIMEOUT) { /* Buffer not dequeued before the timeout */ return MCC_ERR_TIMEOUT; } _lwevent_clear(&lwevent_buffer_freed, 1); #endif } MCC_DCACHE_INVALIDATE_MLINES((void*)&bookeeping_data->free_list, sizeof(MCC_RECEIVE_LIST*)); mcc_get_semaphore(); buf = mcc_dequeue_buffer(&bookeeping_data->free_list); } /* Semaphore-protected section end */ mcc_release_semaphore(); if(return_value != MCC_SUCCESS) return return_value; /* Copy the message into the MCC receive buffer */ mcc_memcpy(msg, (void*)buf->data, (unsigned int)msg_size); MCC_DCACHE_FLUSH_MLINES((void*)buf->data, msg_size); buf->data_len = msg_size; MCC_DCACHE_FLUSH_MLINES((void*)&buf->data_len, sizeof(MCC_MEM_SIZE)); /* 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), 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 = *endpoint; return_value = mcc_queue_signal(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 */ 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; }
/*! * \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; }
/*! * \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; }