summaryrefslogtreecommitdiff
path: root/cesar/host/src/sci.c
diff options
context:
space:
mode:
Diffstat (limited to 'cesar/host/src/sci.c')
-rw-r--r--cesar/host/src/sci.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/cesar/host/src/sci.c b/cesar/host/src/sci.c
new file mode 100644
index 0000000000..99e2f5267d
--- /dev/null
+++ b/cesar/host/src/sci.c
@@ -0,0 +1,346 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+
+/**
+ * \file sci.c
+ * \brief The sci communication functions
+ * \ingroup host
+ *
+ * This file provide sci communication functions
+ *
+ * \todo
+ */
+
+#include "common/std.h"
+#include <string.h>
+#include <errno.h>
+#include "host/sci.h"
+#include <time.h>
+#ifndef UNIT_TEST
+#include "host/syscall.h"
+#include "lib/swap.h"
+#else /* UNIT_TEST */
+#include <unistd.h>
+#include <arpa/inet.h>
+#endif /* UNIT_TEST */
+
+/**
+ * sci context, called during station context creation.
+ * \param station station which uses the sci context
+ * \return the new sci context, NULL if station is NULL
+ */
+sci_ctx_t *sci_new(station_ctx_t *station)
+{
+ //sci_ctx_t *sci;
+ /* maybe will not be implemented */
+
+ return NULL;
+}
+
+/**
+ * initialize a static sci context, called during station context creation.
+ * \param sci sci context to initialize
+ * \param station station which uses the sci context
+ * \return 0 if ok, -1 if failed with errno=
+ * - EINVAL if sci or station is NULL
+ */
+int sci_init(sci_ctx_t *sci, station_ctx_t *station)
+{
+ DBG_ASSERT(sci);
+ DBG_ASSERT(station);
+ if((sci == NULL)
+ || (station == NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* init structure */
+ memset(sci, '\0', sizeof(sci_ctx_t));
+ sci->station = station;
+
+ return 0;
+}
+
+/**
+ * sci context destruction with memory freeing.
+ * \param sci sci context to destroy
+ */
+void sci_free(sci_ctx_t *sci)
+{
+ /* maybe will not be implemented */
+ return;
+}
+
+/**
+ * register a callback function to process a message
+ * \param sci current sci context
+ * \param type message type index
+ * \param function pointer to the callback function to process the received message
+ * \param data user data to be included into callback function as 'data' parameter
+ * \return 0 if ok, -1 if failed with errno:
+ * - EINVAL if sci is null or type is wrong
+ */
+int sci_register_callback(
+ sci_ctx_t *sci,
+ sci_msg_type_t type,
+ int(*function)(sci_msg_t *msg, void *data),
+ void *data)
+{
+ DBG_ASSERT(sci);
+ DBG_ASSERT(type < SCI_MSG_TYPE_NB);
+ DBG_ASSERT(function);
+ if((sci == NULL)
+ || (type >= SCI_MSG_TYPE_NB)
+ || (function == NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ sci->msg_callback[type].function = function;
+ sci->msg_callback[type].data = data;
+
+ return 0;
+}
+
+/**
+ * fill a blank sci header with needed msg type and length
+ * a sci_msg_push() is used to get free space to store the header inside message
+ * \param sci current sci context
+ * \param msg sci message to fill header
+ * \param type type of message
+ * \param flags flags of message
+ * \return 0 if ok, -1 if failed with errno:
+ * - EINVAL if sci or msg are NULL, or if type or length are out of range
+ * - ENOSPC if there is no space left for sci header
+ */
+int sci_fill_hdr(
+ sci_ctx_t *sci,
+ sci_msg_t *msg,
+ sci_msg_type_t type,
+ int flags)
+{
+ DBG_ASSERT(sci);
+ DBG_ASSERT(msg);
+ DBG_ASSERT(type < SCI_MSG_TYPE_NB);
+ if((sci == NULL)
+ || (msg == NULL)
+ || (type >= SCI_MSG_TYPE_NB))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(sci_msg_push(msg, sizeof(sci_msg_hdr_t)) < (int)sizeof(sci_msg_hdr_t))
+ return -1;
+ msg->sci_hdr = (sci_msg_hdr_t *)msg->data_begin;
+ memcpy((unsigned char *)&msg->sci_hdr->magic_id, SCI_MSG_MAGIC, 4);
+ msg->sci_hdr->version = SCI_MSG_VERSION;
+ msg->sci_hdr->type = type;
+ msg->sci_hdr->flags = htons(flags);
+ msg->sci_hdr->station_id = htons(sci->station->id);
+ msg->sci_hdr->length = htons(msg->length - sizeof(sci_msg_hdr_t));
+ msg->sci_hdr->msg_id = htons(sci->current_msg_id | SCI_MSG_ID_STATION);
+ msg->sci_hdr->netclock_high = htonl(sci->station->current_tick_tck >> 32);
+ msg->sci_hdr->netclock_low = htonl(sci->station->current_tick_tck & 0xffffffff);
+
+ /* next current_id */
+ sci->current_msg_id = (sci->current_msg_id + 1) & SCI_MSG_ID_MASK;
+
+ return 0;
+}
+
+/**
+ * sends a sci message to output pipe
+ * \param sci current sci context
+ * \param msg sci message to send to the pipe
+ * \return length of sent data, -1 if failed with errno:
+ * - EINVAL if sci or msg are NULL
+ * - all errno generated by write() sys call
+ */
+int sci_send(sci_ctx_t *sci, sci_msg_t *msg)
+{
+ int len, total_length;
+
+ DBG_ASSERT(sci);
+ DBG_ASSERT(msg);
+ if((sci == NULL)
+ || (msg == NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ total_length = 0;
+ while(total_length < msg->length)
+ {
+#ifdef STATION_SOCK
+ len = write(sci->station->sock_fd, msg->data_begin + total_length, msg->length - total_length);
+ //station_log(sci->station, STATION_LOG_DEBUG, STATION_LOGTYPE_SCI, "%s: write %d byte", __FUNCTION__, ntohs(msg->length));
+ if(len < 0)
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: write %d failed (errno=%d)", __FUNCTION__, ntohs(msg->sci_hdr->msg_id), errno);
+ return -1;
+ }
+#else /* STATION_SOCK */
+ len = write(sci->station->pipe_out_fd, msg->data_begin + total_length, msg->length - total_length);
+ if(len < 0)
+ {
+ if(errno != EAGAIN)
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: write %d failed (errno=%d)", __FUNCTION__, ntohs(msg->sci_hdr->msg_id), errno);
+ return -1;
+ }
+ else
+ {
+ len = 0;
+ }
+ }
+#endif /* STATION_SOCK */
+ total_length += len;
+ }
+
+ return total_length;
+}
+
+/**
+ * receive a sci message from input pipe and process the registred callback
+ * \param sci current sci context
+ * \return 0 if ok, -1 if failed with errno:
+ * - EINVAL if sci is NULL
+ * - ENOSPC if msg length is > SCI_MSG_MAX_SIZE
+ * - all errno generated by read() sys call
+ */
+int sci_recv(sci_ctx_t *sci)
+{
+ static unsigned char sci_buffer[SCI_MSG_MAX_SIZE]; /** the receive buffer */
+ sci_msg_hdr_t hdr;
+ sci_msg_t msg;
+ int len;
+ tick_t msg_tick_tck;
+
+ DBG_ASSERT(sci);
+ if(sci == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(sci_msg_init(&msg, sci_buffer, SCI_MSG_MAX_SIZE) < 0)
+ return -1;
+
+ /* at first, read msg header */
+#ifdef STATION_SOCK
+ len = read(sci->station->sock_fd, &hdr, sizeof(sci_msg_hdr_t));
+ //station_log(sci->station, STATION_LOG_DEBUG, STATION_LOGTYPE_SCI, "%s: read %d bytes", __FUNCTION__, len);
+#else /* STATION_SOCK */
+ len = read(sci->station->pipe_in_fd, &hdr, sizeof(sci_msg_hdr_t));
+#endif /* STATION_SOCK */
+ if(len < 0)
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: read msg_hdr failed (errno=%d)", __FUNCTION__, errno);
+ return -1;
+ }
+ /* as read is in blocking mode, we got the wanted msg_header */
+ /* check magic id */
+ if(memcmp((unsigned char *)&hdr.magic_id, SCI_MSG_MAGIC, 4))
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: bad magic id: %02x-%02x-%02x-%02x",
+ __FUNCTION__,
+ ((unsigned char *)&hdr.magic_id)[0],
+ ((unsigned char *)&hdr.magic_id)[1],
+ ((unsigned char *)&hdr.magic_id)[2],
+ ((unsigned char *)&hdr.magic_id)[3]
+ );
+ errno = EPROTOTYPE;
+ goto fd_flush;
+ }
+
+ /* check msg size */
+ if(ntohs(hdr.length) > SCI_MSG_MAX_SIZE - sizeof(sci_msg_hdr_t))
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: msg length too long: %d bytes", __FUNCTION__, ntohs(hdr.length));
+ errno = ENOSPC;
+ goto fd_flush;
+ }
+
+ /* check msg type */
+ if(hdr.type >= SCI_MSG_TYPE_NB)
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: bad msg type: %d", __FUNCTION__, hdr.type);
+ errno = EPROTOTYPE;
+ goto read_end;
+ }
+
+ /* check station id */
+ /* for PHY SCI messages, station id is the source station id or the Maximus pid */
+ /* for all other messages, station id is the destination station id */
+ if(((SCI_MSG_TYPE_PHY == hdr.type) && (0 == ntohs(hdr.station_id)))
+ || ((SCI_MSG_TYPE_PHY != hdr.type) && (ntohs(hdr.station_id) != sci->station->id)))
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: bad station id: %d", __FUNCTION__, ntohs(hdr.station_id));
+ errno = ENODEV;
+ goto read_end;
+ }
+
+ /* read the remaining part of message */
+ if(sci_msg_push(&msg, ntohs(hdr.length)) < ntohs(hdr.length))
+ return -1;
+#ifdef STATION_SOCK
+ len = read(sci->station->sock_fd, msg.data_begin, msg.length);
+#else /* STATION_SOCK */
+ len = read(sci->station->pipe_in_fd, msg.data_begin, msg.length);
+#endif /* STATION_SOCK */
+ if(len < 0)
+ {
+ station_log(sci->station, STATION_LOG_WARNING, STATION_LOGTYPE_SCI, "%s: read msg_body failed (errno=%d)", __FUNCTION__, errno);
+ return -1;
+ }
+ if(sci_msg_push(&msg, sizeof(sci_msg_hdr_t)) < (int)sizeof(sci_msg_hdr_t))
+ return -1;
+ memcpy(msg.data_begin, &hdr, sizeof(sci_msg_hdr_t));
+ msg.sci_hdr = (sci_msg_hdr_t *)msg.data_begin;
+ if(sci_msg_pop(&msg, sizeof(sci_msg_hdr_t)) < (int)sizeof(sci_msg_hdr_t))
+ return -1;
+ msg.hdr.generic = msg.data_begin;
+
+ /* get new network tick */
+ msg_tick_tck = (((unsigned long long)(ntohl(msg.sci_hdr->netclock_high))) << 32)
+ | (unsigned long long)(ntohl(msg.sci_hdr->netclock_low));
+ /* check if new tick is now too old */
+ if(msg_tick_tck > sci->station->current_tick_tck)
+ sci->station->current_tick_tck = msg_tick_tck;
+
+ /* now, call the callback function */
+ if(sci->msg_callback[msg.sci_hdr->type].function != NULL)
+ {
+ return (*sci->msg_callback[msg.sci_hdr->type].function)(&msg, sci->msg_callback[msg.sci_hdr->type].data);
+ }
+ else
+ {
+ return 0;
+ }
+
+fd_flush:
+#ifdef STATION_SOCK
+ read(sci->station->sock_fd, sci_buffer, sizeof(sci_buffer));
+#else /* STATION_SOCK */
+ read(sci->station->pipe_in_fd, sci_buffer, sizeof(sci_buffer));
+#endif /* STATIOn_SOCK */
+ return -1;
+
+read_end:
+#ifdef STATION_SOCK
+ read(sci->station->sock_fd, sci_buffer, hdr.length);
+#else /* STATION_SOCK */
+ read(sci->station->pipe_in_fd, sci_buffer, hdr.length);
+#endif /* STATION_SOCK */
+ return -1;
+}
+