| /* mailbox kernel services */ |
| |
| /* |
| * Copyright (c) 1997-2014 Wind River Systems, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #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.pool_id != 0) |
| |
| /** |
| * |
| * @brief Copy a packet |
| * |
| * @param in the packet to be copied |
| * @param out the packet to copy to |
| * |
| * @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(Reader->args.m1.mess.size != (uint32_t)(-1)); |
| |
| return Reader->args.m1.mess.size; |
| } |
| |
| return -1; /* There was no match */ |
| } |
| |
| /** |
| * @brief 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(reader->next == NULL); |
| reader->Comm = _K_SVC_MBOX_RECEIVE_ACK; |
| reader->Time.rcode = RC_OK; |
| |
| __ASSERT_NO_MSG(writer->next == NULL); |
| writer->alloc = true; |
| |
| writer->Comm = _K_SVC_MBOX_SEND_ACK; |
| writer->Time.rcode = RC_OK; |
| |
| if (move) { |
| /* { move != NULL, which means full data exchange } */ |
| bool all_data_present = true; |
| |
| move->Comm = _K_SVC_MOVEDATA_REQ; |
| /* |
| * transfer the data with the highest |
| * priority of reader and writer |
| */ |
| move->priority = max(writer->priority, reader->priority); |
| move->Ctxt.task = NULL; |
| move->args.moved_req.action = |
| (MovedAction)(MVDACT_SNDACK | MVDACT_RCVACK); |
| move->args.moved_req.total_size = writer->args.m1.mess.size; |
| move->args.moved_req.extra.setup.continuation_send = NULL; |
| move->args.moved_req.extra.setup.continuation_receive = 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.moved_req.destination = |
| reader->args.m1.mess.rx_data; |
| writer->args.m1.mess.rx_data = |
| reader->args.m1.mess.rx_data; |
| |
| /* chain the reader */ |
| move->args.moved_req.extra.setup.continuation_receive = reader; |
| } |
| |
| /* writer: */ |
| if (ISASYNCMSG(&(writer->args.m1.mess))) { |
| move->args.moved_req.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(writer->args.m1.mess.tx_data != NULL); |
| move->args.moved_req.source = |
| writer->args.m1.mess.tx_data; |
| reader->args.m1.mess.tx_data = |
| writer->args.m1.mess.tx_data; |
| } |
| /* chain the writer */ |
| move->args.moved_req.extra.setup.continuation_send = writer; |
| |
| return all_data_present; |
| } |
| |
| /* { NULL == move, which means header exchange only } */ |
| return 0; /* == don't care actually */ |
| } |
| |
| /** |
| * @brief Do transfer |
| * |
| * @return N/A |
| */ |
| static void transfer(struct k_args *pMvdReq) |
| { |
| __ASSERT_NO_MSG(pMvdReq->args.moved_req.source != NULL); |
| __ASSERT_NO_MSG(pMvdReq->args.moved_req.destination != NULL); |
| |
| _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) |
| { |
| struct k_args *Starter; |
| |
| 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 = _K_SVC_SEM_SIGNAL; |
| 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 (pCopyWriter->args.m1.mess.tx_block.pool_id != (uint32_t)(-1)) { |
| /* |
| * special value to tell if block should be |
| * freed or not |
| */ |
| pCopyWriter->Comm = _K_SVC_MEM_POOL_BLOCK_RELEASE; |
| pCopyWriter->args.p1.pool_id = |
| pCopyWriter->args.m1.mess.tx_block.pool_id; |
| 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; |
| } |
| |
| FREEARGS(pCopyWriter); |
| return; |
| } |
| |
| /* |
| * 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.task, 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 = _K_SVC_MBOX_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 _k_mbox_struct *MailBox; |
| struct k_args *CopyReader; |
| struct k_args *CopyWriter; |
| struct k_args *temp; |
| bool bAsync; |
| |
| bAsync = ISASYNCMSG(&Writer->args.m1.mess); |
| |
| struct k_task *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.task = sender; |
| |
| MailBox = (struct _k_mbox_struct *)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 [next] field can be changed later when added to the Writer's |
| * list, but when not listed, [next] must be NULL. |
| */ |
| |
| CopyWriter->next = NULL; |
| |
| for (CopyReader = MailBox->readers, temp = NULL; CopyReader != NULL; |
| temp = CopyReader, CopyReader = CopyReader->next) { |
| uint32_t u32Size; |
| |
| u32Size = match(CopyReader, CopyWriter); |
| |
| if (u32Size != (uint32_t)(-1)) { |
| #ifdef CONFIG_OBJECT_MONITOR |
| MailBox->count++; |
| #endif |
| |
| /* |
| * There is a match. Remove the chosen reader from the |
| * list. |
| */ |
| |
| if (temp != NULL) { |
| temp->next = CopyReader->next; |
| } else { |
| MailBox->readers = CopyReader->next; |
| } |
| CopyReader->next = 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 (u32Size == 0) { |
| /* 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 = _K_SVC_MBOX_SEND_REPLY; |
| |
| /* 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 = _K_SVC_MBOX_SEND_ACK; |
| CopyWriter->Time.rcode = RC_FAIL; |
| SENDARGS(CopyWriter); |
| } |
| } |
| |
| int task_mbox_put(kmbox_t mbox, kpriority_t prio, struct k_msg *M, int32_t timeout) |
| { |
| struct k_args A; |
| |
| __ASSERT((M->size == 0) || (M->tx_data != NULL), |
| "Invalid mailbox data specification\n"); |
| |
| if (unlikely(M->size == (uint32_t)(-1))) { |
| /* the sender side cannot specify a size of -1 == 0xfff..ff */ |
| return RC_FAIL; |
| } |
| |
| M->tx_task = _k_current_task->id; |
| M->tx_block.pool_id = 0; /* NO ASYNC POST */ |
| M->extra.sema = 0; |
| M->mailbox = mbox; |
| |
| A.priority = prio; |
| A.Comm = _K_SVC_MBOX_SEND_REQUEST; |
| A.Time.ticks = timeout; |
| 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.task, 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 = _K_SVC_MBOX_RECEIVE_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 _k_mbox_struct *MailBox; |
| struct k_args *CopyWriter; |
| struct k_args *temp; |
| struct k_args *CopyReader; |
| |
| Reader->Ctxt.task = _k_current_task; |
| _k_state_bit_set(Reader->Ctxt.task, TF_RECV); |
| |
| copy_packet(&CopyReader, Reader); |
| |
| /* |
| * The [next] field can be changed later when added to the Reader's |
| * list, but when not listed, [next] must be NULL. |
| */ |
| |
| CopyReader->next = NULL; |
| |
| MailBox = (struct _k_mbox_struct *)MailBoxId; |
| |
| for (CopyWriter = MailBox->writers, temp = NULL; CopyWriter != NULL; |
| temp = CopyWriter, CopyWriter = CopyWriter->next) { |
| uint32_t u32Size; |
| |
| u32Size = match(CopyReader, CopyWriter); |
| |
| if (u32Size != (uint32_t)(-1)) { |
| #ifdef CONFIG_OBJECT_MONITOR |
| MailBox->count++; |
| #endif |
| |
| /* |
| * There is a match. Remove the chosen reader |
| * from the list. |
| */ |
| |
| if (temp != NULL) { |
| temp->next = CopyWriter->next; |
| } else { |
| MailBox->writers = CopyWriter->next; |
| } |
| CopyWriter->next = 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 (u32Size == 0) { |
| /* 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 = _K_SVC_MBOX_RECEIVE_REPLY; |
| |
| /* 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 = _K_SVC_MBOX_RECEIVE_ACK; |
| CopyReader->Time.rcode = RC_FAIL; |
| SENDARGS(CopyReader); |
| } |
| } |
| |
| |
| int task_mbox_get(kmbox_t mbox, struct k_msg *M, int32_t timeout) |
| { |
| struct k_args A; |
| |
| M->rx_task = _k_current_task->id; |
| 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.priority = _k_current_task->priority; |
| A.Comm = _K_SVC_MBOX_RECEIVE_REQUEST; |
| A.Time.ticks = timeout; |
| A.args.m1.mess = *M; |
| |
| KERNEL_ENTRY(&A); |
| *M = A.args.m1.mess; |
| return A.Time.rcode; |
| } |
| |
| |
| void _task_mbox_block_put(kmbox_t mbox, |
| kpriority_t prio, |
| struct k_msg *M, |
| ksem_t sema) |
| { |
| struct k_args A; |
| |
| __ASSERT(0xFFFFFFFF != M->size, "Invalid mailbox data specification\n"); |
| |
| if (M->size == 0) { |
| /* |
| * trick: special value to indicate that tx_block |
| * should NOT be released in the SND_ACK |
| */ |
| M->tx_block.pool_id = (uint32_t)(-1); |
| } |
| |
| M->tx_task = _k_current_task->id; |
| M->tx_data = NULL; |
| M->mailbox = mbox; |
| M->extra.sema = sema; |
| |
| #ifdef CONFIG_SYS_CLOCK_EXISTS |
| A.Time.timer = NULL; |
| #endif |
| A.priority = prio; |
| A.Comm = _K_SVC_MBOX_SEND_REQUEST; |
| 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.task = _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 = _K_SVC_MBOX_RECEIVE_ACK; |
| CopyStarter->Time.rcode = RC_OK; |
| |
| MoveD->args.moved_req.extra.setup.continuation_receive = CopyStarter; |
| CopyStarter->next = NULL; |
| MoveD->args.moved_req.destination = CopyStarter->args.m1.mess.rx_data; |
| |
| MoveD->args.moved_req.total_size = CopyStarter->args.m1.mess.size; |
| |
| Writer = MoveD->args.moved_req.extra.setup.continuation_send; |
| 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 */ |
| } |
| } |
| |
| |
| void _task_mbox_data_get(struct k_msg *M) |
| { |
| struct k_args A; |
| |
| /* sanity checks */ |
| if (unlikely(M->extra.transfer == NULL)) { |
| /* |
| * 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 = _K_SVC_MBOX_RECEIVE_DATA; |
| |
| KERNEL_ENTRY(&A); |
| } |
| |
| |
| int task_mbox_data_block_get(struct k_msg *M, struct k_block *block, |
| kmemory_pool_t pool_id, int32_t timeout) |
| { |
| int retval; |
| struct k_args *MoveD; |
| |
| /* sanity checks: */ |
| if (M->extra.transfer == NULL) { |
| /* |
| * 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(M)) { |
| /* First transfer block */ |
| __ASSERT_NO_MSG(M->tx_block.pool_id != -1); |
| *block = M->tx_block; |
| |
| /* This is the MOVED packet */ |
| MoveD = M->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 [pool_id] to -1. |
| */ |
| |
| Writer = MoveD->args.moved_req.extra.setup.continuation_send; |
| __ASSERT_NO_MSG(Writer != NULL); |
| __ASSERT_NO_MSG(Writer->next == NULL); |
| |
| Writer->args.m1.mess.tx_block.pool_id = (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.moved_req.extra.setup.continuation_receive; |
| __ASSERT_NO_MSG(dummy == NULL); |
| #endif |
| |
| FREEARGS(MoveD); /* Clean up MOVED */ |
| |
| return RC_OK; |
| } |
| |
| /* 'normal' flow of task_mbox_data_block_get(): */ |
| |
| if (M->size != 0) { |
| retval = task_mem_pool_alloc(block, pool_id, |
| M->size, timeout); |
| if (retval != RC_OK) { |
| return retval; |
| } |
| M->rx_data = block->pointer_to_data; |
| } else { |
| block->pool_id = (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 = *M; |
| A.Comm = _K_SVC_MBOX_RECEIVE_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.task = _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 = _K_SVC_MBOX_SEND_ACK; |
| |
| MoveD->args.moved_req.extra.setup.continuation_send = CopyStarter; |
| CopyStarter->next = NULL; |
| MoveD->args.moved_req.source = CopyStarter->args.m1.mess.rx_data; |
| |
| Reader = MoveD->args.moved_req.extra.setup.continuation_receive; |
| 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 */ |
| } |
| } |