986 lines
23 KiB
C
986 lines
23 KiB
C
/* mailbox kernel services */
|
|
|
|
/*
|
|
* Copyright (c) 1997-2014 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <microkernel.h>
|
|
#include <string.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
|
|
#include <micro_private.h>
|
|
|
|
#include <misc/__assert.h>
|
|
#include <misc/util.h>
|
|
|
|
/**
|
|
*
|
|
* @brief Determines if mailbox message is synchronous or asynchronous
|
|
*
|
|
* Returns a non-zero value if the specified message contains a valid pool ID,
|
|
* indicating that it is an asynchronous message.
|
|
*/
|
|
|
|
#define ISASYNCMSG(message) ((message)->tx_block.poolid != 0)
|
|
|
|
/**
|
|
*
|
|
* @brief Copy a packet
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
static void copy_packet(struct k_args **out, struct k_args *in)
|
|
{
|
|
GETARGS(*out);
|
|
|
|
/*
|
|
* Copy the data from <in> to <*out> and create
|
|
* a backpointer to the original packet.
|
|
*/
|
|
|
|
memcpy(*out, in, sizeof(struct k_args));
|
|
(*out)->Ctxt.args = in;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Determine if there is a match between the mailbox sender and receiver
|
|
*
|
|
* @return matched message size, or -1 if no match
|
|
*/
|
|
|
|
static int match(struct k_args *Reader, struct k_args *Writer)
|
|
{
|
|
if ((Reader->Args.m1.mess.tx_task == ANYTASK ||
|
|
Reader->Args.m1.mess.tx_task == Writer->Args.m1.mess.tx_task) &&
|
|
(Writer->Args.m1.mess.rx_task == ANYTASK ||
|
|
Writer->Args.m1.mess.rx_task == Reader->Args.m1.mess.rx_task)) {
|
|
if (!ISASYNCMSG(&(Writer->Args.m1.mess))) {
|
|
int32_t info;
|
|
|
|
Reader->Args.m1.mess.tx_task =
|
|
Writer->Args.m1.mess.tx_task;
|
|
|
|
Writer->Args.m1.mess.rx_task =
|
|
Reader->Args.m1.mess.rx_task;
|
|
|
|
info = Reader->Args.m1.mess.info;
|
|
Reader->Args.m1.mess.info = Writer->Args.m1.mess.info;
|
|
Writer->Args.m1.mess.info = info;
|
|
} else {
|
|
Reader->Args.m1.mess.tx_task =
|
|
Writer->Args.m1.mess.tx_task;
|
|
Reader->Args.m1.mess.tx_data = NULL;
|
|
Reader->Args.m1.mess.tx_block =
|
|
Writer->Args.m1.mess.tx_block;
|
|
Reader->Args.m1.mess.info = Writer->Args.m1.mess.info;
|
|
}
|
|
|
|
if (Reader->Args.m1.mess.size > Writer->Args.m1.mess.size) {
|
|
Reader->Args.m1.mess.size = Writer->Args.m1.mess.size;
|
|
} else {
|
|
Writer->Args.m1.mess.size = Reader->Args.m1.mess.size;
|
|
}
|
|
|
|
/*
|
|
* The __ASSERT_NO_MSG() statements are used to verify that
|
|
* the -1 will not be returned when there is a match.
|
|
*/
|
|
|
|
__ASSERT_NO_MSG(Writer->Args.m1.mess.size ==
|
|
Reader->Args.m1.mess.size);
|
|
|
|
__ASSERT_NO_MSG((uint32_t)(-1) != Reader->Args.m1.mess.size);
|
|
|
|
return Reader->Args.m1.mess.size;
|
|
}
|
|
|
|
return -1; /* There was no match */
|
|
}
|
|
|
|
/**
|
|
*
|
|
* prepare_transfer -
|
|
*
|
|
* @return true or false
|
|
*/
|
|
|
|
static bool prepare_transfer(struct k_args *move,
|
|
struct k_args *reader,
|
|
struct k_args *writer)
|
|
{
|
|
/* extract info from writer and reader before they change: */
|
|
|
|
/*
|
|
* prepare writer and reader cmd packets for 'return':
|
|
* (this is shared code, irrespective of the value of 'move')
|
|
*/
|
|
__ASSERT_NO_MSG(NULL == reader->Forw);
|
|
reader->Comm = RECV_ACK;
|
|
reader->Time.rcode = RC_OK;
|
|
|
|
__ASSERT_NO_MSG(NULL == writer->Forw);
|
|
writer->alloc = true;
|
|
|
|
writer->Comm = SEND_ACK;
|
|
writer->Time.rcode = RC_OK;
|
|
|
|
if (move) {
|
|
/* { move != NULL, which means full data exchange } */
|
|
|
|
bool all_data_present = true;
|
|
move->Comm = MVD_REQ;
|
|
/*
|
|
* transfer the data with the highest
|
|
* priority of reader and writer
|
|
*/
|
|
move->Prio = max(writer->Prio, reader->Prio);
|
|
move->Ctxt.proc = NULL;
|
|
move->Args.MovedReq.Action =
|
|
(MovedAction)(MVDACT_SNDACK | MVDACT_RCVACK);
|
|
move->Args.MovedReq.iTotalSize = writer->Args.m1.mess.size;
|
|
move->Args.MovedReq.Extra.Setup.ContSnd = NULL;
|
|
move->Args.MovedReq.Extra.Setup.ContRcv = NULL;
|
|
|
|
/* reader: */
|
|
if (reader->Args.m1.mess.rx_data == NULL) {
|
|
all_data_present = false;
|
|
__ASSERT_NO_MSG(0 == reader->Args.m1.mess.extra
|
|
.transfer); /* == extra.sema */
|
|
reader->Args.m1.mess.extra.transfer = move;
|
|
/*SENDARGS(reader); */
|
|
} else {
|
|
move->Args.MovedReq.destination =
|
|
reader->Args.m1.mess.rx_data;
|
|
writer->Args.m1.mess.rx_data =
|
|
reader->Args.m1.mess.rx_data;
|
|
|
|
/* chain the reader */
|
|
move->Args.MovedReq.Extra.Setup.ContRcv = reader;
|
|
}
|
|
|
|
/* writer: */
|
|
if (ISASYNCMSG(&(writer->Args.m1.mess))) {
|
|
move->Args.MovedReq.source =
|
|
writer->Args.m1.mess.tx_block.pointer_to_data;
|
|
reader->Args.m1.mess.tx_block =
|
|
writer->Args.m1.mess.tx_block;
|
|
} else {
|
|
__ASSERT_NO_MSG(NULL != writer->Args.m1.mess.tx_data);
|
|
move->Args.MovedReq.source =
|
|
writer->Args.m1.mess.tx_data;
|
|
reader->Args.m1.mess.tx_data =
|
|
writer->Args.m1.mess.tx_data;
|
|
}
|
|
/* chain the writer */
|
|
move->Args.MovedReq.Extra.Setup.ContSnd = writer;
|
|
|
|
return all_data_present;
|
|
} else {
|
|
/* { NULL == move, which means header exchange only } */
|
|
return 0; /* == don't care actually */
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* transfer -
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
static void transfer(struct k_args *pMvdReq)
|
|
{
|
|
__ASSERT_NO_MSG(NULL != pMvdReq->Args.MovedReq.source);
|
|
__ASSERT_NO_MSG(NULL != pMvdReq->Args.MovedReq.destination);
|
|
|
|
_k_movedata_request(pMvdReq);
|
|
FREEARGS(pMvdReq);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process the acknowledgment to a mailbox send request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_send_ack(struct k_args *pCopyWriter)
|
|
{
|
|
if (ISASYNCMSG(&(pCopyWriter->Args.m1.mess))) {
|
|
if (pCopyWriter->Args.m1.mess.extra.sema) {
|
|
/*
|
|
* Signal the semaphore. Alternatively, this could
|
|
* be done using the continuation mechanism.
|
|
*/
|
|
|
|
struct k_args A;
|
|
#ifndef NO_KARG_CLEAR
|
|
memset(&A, 0xfd, sizeof(struct k_args));
|
|
#endif
|
|
A.Comm = SIGNALS;
|
|
A.Args.s1.sema = pCopyWriter->Args.m1.mess.extra.sema;
|
|
_k_sem_signal(&A);
|
|
}
|
|
|
|
/*
|
|
* release the block from the memory pool
|
|
* unless this an asynchronous transfer.
|
|
*/
|
|
|
|
if ((uint32_t)(-1) !=
|
|
pCopyWriter->Args.m1.mess.tx_block.poolid) {
|
|
/*
|
|
* special value to tell if block should be
|
|
* freed or not
|
|
*/
|
|
pCopyWriter->Comm = REL_BLOCK;
|
|
pCopyWriter->Args.p1.poolid =
|
|
pCopyWriter->Args.m1.mess.tx_block.poolid;
|
|
pCopyWriter->Args.p1.rep_poolptr =
|
|
pCopyWriter->Args.m1.mess.tx_block
|
|
.address_in_pool;
|
|
pCopyWriter->Args.p1.rep_dataptr =
|
|
pCopyWriter->Args.m1.mess.tx_block
|
|
.pointer_to_data;
|
|
pCopyWriter->Args.p1.req_size =
|
|
pCopyWriter->Args.m1.mess.tx_block.req_size;
|
|
SENDARGS(pCopyWriter);
|
|
return;
|
|
} else {
|
|
FREEARGS(pCopyWriter);
|
|
return;
|
|
}
|
|
} else {
|
|
struct k_args *Starter;
|
|
|
|
/*
|
|
* Get a pointer to the original command packet of the sender
|
|
* and copy both the result as well as the message information
|
|
* from the received packet of the sender before resetting the
|
|
* TF_SEND and TF_SENDDATA state bits.
|
|
*/
|
|
|
|
Starter = pCopyWriter->Ctxt.args;
|
|
Starter->Time.rcode = pCopyWriter->Time.rcode;
|
|
Starter->Args.m1.mess = pCopyWriter->Args.m1.mess;
|
|
_k_state_bit_reset(Starter->Ctxt.proc, TF_SEND | TF_SENDDATA);
|
|
|
|
FREEARGS(pCopyWriter);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process the timeout for a mailbox send request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_send_reply(struct k_args *pCopyWriter)
|
|
{
|
|
FREETIMER(pCopyWriter->Time.timer);
|
|
REMOVE_ELM(pCopyWriter);
|
|
pCopyWriter->Time.rcode = RC_TIME;
|
|
pCopyWriter->Comm = SEND_ACK;
|
|
SENDARGS(pCopyWriter);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process a mailbox send request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_send_request(struct k_args *Writer)
|
|
{
|
|
kmbox_t MailBoxId = Writer->Args.m1.mess.mailbox;
|
|
struct mbx_struct *MailBox;
|
|
struct k_args *CopyReader;
|
|
struct k_args *CopyWriter;
|
|
struct k_args *temp;
|
|
bool bAsync;
|
|
|
|
bAsync = ISASYNCMSG(&Writer->Args.m1.mess);
|
|
|
|
struct k_proc *sender = NULL;
|
|
|
|
/*
|
|
* Only deschedule the task if it is not a poster
|
|
* (not an asynchronous request).
|
|
*/
|
|
|
|
if (!bAsync) {
|
|
sender = _k_current_task;
|
|
_k_state_bit_set(sender, TF_SEND);
|
|
}
|
|
|
|
Writer->Ctxt.proc = sender;
|
|
|
|
MailBox = _k_mbox_list + OBJ_INDEX(MailBoxId);
|
|
|
|
copy_packet(&CopyWriter, Writer);
|
|
|
|
if (bAsync) {
|
|
/*
|
|
* Clear the [Ctxt] field in an asynchronous request as the
|
|
* original packet will not be available later.
|
|
*/
|
|
|
|
CopyWriter->Ctxt.args = NULL;
|
|
}
|
|
|
|
/*
|
|
* The [Forw] field can be changed later when added to the Writer's
|
|
* list, but when not listed, [Forw] must be NULL.
|
|
*/
|
|
|
|
CopyWriter->Forw = NULL;
|
|
|
|
for (CopyReader = MailBox->Readers, temp = NULL; CopyReader != NULL;
|
|
temp = CopyReader, CopyReader = CopyReader->Forw) {
|
|
uint32_t u32Size;
|
|
|
|
u32Size = match(CopyReader, CopyWriter);
|
|
|
|
if ((uint32_t)(-1) != u32Size) {
|
|
#ifdef CONFIG_OBJECT_MONITOR
|
|
MailBox->Count++;
|
|
#endif
|
|
|
|
/*
|
|
* There is a match. Remove the chosen reader from the
|
|
* list.
|
|
*/
|
|
|
|
if (temp != NULL) {
|
|
temp->Forw = CopyReader->Forw;
|
|
} else {
|
|
MailBox->Readers = CopyReader->Forw;
|
|
}
|
|
CopyReader->Forw = NULL;
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (CopyReader->Time.timer != NULL) {
|
|
/*
|
|
* The reader was trying to handshake with
|
|
* timeout
|
|
*/
|
|
_k_timer_delist(CopyReader->Time.timer);
|
|
FREETIMER(CopyReader->Time.timer);
|
|
}
|
|
#endif
|
|
|
|
if (0 == u32Size) {
|
|
/* No data exchange--header only */
|
|
prepare_transfer(NULL, CopyReader, CopyWriter);
|
|
SENDARGS(CopyReader);
|
|
SENDARGS(CopyWriter);
|
|
} else {
|
|
struct k_args *Moved_req;
|
|
|
|
GETARGS(Moved_req);
|
|
|
|
if (prepare_transfer(Moved_req,
|
|
CopyReader, CopyWriter)) {
|
|
/*
|
|
* <Moved_req> will be
|
|
* cleared as well
|
|
*/
|
|
transfer(Moved_req);
|
|
} else {
|
|
SENDARGS(CopyReader);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* There is no matching receiver for this message. */
|
|
|
|
if (bAsync) {
|
|
/*
|
|
* For asynchronous requests, just post the message into the
|
|
* list and continue. No further action is required.
|
|
*/
|
|
|
|
INSERT_ELM(MailBox->Writers, CopyWriter);
|
|
return;
|
|
}
|
|
|
|
if (CopyWriter->Time.ticks != TICKS_NONE) {
|
|
/*
|
|
* The writer specified a wait or wait with timeout operation.
|
|
*
|
|
* Note: Setting the command to SEND_TMO is only necessary in
|
|
* the wait with timeout case. However, it is more efficient
|
|
* to blindly set it rather than waste time on a comparison.
|
|
*/
|
|
|
|
CopyWriter->Comm = SEND_TMO;
|
|
|
|
/* Put the letter into the mailbox */
|
|
INSERT_ELM(MailBox->Writers, CopyWriter);
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (CopyWriter->Time.ticks == TICKS_UNLIMITED) {
|
|
/* This is a wait operation; there is no timer. */
|
|
CopyWriter->Time.timer = NULL;
|
|
} else {
|
|
/*
|
|
* This is a wait with timeout operation.
|
|
* Enlist a new timeout.
|
|
*/
|
|
_k_timeout_alloc(CopyWriter);
|
|
}
|
|
#endif
|
|
} else {
|
|
/*
|
|
* This is a no-wait operation.
|
|
* Notify the sender of failure.
|
|
*/
|
|
CopyWriter->Comm = SEND_ACK;
|
|
CopyWriter->Time.rcode = RC_FAIL;
|
|
SENDARGS(CopyWriter);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Send a message to a mailbox
|
|
*
|
|
* This routine sends a message to a mailbox and looks for a matching receiver.
|
|
*
|
|
* @return RC_OK, RC_FAIL, RC_TIME on success, failure, timeout respectively
|
|
*/
|
|
|
|
int _task_mbox_put(kmbox_t mbox, /* mailbox */
|
|
kpriority_t prio, /* priority of data transfer */
|
|
struct k_msg *M, /* pointer to message to send */
|
|
int32_t time /* maximum number of ticks to wait */
|
|
)
|
|
{
|
|
struct k_args A;
|
|
|
|
__ASSERT((0 == M->size) || (NULL != M->tx_data),
|
|
"Invalid mailbox data specification\n");
|
|
|
|
if (unlikely((uint32_t)(-1) == M->size)) {
|
|
/* the sender side cannot specify a size of -1 == 0xfff..ff */
|
|
return RC_FAIL;
|
|
}
|
|
|
|
M->tx_task = _k_current_task->Ident;
|
|
M->tx_block.poolid = 0; /* NO ASYNC POST */
|
|
M->extra.sema = 0;
|
|
M->mailbox = mbox;
|
|
|
|
A.Prio = prio;
|
|
A.Comm = SEND_REQ;
|
|
A.Time.ticks = time;
|
|
A.Args.m1.mess = *M;
|
|
|
|
KERNEL_ENTRY(&A);
|
|
|
|
*M = A.Args.m1.mess;
|
|
return A.Time.rcode;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process a mailbox receive acknowledgment
|
|
*
|
|
* This routine processes a mailbox receive acknowledgment.
|
|
*
|
|
* INTERNAL: This routine frees the <pCopyReader> packet
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_receive_ack(struct k_args *pCopyReader)
|
|
{
|
|
struct k_args *Starter;
|
|
|
|
/* Get a pointer to the original command packet of the sender */
|
|
Starter = pCopyReader->Ctxt.args;
|
|
|
|
/* Copy result from received packet */
|
|
Starter->Time.rcode = pCopyReader->Time.rcode;
|
|
|
|
/* And copy the message information from the received packet. */
|
|
Starter->Args.m1.mess = pCopyReader->Args.m1.mess;
|
|
|
|
/* Reschedule the sender task */
|
|
_k_state_bit_reset(Starter->Ctxt.proc, TF_RECV | TF_RECVDATA);
|
|
|
|
FREEARGS(pCopyReader);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process the timeout for a mailbox receive request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_receive_reply(struct k_args *pCopyReader)
|
|
{
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
FREETIMER(pCopyReader->Time.timer);
|
|
REMOVE_ELM(pCopyReader);
|
|
pCopyReader->Time.rcode = RC_TIME;
|
|
pCopyReader->Comm = RECV_ACK;
|
|
SENDARGS(pCopyReader);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process a mailbox receive request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_receive_request(struct k_args *Reader)
|
|
{
|
|
kmbox_t MailBoxId = Reader->Args.m1.mess.mailbox;
|
|
struct mbx_struct *MailBox;
|
|
struct k_args *CopyWriter;
|
|
struct k_args *temp;
|
|
struct k_args *CopyReader;
|
|
|
|
Reader->Ctxt.proc = _k_current_task;
|
|
_k_state_bit_set(Reader->Ctxt.proc, TF_RECV);
|
|
|
|
copy_packet(&CopyReader, Reader);
|
|
|
|
/*
|
|
* The [Forw] field can be changed later when added to the Reader's
|
|
* list, but when not listed, [Forw] must be NULL.
|
|
*/
|
|
|
|
CopyReader->Forw = NULL;
|
|
|
|
MailBox = _k_mbox_list + OBJ_INDEX(MailBoxId);
|
|
|
|
for (CopyWriter = MailBox->Writers, temp = NULL; CopyWriter != NULL;
|
|
temp = CopyWriter, CopyWriter = CopyWriter->Forw) {
|
|
uint32_t u32Size;
|
|
|
|
u32Size = match(CopyReader, CopyWriter);
|
|
|
|
if ((uint32_t)(-1) != u32Size) {
|
|
#ifdef CONFIG_OBJECT_MONITOR
|
|
MailBox->Count++;
|
|
#endif
|
|
|
|
/*
|
|
* There is a match. Remove the chosen reader
|
|
* from the list.
|
|
*/
|
|
|
|
if (temp != NULL) {
|
|
temp->Forw = CopyWriter->Forw;
|
|
} else {
|
|
MailBox->Writers = CopyWriter->Forw;
|
|
}
|
|
CopyWriter->Forw = NULL;
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (CopyWriter->Time.timer != NULL) {
|
|
/*
|
|
* The writer was trying to handshake with
|
|
* timeout.
|
|
*/
|
|
_k_timer_delist(CopyWriter->Time.timer);
|
|
FREETIMER(CopyWriter->Time.timer);
|
|
}
|
|
#endif
|
|
|
|
if (0 == u32Size) {
|
|
/* No data exchange--header only */
|
|
prepare_transfer(NULL, CopyReader, CopyWriter);
|
|
SENDARGS(CopyReader);
|
|
SENDARGS(CopyWriter);
|
|
} else {
|
|
struct k_args *Moved_req;
|
|
|
|
GETARGS(Moved_req);
|
|
|
|
if (prepare_transfer(Moved_req,
|
|
CopyReader, CopyWriter)) {
|
|
/*
|
|
* <Moved_req> will be
|
|
* cleared as well
|
|
*/
|
|
transfer(Moved_req);
|
|
} else {
|
|
SENDARGS(CopyReader);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* There is no matching writer for this message. */
|
|
|
|
if (Reader->Time.ticks != TICKS_NONE) {
|
|
/*
|
|
* The writer specified a wait or wait with timeout operation.
|
|
*
|
|
* Note: Setting the command to RECV_TMO is only necessary in
|
|
* the wait with timeout case. However, it is more efficient
|
|
* to blindly set it rather than waste time on a comparison.
|
|
*/
|
|
|
|
CopyReader->Comm = RECV_TMO;
|
|
|
|
/* Put the letter into the mailbox */
|
|
INSERT_ELM(MailBox->Readers, CopyReader);
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (CopyReader->Time.ticks == TICKS_UNLIMITED) {
|
|
/* This is a wait operation; there is no timer. */
|
|
CopyReader->Time.timer = NULL;
|
|
} else {
|
|
/*
|
|
* This is a wait with timeout operation.
|
|
* Enlist a new timeout.
|
|
*/
|
|
_k_timeout_alloc(CopyReader);
|
|
}
|
|
#endif
|
|
} else {
|
|
/*
|
|
* This is a no-wait operation.
|
|
* Notify the receiver of failure.
|
|
*/
|
|
CopyReader->Comm = RECV_ACK;
|
|
CopyReader->Time.rcode = RC_FAIL;
|
|
SENDARGS(CopyReader);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Gets struct k_msg message header structure information
|
|
* from a mailbox
|
|
*
|
|
* @return RC_OK, RC_FAIL, RC_TIME on success, failure, timeout respectively
|
|
*/
|
|
|
|
int _task_mbox_get(kmbox_t mbox, /* mailbox */
|
|
struct k_msg *M, /* pointer to message */
|
|
int32_t time /* maximum number of ticks to wait */
|
|
)
|
|
{
|
|
struct k_args A;
|
|
|
|
M->rx_task = _k_current_task->Ident;
|
|
M->mailbox = mbox;
|
|
M->extra.transfer = 0;
|
|
|
|
/*
|
|
* NOTE: to make sure there is no conflict with extra.sema,
|
|
* there is an assertion check in prepare_transfer() if equal to 0
|
|
*/
|
|
|
|
A.Prio = _k_current_task->Prio;
|
|
A.Comm = RECV_REQ;
|
|
A.Time.ticks = time;
|
|
A.Args.m1.mess = *M;
|
|
|
|
KERNEL_ENTRY(&A);
|
|
*M = A.Args.m1.mess;
|
|
return A.Time.rcode;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Send a message asynchronously to a mailbox
|
|
*
|
|
* This routine sends a message to a mailbox and does not wait for a matching
|
|
* receiver. There is no exchange header returned to the sender. When the data
|
|
* has been transferred to the receiver, the semaphore signaling is performed.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _task_mbox_put_async(kmbox_t mbox, /* mailbox to which to send message */
|
|
kpriority_t prio, /* priority of data transfer */
|
|
struct k_msg *M, /* pointer to message to send */
|
|
ksem_t sema /* semaphore to signal when transfer is complete */
|
|
)
|
|
{
|
|
struct k_args A;
|
|
|
|
__ASSERT(0xFFFFFFFF != M->size, "Invalid mailbox data specification\n");
|
|
|
|
if (0 == M->size) {
|
|
/*
|
|
* trick: special value to indicate that tx_block
|
|
* should NOT be released in the SND_ACK
|
|
*/
|
|
M->tx_block.poolid = (uint32_t)(-1);
|
|
}
|
|
|
|
M->tx_task = _k_current_task->Ident;
|
|
M->tx_data = NULL;
|
|
M->mailbox = mbox;
|
|
M->extra.sema = sema;
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
A.Time.timer = NULL;
|
|
#endif
|
|
A.Prio = prio;
|
|
A.Comm = SEND_REQ;
|
|
A.Args.m1.mess = *M;
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process a mailbox receive data request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_receive_data(struct k_args *Starter)
|
|
{
|
|
struct k_args *CopyStarter;
|
|
struct k_args *MoveD;
|
|
struct k_args *Writer;
|
|
|
|
Starter->Ctxt.proc = _k_current_task;
|
|
_k_state_bit_set(_k_current_task, TF_RECVDATA);
|
|
|
|
GETARGS(CopyStarter);
|
|
memcpy(CopyStarter, Starter, sizeof(struct k_args));
|
|
CopyStarter->Ctxt.args = Starter;
|
|
|
|
MoveD = CopyStarter->Args.m1.mess.extra.transfer;
|
|
CopyStarter->Comm = RECV_ACK;
|
|
CopyStarter->Time.rcode = RC_OK;
|
|
|
|
MoveD->Args.MovedReq.Extra.Setup.ContRcv = CopyStarter;
|
|
CopyStarter->Forw = NULL;
|
|
MoveD->Args.MovedReq.destination = CopyStarter->Args.m1.mess.rx_data;
|
|
|
|
MoveD->Args.MovedReq.iTotalSize = CopyStarter->Args.m1.mess.size;
|
|
|
|
Writer = MoveD->Args.MovedReq.Extra.Setup.ContSnd;
|
|
if (Writer != NULL) {
|
|
if (ISASYNCMSG(&(Writer->Args.m1.mess))) {
|
|
CopyStarter->Args.m1.mess.tx_block =
|
|
Writer->Args.m1.mess.tx_block;
|
|
} else {
|
|
Writer->Args.m1.mess.rx_data =
|
|
CopyStarter->Args.m1.mess.rx_data;
|
|
CopyStarter->Args.m1.mess.tx_data =
|
|
Writer->Args.m1.mess.tx_data;
|
|
}
|
|
transfer(MoveD); /* and MoveD will be cleared as well */
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Get message data
|
|
*
|
|
* This routine is called for either of the two following purposes:
|
|
* 1. To transfer data if the call to task_mbox_get() resulted in a non-zero size
|
|
* field in the struct k_msg header structure.
|
|
* 2. To wake up and release a transmitting task that is blocked on a call to
|
|
* task_mbox_put[wait|wait_timeout]().
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _task_mbox_data_get(struct k_msg *M /* message from which to get data */
|
|
)
|
|
{
|
|
struct k_args A;
|
|
|
|
/* sanity checks */
|
|
if (unlikely(NULL == M->extra.transfer)) {
|
|
/*
|
|
* protection: if a user erroneously calls this function after
|
|
* a task_mbox_get(), we should not run into trouble
|
|
*/
|
|
return;
|
|
}
|
|
|
|
A.Args.m1.mess = *M;
|
|
A.Comm = RECV_DATA;
|
|
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Get the mailbox data and place
|
|
* in a memory pool block
|
|
*
|
|
* @return RC_OK upon success, RC_FAIL upon failure, or RC_TIME upon timeout
|
|
*/
|
|
|
|
int _task_mbox_data_get_async_block(struct k_msg *message,
|
|
struct k_block *rxblock,
|
|
kmemory_pool_t poolid,
|
|
int32_t time)
|
|
{
|
|
int retval;
|
|
struct k_args *MoveD;
|
|
|
|
/* sanity checks: */
|
|
if (NULL == message->extra.transfer) {
|
|
/*
|
|
* If a user erroneously calls this function after a
|
|
* task_mbox_get(), we should not run into trouble.
|
|
* Return RC_OK instead of RC_FAIL to be downwards compatible.
|
|
*/
|
|
|
|
return RC_OK;
|
|
}
|
|
|
|
/* special flow to check for possible optimisations: */
|
|
|
|
if (ISASYNCMSG(message)) {
|
|
/* First transfer block */
|
|
__ASSERT_NO_MSG(-1 != message->tx_block.poolid);
|
|
*rxblock = message->tx_block;
|
|
|
|
/* This is the MOVED packet */
|
|
MoveD = message->extra.transfer;
|
|
|
|
/* Then release sender (writer) */
|
|
|
|
struct k_args *Writer;
|
|
|
|
/*
|
|
* This is the first of the continuation packets for
|
|
* continuation on send. It should be the only one.
|
|
* That is, it should not have any followers. To
|
|
* prevent [tx_block] from being released when the
|
|
* SEND_ACK is processed, change its [poolid] to -1.
|
|
*/
|
|
|
|
Writer = MoveD->Args.MovedReq.Extra.Setup.ContSnd;
|
|
__ASSERT_NO_MSG(NULL != Writer);
|
|
__ASSERT_NO_MSG(NULL == Writer->Forw);
|
|
|
|
Writer->Args.m1.mess.tx_block.poolid = (uint32_t)(-1);
|
|
nano_task_stack_push(&_k_command_stack, (uint32_t)Writer);
|
|
|
|
#ifdef ACTIV_ASSERTS
|
|
struct k_args *Dummy;
|
|
|
|
/*
|
|
* Confirm that there are not any continuation packets
|
|
* for continuation on receive.
|
|
*/
|
|
|
|
Dummy = MoveD->Args.MovedReq.Extra.Setup.ContRcv;
|
|
__ASSERT_NO_MSG(NULL == Dummy);
|
|
#endif
|
|
|
|
FREEARGS(MoveD); /* Clean up MOVED */
|
|
|
|
return RC_OK;
|
|
}
|
|
|
|
/* 'normal' flow of task_mbox_data_get_async_block(): */
|
|
|
|
if (0 != message->size) {
|
|
retval = _task_mem_pool_alloc(rxblock, poolid,
|
|
message->size, time);
|
|
if (retval != RC_OK) {
|
|
return retval;
|
|
}
|
|
message->rx_data = rxblock->pointer_to_data;
|
|
} else {
|
|
rxblock->poolid = (kmemory_pool_t) -1;
|
|
}
|
|
|
|
/*
|
|
* Invoke task_mbox_data_get() core without sanity checks, as they have
|
|
* already been performed.
|
|
*/
|
|
|
|
struct k_args A;
|
|
A.Args.m1.mess = *message;
|
|
A.Comm = RECV_DATA;
|
|
KERNEL_ENTRY(&A);
|
|
|
|
return RC_OK; /* task_mbox_data_get() doesn't return anything */
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Process a mailbox send data request
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _k_mbox_send_data(struct k_args *Starter)
|
|
{
|
|
struct k_args *CopyStarter;
|
|
struct k_args *MoveD;
|
|
struct k_args *Reader;
|
|
|
|
Starter->Ctxt.proc = _k_current_task;
|
|
_k_state_bit_set(_k_current_task, TF_SENDDATA);
|
|
|
|
GETARGS(CopyStarter);
|
|
memcpy(CopyStarter, Starter, sizeof(struct k_args));
|
|
CopyStarter->Ctxt.args = Starter;
|
|
|
|
MoveD = CopyStarter->Args.m1.mess.extra.transfer;
|
|
|
|
CopyStarter->Time.rcode = RC_OK;
|
|
CopyStarter->Comm = SEND_ACK;
|
|
|
|
MoveD->Args.MovedReq.Extra.Setup.ContSnd = CopyStarter;
|
|
CopyStarter->Forw = NULL;
|
|
MoveD->Args.MovedReq.source = CopyStarter->Args.m1.mess.rx_data;
|
|
|
|
Reader = MoveD->Args.MovedReq.Extra.Setup.ContRcv;
|
|
if (Reader != NULL) {
|
|
Reader->Args.m1.mess.rx_data =
|
|
CopyStarter->Args.m1.mess.rx_data;
|
|
CopyStarter->Args.m1.mess.tx_data =
|
|
Reader->Args.m1.mess.tx_data;
|
|
|
|
transfer(MoveD); /* and MoveD will be cleared as well */
|
|
}
|
|
}
|