/* Cleopatre project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \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 #include #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