419 lines
12 KiB
C
419 lines
12 KiB
C
/*
|
|
All files except if stated otherwise in the begining of the file
|
|
are under the ISC license:
|
|
----------------------------------------------------------------------
|
|
|
|
Copyright (c) 2010-2012 Design Art Networks Ltd.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include "ipc_reg.h"
|
|
#include "ipc_api.h"
|
|
|
|
#include "danipc_lowlevel.h"
|
|
|
|
|
|
/* -----------------------------------------------------------
|
|
* MACRO (define) section
|
|
* -----------------------------------------------------------
|
|
*/
|
|
/* max. number of local agents per one Node */
|
|
#define MAX_LOCAL_ID (MAX_LOCAL_AGENT-1)
|
|
|
|
static uint8_t ipc_req_sn; /* Maintain node related sequence number */
|
|
|
|
uint8_t ipc_own_node;
|
|
|
|
|
|
/* ===========================================================================
|
|
* ipc_appl_init
|
|
* ===========================================================================
|
|
* Description: This function initializes software/HW during startup
|
|
*
|
|
* Parameters: def_trns_funcs - pointer to default transport layer
|
|
* function vector
|
|
*
|
|
* Returns: n/a
|
|
*
|
|
*/
|
|
static inline void ipc_appl_init(struct ipc_trns_func const *def_trns_funcs)
|
|
{
|
|
ipc_own_node = ipc_get_own_node();
|
|
ipc_agent_table_clean();
|
|
|
|
ipc_trns_fifo_buf_init(ipc_own_node);
|
|
|
|
/* Initialize IPC routing table (of CPU#) */
|
|
ipc_route_table_init(def_trns_funcs);
|
|
}
|
|
|
|
|
|
unsigned ipc_init(void)
|
|
{
|
|
ipc_appl_init(&ipc_fifo_utils);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* ===========================================================================
|
|
* ipc_buf_alloc
|
|
* ===========================================================================
|
|
* Description: buffer allocation API, should be called before building
|
|
* new message
|
|
*
|
|
* Parameters: dest_aid - Message destination AgentId
|
|
* prio - Transport priority level
|
|
*
|
|
*
|
|
* Returns: Pointer to a 128 Byte buffer
|
|
*
|
|
*/
|
|
char *ipc_buf_alloc(uint8_t dest_aid, enum ipc_trns_prio prio)
|
|
{
|
|
char *ptr = NULL;
|
|
struct ipc_trns_func const *trns_funcs;
|
|
ipc_trns_alloc_t alloc_func;
|
|
const uint8_t cpuid = ipc_get_node(dest_aid);
|
|
|
|
/* Allocate buffer of 128 Bytes using the allocation function */
|
|
/* associated with the given destination agentId */
|
|
trns_funcs = (void *)get_trns_funcs(cpuid);
|
|
if (likely(trns_funcs)) {
|
|
alloc_func = trns_funcs->trns_alloc;
|
|
if (likely(alloc_func)) {
|
|
ptr = alloc_func(cpuid, prio);
|
|
|
|
/* Clear the 'Next buffer' field. */
|
|
if (likely(ptr))
|
|
((struct ipc_buf_hdr *)ptr)->next = 0;
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_buf_free
|
|
* ===========================================================================
|
|
* Description: Free the buffer, could be called on IPC message receiving node
|
|
* or on sending node when need to free previously allocated
|
|
* buffers
|
|
*
|
|
* Parameters: buf_first - Pointer to first message buffer
|
|
* prio - Transport priority level
|
|
*
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
int32_t ipc_buf_free(char *buf_first, enum ipc_trns_prio prio)
|
|
{
|
|
struct ipc_buf_hdr *cur_buf;
|
|
struct ipc_buf_hdr *next_buf;
|
|
struct ipc_trns_func const *trns_funcs;
|
|
ipc_trns_free_t free_func;
|
|
uint8_t dest_aid;
|
|
uint8_t cpuid;
|
|
int32_t res = IPC_GENERIC_ERROR;
|
|
|
|
if (likely(buf_first)) {
|
|
dest_aid = (((struct ipc_first_buf *)buf_first)->
|
|
msg_hdr).dest_aid;
|
|
cur_buf = (struct ipc_buf_hdr *)buf_first;
|
|
cpuid = ipc_get_node(dest_aid);
|
|
trns_funcs = get_trns_funcs(cpuid);
|
|
if (likely(trns_funcs)) {
|
|
free_func = trns_funcs->trns_free;
|
|
if (likely(free_func)) {
|
|
/* Now loop all allocated buffers and free them.
|
|
* Last buffer is either a single (type = 0)
|
|
* or the buffer marked as the last (type = 2)
|
|
* all other buffers have their LSB set
|
|
* (type = 1 or 3).
|
|
*/
|
|
do {
|
|
next_buf = ((struct ipc_msg_hdr *)
|
|
IPC_NEXT_PTR_PART(cur_buf))->next;
|
|
free_func(IPC_NEXT_PTR_PART(cur_buf),
|
|
cpuid, prio);
|
|
cur_buf = next_buf;
|
|
} while ((uint32_t)cur_buf & IPC_BUF_TYPE_MTC);
|
|
res = IPC_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_buf_link
|
|
* ===========================================================================
|
|
* Description: Link two buffers, should be called when message does not fit
|
|
* the single buffer
|
|
*
|
|
* Parameters: buf_prev - Pointer to a message buffer
|
|
* buf_next - Pointer to the next message buffer
|
|
* (to be linked to)
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
static int32_t ipc_buf_link(char *buf_prev, char *buf_next)
|
|
{
|
|
if (buf_prev == NULL || buf_next == NULL)
|
|
return IPC_GENERIC_ERROR;
|
|
|
|
/* Set the next buffer pointer in place */
|
|
*(uint32_t *)buf_prev |= (uint32_t)buf_next & ~IPC_BUF_TYPE_BITS;
|
|
/* Set the LSB of the prev buffer to signal there are more to come */
|
|
*(uint32_t *)buf_prev |= IPC_BUF_TYPE_MTC;
|
|
/* Mark the next buffer as the last one */
|
|
*(uint32_t *)buf_next |= IPC_BUF_TYPE_END;
|
|
return IPC_SUCCESS;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_msg_set_len
|
|
* ===========================================================================
|
|
* Description: sets message length, first buffer of the message
|
|
* should be provided
|
|
*
|
|
* Parameters: buf_first - Pointer to the first message buffer
|
|
* len - Message length (bytes)
|
|
*
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
static int32_t ipc_msg_set_len(char *buf_first, size_t len)
|
|
{
|
|
if (buf_first == NULL)
|
|
return IPC_GENERIC_ERROR;
|
|
(((struct ipc_first_buf *)buf_first)->msg_hdr).msg_len = len;
|
|
return IPC_SUCCESS;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_msg_set_type
|
|
* ===========================================================================
|
|
* Description: sets message type, first buffer of the message
|
|
* should be provided
|
|
*
|
|
* Parameters: buf_first - Pointer to the first message buffer
|
|
* type - Message type
|
|
*
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
static int32_t ipc_msg_set_type(char *buf_first, uint8_t type)
|
|
{
|
|
if (buf_first == NULL)
|
|
return IPC_GENERIC_ERROR;
|
|
(((struct ipc_first_buf *)buf_first)->msg_hdr).msg_type = type;
|
|
return IPC_SUCCESS;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_msg_set_reply_ptr
|
|
* ===========================================================================
|
|
* Description: sets message reply buffer pointer
|
|
*
|
|
* Parameters: buf_first - Pointer to the first message buffer
|
|
* buf_rep - Pointer to the expected replay message
|
|
*
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
static int32_t ipc_msg_set_reply_ptr(
|
|
char *buf_first,
|
|
char *buf_rep
|
|
)
|
|
{
|
|
if (buf_first == NULL)
|
|
return IPC_GENERIC_ERROR;
|
|
(((struct ipc_first_buf *)buf_first)->msg_hdr).reply = buf_rep;
|
|
return IPC_SUCCESS;
|
|
}
|
|
|
|
|
|
/* ===========================================================================
|
|
* ipc_msg_alloc
|
|
* ===========================================================================
|
|
* Description: Allocate message buffer[s] and set the type and length.
|
|
* Copy message data into allocated buffers.
|
|
*
|
|
*
|
|
* Parameters: src_aid - Message source AgentId
|
|
* dest_aid - Message destination AgentId
|
|
* msg - Pointer to message data
|
|
* msg_len - Message length
|
|
* msg_type - Message type
|
|
* prio - Transport priority level
|
|
*
|
|
*
|
|
* Returns: Pointer to the message first buffer
|
|
*
|
|
*/
|
|
char *ipc_msg_alloc(
|
|
uint8_t src_aid,
|
|
uint8_t dest_aid,
|
|
char *msg,
|
|
size_t msg_len,
|
|
uint8_t msg_type,
|
|
enum ipc_trns_prio prio
|
|
)
|
|
{
|
|
char *first_buf = NULL;
|
|
char *prev_buf = NULL;
|
|
char *next_buf = NULL;
|
|
unsigned buf;
|
|
unsigned next_bufs_num = 0;
|
|
size_t tmp_size, reminder;
|
|
char *last_data;
|
|
|
|
if ((msg_len > IPC_MAX_MESSAGE_SIZE) || (msg_len == 0))
|
|
return NULL;
|
|
|
|
/* Calculate number of 'next' buffers required */
|
|
/* (i.e. buffers additional to the first buffer) */
|
|
if (msg_len > IPC_FIRST_BUF_DATA_SIZE_MAX) {
|
|
next_bufs_num = (msg_len - IPC_FIRST_BUF_DATA_SIZE_MAX) /
|
|
IPC_NEXT_BUF_DATA_SIZE_MAX;
|
|
if ((msg_len - IPC_FIRST_BUF_DATA_SIZE_MAX) %
|
|
IPC_NEXT_BUF_DATA_SIZE_MAX)
|
|
next_bufs_num++;
|
|
}
|
|
|
|
first_buf = prev_buf = ipc_buf_alloc(dest_aid, prio);
|
|
for (buf = 0; buf < next_bufs_num; buf++) {
|
|
if (prev_buf == NULL)
|
|
break;
|
|
next_buf = ipc_buf_alloc(dest_aid, prio);
|
|
if (next_buf != NULL)
|
|
ipc_buf_link(prev_buf, next_buf);
|
|
prev_buf = next_buf;
|
|
}
|
|
|
|
/* If buffer allocation failed free the entire buffers */
|
|
if ((prev_buf == NULL) && (first_buf != NULL)) {
|
|
ipc_buf_free(first_buf, prio);
|
|
first_buf = NULL;
|
|
} else if (first_buf) {
|
|
ipc_msg_set_type(first_buf, msg_type);
|
|
ipc_msg_set_len(first_buf, msg_len);
|
|
ipc_msg_set_reply_ptr(first_buf, NULL);
|
|
((struct ipc_msg_hdr *)first_buf)->dest_aid = dest_aid;
|
|
((struct ipc_msg_hdr *)first_buf)->src_aid = src_aid;
|
|
((struct ipc_msg_hdr *)first_buf)->request_num = ipc_req_sn;
|
|
ipc_req_sn++;
|
|
|
|
if (msg) {
|
|
last_data = msg + msg_len;
|
|
|
|
/* Now copy the Data */
|
|
reminder = msg_len;
|
|
tmp_size = min_t(size_t, reminder,
|
|
IPC_FIRST_BUF_DATA_SIZE_MAX);
|
|
|
|
memcpy(((struct ipc_first_buf *)first_buf)->body,
|
|
last_data - reminder, tmp_size);
|
|
|
|
reminder -= tmp_size;
|
|
prev_buf = first_buf;
|
|
|
|
while (reminder > 0) {
|
|
next_buf = IPC_NEXT_PTR_PART(
|
|
((struct ipc_msg_hdr *)prev_buf)->next);
|
|
tmp_size = min_t(size_t, reminder,
|
|
IPC_NEXT_BUF_DATA_SIZE_MAX);
|
|
|
|
memcpy(((struct ipc_next_buf *)next_buf)->body,
|
|
last_data - reminder, tmp_size);
|
|
|
|
reminder -= tmp_size;
|
|
prev_buf = next_buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
return first_buf;
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* ipc_msg_send
|
|
* ===========================================================================
|
|
* Description: Message send, first buffer of the message should be provided,
|
|
*
|
|
* Parameters: buf_first - Pointer to the first message buffer
|
|
* prio - Transport priority level
|
|
*
|
|
*
|
|
* Returns: Result code
|
|
*
|
|
*/
|
|
int32_t ipc_msg_send(char *buf_first, enum ipc_trns_prio prio)
|
|
{
|
|
struct ipc_next_buf *buf;
|
|
struct ipc_trns_func const *trns_funcs;
|
|
ipc_trns_send_t send_func;
|
|
uint8_t dest_aid;
|
|
uint8_t cpuid;
|
|
int32_t res = IPC_GENERIC_ERROR;
|
|
|
|
if (likely(buf_first)) {
|
|
dest_aid = (((struct ipc_first_buf *)buf_first)->
|
|
msg_hdr).dest_aid;
|
|
cpuid = ipc_get_node(dest_aid);
|
|
buf = (struct ipc_next_buf *)buf_first;
|
|
trns_funcs = get_trns_funcs(cpuid);
|
|
if (likely(trns_funcs)) {
|
|
send_func = trns_funcs->trns_send;
|
|
if (send_func)
|
|
res = send_func((char *)buf, cpuid, prio);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* -----------------------------------------------------------
|
|
* Function: ipc_recv
|
|
* Description: Processing IPC messages
|
|
* Input: max_msg_count - max number processed messages per call
|
|
* prio - transport priority level
|
|
* Output: number of processed messages
|
|
* -----------------------------------------------------------
|
|
*/
|
|
uint32_t ipc_recv(uint32_t max_msg_count, enum ipc_trns_prio prio)
|
|
{
|
|
unsigned ix;
|
|
char *ipc_data;
|
|
|
|
for (ix = 0; ix < max_msg_count; ix++) {
|
|
ipc_data = ipc_trns_fifo_buf_read(prio);
|
|
|
|
if (ipc_data) {
|
|
/* IPC_msg_handler(ipc_data); */
|
|
handle_incoming_packet(ipc_data, ipc_own_node, prio);
|
|
} else
|
|
break; /* no more messages, queue empty */
|
|
}
|
|
return ix;
|
|
}
|