blob: abf6c6301893f2d62c161e282446f7f46ad79047 [file] [log] [blame]
/*
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;
}