summaryrefslogtreecommitdiff
path: root/cleopatre/devkit/plcdrv/src/mailbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'cleopatre/devkit/plcdrv/src/mailbox.c')
-rw-r--r--cleopatre/devkit/plcdrv/src/mailbox.c526
1 files changed, 526 insertions, 0 deletions
diff --git a/cleopatre/devkit/plcdrv/src/mailbox.c b/cleopatre/devkit/plcdrv/src/mailbox.c
new file mode 100644
index 0000000000..0574d64557
--- /dev/null
+++ b/cleopatre/devkit/plcdrv/src/mailbox.c
@@ -0,0 +1,526 @@
+/* Cleopatre project {{{
+ *
+ * Copyright (C) 2008 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file mailbox.c
+ * \brief Mailbox Layer
+ * \ingroup Cleopatre - PlcDrv
+ *
+ * This file content the mailbox layer, this layer will
+ * provide all mechanisms to manage mailboxes.
+ */
+
+#include "common.h"
+#include "linux_drv.h"
+#include "processing.h"
+#include "mailbox.h"
+#include "hal.h"
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+
+#ifdef __UTESTS__
+/* TODO : move this ! */
+uint32_t mbx_send_result[2];
+uint32_t mbx_buffer_add_result[2];
+uint32_t mbx_rx_ptr_result;
+#endif
+
+/** Define Debug/Trace Level */
+#define TRACE(...) if(test_bit(TRACE_MAIL, (const volatile unsigned long*)&trace)) printk(KERN_INFO "SPC300: MAIL: " __VA_ARGS__)
+
+/** Data parameters */
+#define MBX_MSG_DATA_TYPE_DATA 0
+#define MBX_MSG_DATA_TYPE_MME 1
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t param_type :1;
+ uint32_t param_length :11;
+ uint32_t param_prio :3;
+ uint32_t reserved :5;
+} __attribute__ ((__packed__)) mbx_data_hdr_t;
+
+/** Buffer add parameters */
+#define MBX_MSG_BUFF_ADD_TYPE_DATA 0
+#define MBX_MSG_BUFF_ADD_TYPE_MME 1
+#define MBX_MSG_BUFF_ADD_TYPE_INTERFACE 2
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t param_type :2;
+ uint32_t reserved :18;
+} __attribute__ ((__packed__)) mbx_buffer_add_hdr_t;
+
+/** Send done parameters */
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t reserved :20;
+} __attribute__ ((__packed__)) mbx_send_done_hdr_t;
+
+/** Interface parameters */
+#define MBX_MSG_INTERFACE_TYPE_INTERFACE 0
+#define MBX_MSG_INTERFACE_TYPE_SNIFFER 1
+#define MBX_MSG_INTERFACE_TYPE_FCALL 2
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t param_type :8;
+ uint32_t param_length :11;
+ uint32_t reserved :1;
+} __attribute__ ((__packed__)) mbx_interface_hdr_t;
+
+/** Debug dump parameters. */
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t param_length :16;
+ uint32_t reserved :4;
+} __attribute__ ((__packed__)) mbx_debug_dump_hdr_t;
+
+/** Generic parameters */
+typedef struct {
+ uint32_t type :8;
+ uint32_t length :4;
+ uint32_t raw :20;
+} __attribute__ ((__packed__)) mbx_generic_hdr_t;
+
+/** Mailbox type */
+#define MBX_MSG_TYPE_DATA 0
+#define MBX_MSG_TYPE_BUFFER_ADD 1
+#define MBX_MSG_TYPE_SEND_DONE 2
+#define MBX_MSG_TYPE_INTERFACE 3
+#define MBX_MSG_TYPE_DEBUG_DUMP 0x40
+
+/** Mailbox Header Structure */
+typedef union {
+ mbx_data_hdr_t data;
+ mbx_buffer_add_hdr_t buffer_add;
+ mbx_send_done_hdr_t send_done;
+ mbx_interface_hdr_t interface;
+ mbx_debug_dump_hdr_t debug_dump;
+ mbx_generic_hdr_t generic;
+} mbx_hdr_t;
+
+/** Mailbox Data Structure */
+typedef union {
+ uint32_t buffer_ptr;
+ uint32_t further_use;
+} __attribute__ ((__packed__)) mbx_data_t;
+
+/** Mailbox Structure */
+typedef struct {
+ mbx_hdr_t header;
+ mbx_data_t data;
+} __attribute__ ((__packed__)) mbx_t;
+
+
+/** Our global HAL context pointer */
+struct halctx *ctx;
+
+/**
+ * Initialize the mailbox layer.
+ *
+ * \param info initialisation structure.
+ * \return error code.
+ */
+int mailbox_init(struct init_info *info, struct net_device *dev)
+{
+ struct net_priv *priv;
+ //Check arguments
+ if(info == NULL)
+ return -1;
+
+ if(dev == NULL)
+ return -1;
+ priv = (struct net_priv *)dev->priv;
+ if(priv == NULL)
+ return -1;
+
+ //Initialize registers
+ if((ctx = halmbx_init(info))==NULL)
+ return -1;
+
+ priv->halctx = ctx;
+
+ //Initialize spin lock.
+ spin_lock_init (&ctx->lock);
+
+ //Stop all interrupts
+ A2La_it_disable(ctx);
+ L2At_it_disable(ctx);
+ L2Awd_it_disable(ctx);
+
+ //Clear interrupts
+ clr_A2La_interrupt(ctx);
+ clr_L2At_interrupt(ctx);
+ clr_L2Awd_interrupt(ctx);
+
+ //Unmask sending interrupts
+ L2At_it_enable(ctx);
+
+ L2Awd_it_enable (ctx);
+ return 0;
+}// mailbox_init
+
+/**
+ * UnInitialize the mailbox layer.
+ *
+ * \return error code.
+ */
+int mailbox_uninit(void)
+{
+ //Mask all interrupts
+ A2La_it_disable(ctx);
+ L2At_it_disable(ctx);
+ L2Awd_it_disable(ctx);
+
+ //Uninit lower layer
+ return halmbx_uninit(ctx);
+}// mailbox_uninit
+
+/**
+ * Give the message to hardware.
+ *
+ * \param msg message pointer.
+ * \param size size of message.
+ * \return error code or status queue.
+ */
+int internal_mailbox_send(uint32_t *msg, int size)
+{
+ int status;
+ unsigned long flags;
+
+ //Lock access to the mailbox.
+ spin_lock_irqsave(&ctx->lock, flags);
+
+ //Check pointer
+ if(msg == NULL)
+ {
+ status = -1;
+ goto internal_mailbox_send_out;
+ }
+
+ //Check room
+ status = halmbx_A2Lmail_status_queue(ctx);
+ if(status == NEARLY_FULL || status == FULL)
+ {
+ //The mailbox is nearly full stop upper layers
+ //and activate tx_ack interrupt
+ clr_A2La_interrupt(ctx);
+ A2La_it_enable(ctx);
+ if(status == FULL)
+ goto internal_mailbox_send_out;
+ }
+ else
+ {
+ A2La_it_disable(ctx);
+ }
+
+ //Copy to ring buffer
+ if(halmbx_copy_to_ring(ctx, msg, size))
+ {
+ status = -1;
+ goto internal_mailbox_send_out;
+ }
+
+ //Update the ring management
+ if(halmbx_A2Lmail_update(ctx, size))
+ {
+ status = -1;
+ goto internal_mailbox_send_out;
+ }
+
+internal_mailbox_send_out:
+ //Unlock spin lock and irq.
+ spin_unlock_irqrestore(&ctx->lock, flags);
+ return status;
+}// internal_mailbox_send
+
+/**
+ * Prepare the mailbox message
+ * corresponding to a sending frame
+ * and send it to CESAR.
+ *
+ * \param pointer the data message pointer.
+ * \param length the length of the data message.
+ * \param prio message priority.
+ * \param type type of message.
+ * \return error code.
+ */
+int mailbox_send(void *pointer, uint32_t length, uint32_t prio, enum buffer_type type)
+{
+ mbx_t msg;
+
+ //Check pointer and length
+ if(pointer == NULL || length == 0)
+ return -1;
+
+ //Check prio range
+ if(prio > 7)
+ return -1;
+
+ //Reset message variable
+ memset(&msg, 0, sizeof(msg));
+
+ //Header construction
+ switch(type)
+ {
+ case DATA:
+ msg.header.data.type = MBX_MSG_TYPE_DATA;
+ msg.header.data.length = 1;
+ TRACE("SEND data data\n");
+ msg.header.data.param_type = MBX_MSG_DATA_TYPE_DATA;
+ msg.header.data.param_length = length;
+ msg.header.data.param_prio = prio;
+ break;
+ case MME:
+ msg.header.data.type = MBX_MSG_TYPE_DATA;
+ msg.header.data.length = 1;
+ TRACE("SEND data mme\n");
+ msg.header.data.param_type = MBX_MSG_DATA_TYPE_MME;
+ msg.header.data.param_length = length;
+ msg.header.data.param_prio = prio;
+ break;
+ case INTERFACE:
+ msg.header.interface.type = MBX_MSG_TYPE_INTERFACE;
+ msg.header.interface.length = 1;
+ TRACE("SEND interface\n");
+ msg.header.interface.param_type = MBX_MSG_INTERFACE_TYPE_FCALL;
+ msg.header.interface.param_length = length;
+ break;
+ default:
+ return -1;
+ }
+
+ //Data construction
+ msg.data.buffer_ptr = (uint32_t)pointer;
+
+#ifdef __UTESTS__
+ //For Utests we need to return the msg to check it
+ memcpy(mbx_send_result, &msg, sizeof(mbx_send_result));
+#endif
+
+ //Send the message
+ //we consider that all sending
+ //messages(data or interface)
+ //have field length at the same position
+ return internal_mailbox_send((uint32_t*)&msg, sizeof(mbx_hdr_t) + (msg.header.generic.length * sizeof(uint32_t)));
+}// mailbox_send
+
+/**
+ * Prepare the mailbox message
+ * corresponding to a new buffer
+ * and send it to CESAR.
+ *
+ * \param pointer the buffer pointer.
+ * \param type type of message.
+ * \return error code.
+ */
+int mailbox_buffer_add(void *pointer, enum buffer_type type)
+{
+ mbx_t msg;
+
+ //Check pointer
+ if(pointer == NULL)
+ return -1;
+
+ //Reset message variable
+ memset(&msg, 0, sizeof(msg));
+
+ //Header construction
+ msg.header.buffer_add.type = MBX_MSG_TYPE_BUFFER_ADD;
+ msg.header.buffer_add.length = 1;
+ switch(type)
+ {
+ case DATA:
+ TRACE("SEND BUFF ADD type data\n");
+ msg.header.buffer_add.param_type = MBX_MSG_BUFF_ADD_TYPE_DATA;
+ break;
+ case MME:
+ TRACE("SEND BUFF ADD type mme\n");
+ msg.header.buffer_add.param_type = MBX_MSG_BUFF_ADD_TYPE_MME;
+ break;
+ case INTERFACE:
+ TRACE("SEND BUFF ADD type interface\n");
+ msg.header.buffer_add.param_type = MBX_MSG_BUFF_ADD_TYPE_INTERFACE;
+ break;
+ default:
+ return -1;
+ }
+
+ //Data construction
+ msg.data.buffer_ptr = (uint32_t)pointer;
+
+#ifdef __UTESTS__
+ //For Utests we need to return the msg to check it
+ memcpy(mbx_buffer_add_result, &msg, sizeof(mbx_buffer_add_result));
+#endif
+
+ //Send the message
+ return internal_mailbox_send((uint32_t*)&msg, sizeof(mbx_hdr_t) + (msg.header.buffer_add.length * sizeof(uint32_t)));
+}// mailbox_buffer_add
+
+int mailbox_send_debug_dump(void *buffer, uint length)
+{
+ mbx_t msg;
+
+ //Check parameters.
+ BUG_ON(buffer == NULL || length == 0 || length > (u16)(-1));
+
+ //Ensure message is clean.
+ memset(&msg, 0, sizeof(msg));
+
+ //Fill header of message.
+ msg.header.debug_dump.type = MBX_MSG_TYPE_DEBUG_DUMP;
+ msg.header.debug_dump.length = 1;
+ msg.header.debug_dump.param_length = length;
+
+ //Fill data.
+ msg.data.buffer_ptr = (uint32_t)buffer;
+
+ //Send the message.
+ return internal_mailbox_send
+ ((uint32_t*)&msg,
+ sizeof(mbx_hdr_t) + (msg.header.generic.length * sizeof(uint32_t)));
+}
+
+/**
+ * Manage the Hardware after knowing
+ * that a txdone arrived.
+ *
+ * \return error code.
+ */
+int mailbox_txdone(void)
+{
+ //Just clear the interrupt
+ clr_A2La_interrupt(ctx);
+ return 0;
+}
+
+/**
+ * Receive everything from the HLE layer.
+ *
+ * \param budget budget before giving hand to caller
+ * \return error code.
+ */
+int mailbox_receive(unsigned int *budget)
+{
+ mbx_t msg;
+ int result=0;
+
+ while(halmbx_L2Amail_not_empty_queue(ctx) && *budget)
+ {
+ (*budget)--;
+
+ //Copy the new message from the ring buffer
+ //we copy the max size of a message even it's to big
+ if(halmbx_copy_from_ring(ctx, (uint32_t*)&msg, sizeof(msg)))
+ {
+ return -2;
+ }
+
+ //Update the ring management
+ //we consider that all received
+ //messages(data, send_done or interface)
+ //have field length at the same position
+ if(halmbx_L2Amail_update(ctx, sizeof(mbx_hdr_t) + (msg.header.generic.length * sizeof(uint32_t))))
+ {
+ return -3;
+ }
+
+ //Ring correctly updated
+ //say to Leon that the msg is correctly sent
+ set_L2Aa_interrupt(ctx);
+
+ //Proceed the message
+ //we consider that all received
+ //messages(data, send_done or interface)
+ //have field type at the same position
+ switch(msg.header.generic.type)
+ {
+ case MBX_MSG_TYPE_SEND_DONE:
+ TRACE("RECEIVE a send_done\n");
+ if(processing_buffer_free((void*)msg.data.buffer_ptr))
+ {
+ printk (KERN_WARNING "plc mailbox: Rx SEND_DONE failed\n");
+ result = -4;
+ }
+ else
+ {
+ result = 0;
+ }
+ break;
+
+ case MBX_MSG_TYPE_INTERFACE:
+ TRACE("RECEIVE an interface\n");
+ //Transmit to the upper layers
+ if(processing_receive((void*)msg.data.buffer_ptr, msg.header.interface.param_length, INTERFACE))
+ {
+ result = -5;
+ break;
+ }
+ result = 0;
+ break;
+
+ case MBX_MSG_TYPE_DATA:
+ //Transmit to the upper layers
+ if(msg.header.data.param_type == MBX_MSG_DATA_TYPE_MME)
+ {
+ TRACE("RECEIVE a data mme\n");
+ if(processing_receive((void*)msg.data.buffer_ptr, msg.header.data.param_length, MME))
+ {
+ printk (KERN_WARNING "plc mailbox: Rx DATA failed\n");
+ result = -6;
+ break;
+ }
+ }
+ else
+ {
+ TRACE("RECEIVE a data data\n");
+ if(processing_receive((void*)msg.data.buffer_ptr, msg.header.data.param_length, DATA))
+ {
+ result = -7;
+ break;
+ }
+ }
+ result = 0;
+ break;
+ case MBX_MSG_TYPE_DEBUG_DUMP:
+ if(processing_debug_dump_buffer_receive((void*)msg.data.buffer_ptr,
+ msg.header.debug_dump.param_length))
+ {
+ result = -8;
+ break;
+ }
+ result = 0;
+ break;
+ default:
+ result = -1;
+ break;
+ }
+ if(result)
+ break;
+ }
+
+ return result;
+}// mailbox_receive
+
+/**
+ * Receive part for interrupt context.
+ */
+void mailbox_receive_isr(void)
+{
+ //Only clear interrupt.
+ clr_L2At_interrupt(ctx);
+
+ //Receive work is done under mailbox_receive function
+ //mailbox_receive will be called in a non IRQ context
+
+}// mailbox_receive_isr
+