aboutsummaryrefslogtreecommitdiffstats
path: root/os/kernel/src/chmsg.c
blob: 5002a892a2638554e33a60d9f4efa1f729d1bbf0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
    ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
                 2011 Giovanni Di Sirio.

    This file is part of ChibiOS/RT.

    ChibiOS/RT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    ChibiOS/RT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * @file    chmsg.c
 * @brief   Messages code.
 *
 * @addtogroup messages
 * @details Synchronous inter-thread messages APIs and services.
 *          <h2>Operation Mode</h2>
 *          Synchronous messages are an easy to use and fast IPC mechanism,
 *          threads can both act as message servers and/or message clients,
 *          the mechanism allows data to be carried in both directions. Note
 *          that messages are not copied between the client and server threads
 *          but just a pointer passed so the exchange is very time
 *          efficient.<br>
 *          Messages are scalar data types of type @p msg_t that are guaranteed
 *          to be size compatible with data pointers. Note that on some
 *          architectures function pointers can be larger that @p msg_t.<br>
 *          Messages are usually processed in FIFO order but it is possible to
 *          process them in priority order by enabling the
 *          @p CH_USE_MESSAGES_PRIORITY option in @p chconf.h.<br>
 * @pre     In order to use the message APIs the @p CH_USE_MESSAGES option
 *          must be enabled in @p chconf.h.
 * @post    Enabling messages requires 6-12 (depending on the architecture)
 *          extra bytes in the @p Thread structure.
 * @{
 */

#include "ch.h"

#if CH_USE_MESSAGES || defined(__DOXYGEN__)

#if CH_USE_MESSAGES_PRIORITY
#define msg_insert(tp, qp) prio_insert(tp, qp)
#else
#define msg_insert(tp, qp) queue_insert(tp, qp)
#endif

/**
 * @brief   Sends a message to the specified thread.
 * @details The sender is stopped until the receiver executes a
 *          @p chMsgRelease()after receiving the message.
 *
 * @param[in] tp        the pointer to the thread
 * @param[in] msg       the message
 * @return              The answer message from @p chMsgRelease().
 *
 * @api
 */
msg_t chMsgSend(Thread *tp, msg_t msg) {
  Thread *ctp = currp;

  chDbgCheck(tp != NULL, "chMsgSend");

  chSysLock();
  ctp->p_msg = msg;
  ctp->p_u.wtobjp = &tp->p_msgqueue;
  msg_insert(ctp, &tp->p_msgqueue);
  if (tp->p_state == THD_STATE_WTMSG)
    chSchReadyI(tp);
  chSchGoSleepS(THD_STATE_SNDMSGQ);
  msg = ctp->p_u.rdymsg;
  chSysUnlock();
  return msg;
}

/**
 * @brief   Suspends the thread and waits for an incoming message.
 * @post    After receiving a message the function @p chMsgGet() must be
 *          called in order to retrieve the message and then @p chMsgRelease()
 *          must be invoked in order to acknowledge the reception and send
 *          the answer.
 * @note    If the message is a pointer then you can assume that the data
 *          pointed by the message is stable until you invoke @p chMsgRelease()
 *          because the sending thread is suspended until then.
 *
 * @return              A reference to the thread carrying the message.
 *
 * @api
 */
Thread *chMsgWait(void) {
  Thread *tp;

  chSysLock();
  if (!chMsgIsPendingI(currp))
    chSchGoSleepS(THD_STATE_WTMSG);
  tp = fifo_remove(&currp->p_msgqueue);
  tp->p_state = THD_STATE_SNDMSG;
  chSysUnlock();
  return tp;
}

/**
 * @brief   Releases the thread waiting on top of the messages queue.
 * @pre     Invoke this function only after a message has been received
 *          using @p chMsgWait().
 *
 * @param[in] tp        pointer to the thread
 * @param[in] msg       message to be returned to the sender
 *
 * @api
 */
void chMsgRelease(Thread *tp, msg_t msg) {

  chSysLock();
  chDbgAssert(tp->p_state == THD_STATE_SNDMSG,
              "chMsgRelease(), #1", "invalid state");
  chMsgReleaseS(tp, msg);
  chSysUnlock();
}

#endif /* CH_USE_MESSAGES */

/** @} */
* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio

    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.
*/

/**
 * @file    uart.c
 * @brief   UART Driver code.
 *
 * @addtogroup UART
 * @{
 */

#include "hal.h"

#if (HAL_USE_UART == TRUE) || defined(__DOXYGEN__)

/*===========================================================================*/
/* Driver local definitions.                                                 */
/*===========================================================================*/

/*===========================================================================*/
/* Driver exported variables.                                                */
/*===========================================================================*/

/*===========================================================================*/
/* Driver local variables and types.                                         */
/*===========================================================================*/

/*===========================================================================*/
/* Driver local functions.                                                   */
/*===========================================================================*/

/*===========================================================================*/
/* Driver exported functions.                                                */
/*===========================================================================*/

/**
 * @brief   UART Driver initialization.
 * @note    This function is implicitly invoked by @p halInit(), there is
 *          no need to explicitly initialize the driver.
 *
 * @init
 */
void uartInit(void) {

  uart_lld_init();
}

/**
 * @brief   Initializes the standard part of a @p UARTDriver structure.
 *
 * @param[out] uartp    pointer to the @p UARTDriver object
 *
 * @init
 */
void uartObjectInit(UARTDriver *uartp) {

  uartp->state      = UART_STOP;
  uartp->txstate    = UART_TX_IDLE;
  uartp->rxstate    = UART_RX_IDLE;
  uartp->config     = NULL;
#if UART_USE_WAIT == TRUE
  uartp->early      = false;
  uartp->threadrx   = NULL;
  uartp->threadtx   = NULL;
#endif /* UART_USE_WAIT */
#if UART_USE_MUTUAL_EXCLUSION == TRUE
  osalMutexObjectInit(&uartp->mutex);
#endif /* UART_USE_MUTUAL_EXCLUSION */

  /* Optional, user-defined initializer.*/
#if defined(UART_DRIVER_EXT_INIT_HOOK)
  UART_DRIVER_EXT_INIT_HOOK(uartp);
#endif
}

/**
 * @brief   Configures and activates the UART peripheral.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in] config    pointer to the @p UARTConfig object
 *
 * @api
 */
void uartStart(UARTDriver *uartp, const UARTConfig *config) {

  osalDbgCheck((uartp != NULL) && (config != NULL));

  osalSysLock();
  osalDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
                "invalid state");

  uartp->config = config;
  uart_lld_start(uartp);
  uartp->state = UART_READY;
  osalSysUnlock();
}

/**
 * @brief   Deactivates the UART peripheral.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @api
 */
void uartStop(UARTDriver *uartp) {

  osalDbgCheck(uartp != NULL);

  osalSysLock();
  osalDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
                "invalid state");

  uart_lld_stop(uartp);
  uartp->state = UART_STOP;
  uartp->txstate = UART_TX_IDLE;
  uartp->rxstate = UART_RX_IDLE;
  osalSysUnlock();
}

/**
 * @brief   Starts a transmission on the UART peripheral.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in] n         number of data frames to send
 * @param[in] txbuf     the pointer to the transmit buffer
 *
 * @api
 */
void uartStartSend(UARTDriver *uartp, size_t n, const void *txbuf) {

  osalDbgCheck((uartp != NULL) && (n > 0U) && (txbuf != NULL));
             
  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active");

  uart_lld_start_send(uartp, n, txbuf);
  uartp->txstate = UART_TX_ACTIVE;
  osalSysUnlock();
}

/**
 * @brief   Starts a transmission on the UART peripheral.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 * @note    This function has to be invoked from a lock zone.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in] n         number of data frames to send
 * @param[in] txbuf     the pointer to the transmit buffer
 *
 * @iclass
 */
void uartStartSendI(UARTDriver *uartp, size_t n, const void *txbuf) {

  osalDbgCheckClassI();
  osalDbgCheck((uartp != NULL) && (n > 0U) && (txbuf != NULL));
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active");

  uart_lld_start_send(uartp, n, txbuf);
  uartp->txstate = UART_TX_ACTIVE;
}

/**
 * @brief   Stops any ongoing transmission.
 * @note    Stopping a transmission also suppresses the transmission callbacks.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @return              The number of data frames not transmitted by the
 *                      stopped transmit operation.
 * @retval 0            There was no transmit operation in progress.
 *
 * @api
 */
size_t uartStopSend(UARTDriver *uartp) {
  size_t n;

  osalDbgCheck(uartp != NULL);

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "not active");

  if (uartp->txstate == UART_TX_ACTIVE) {
    n = uart_lld_stop_send(uartp);
    uartp->txstate = UART_TX_IDLE;
  }
  else {
    n = 0;
  }
  osalSysUnlock();

  return n;
}

/**
 * @brief   Stops any ongoing transmission.
 * @note    Stopping a transmission also suppresses the transmission callbacks.
 * @note    This function has to be invoked from a lock zone.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @return              The number of data frames not transmitted by the
 *                      stopped transmit operation.
 * @retval 0            There was no transmit operation in progress.
 *
 * @iclass
 */
size_t uartStopSendI(UARTDriver *uartp) {

  osalDbgCheckClassI();
  osalDbgCheck(uartp != NULL);
  osalDbgAssert(uartp->state == UART_READY, "not active");

  if (uartp->txstate == UART_TX_ACTIVE) {
    size_t n = uart_lld_stop_send(uartp);
    uartp->txstate = UART_TX_IDLE;
    return n;
  }
  return 0;
}

/**
 * @brief   Starts a receive operation on the UART peripheral.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in] n         number of data frames to receive
 * @param[in] rxbuf     the pointer to the receive buffer
 *
 * @api
 */
void uartStartReceive(UARTDriver *uartp, size_t n, void *rxbuf) {

  osalDbgCheck((uartp != NULL) && (n > 0U) && (rxbuf != NULL));

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->rxstate != UART_RX_ACTIVE, "rx active");

  uart_lld_start_receive(uartp, n, rxbuf);
  uartp->rxstate = UART_RX_ACTIVE;
  osalSysUnlock();
}

/**
 * @brief   Starts a receive operation on the UART peripheral.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 * @note    This function has to be invoked from a lock zone.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in] n         number of data frames to receive
 * @param[out] rxbuf    the pointer to the receive buffer
 *
 * @iclass
 */
void uartStartReceiveI(UARTDriver *uartp, size_t n, void *rxbuf) {

  osalDbgCheckClassI();
  osalDbgCheck((uartp != NULL) && (n > 0U) && (rxbuf != NULL));
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->rxstate != UART_RX_ACTIVE, "rx active");

  uart_lld_start_receive(uartp, n, rxbuf);
  uartp->rxstate = UART_RX_ACTIVE;
}

/**
 * @brief   Stops any ongoing receive operation.
 * @note    Stopping a receive operation also suppresses the receive callbacks.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @return              The number of data frames not received by the
 *                      stopped receive operation.
 * @retval 0            There was no receive operation in progress.
 *
 * @api
 */
size_t uartStopReceive(UARTDriver *uartp) {
  size_t n;

  osalDbgCheck(uartp != NULL);

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "not active");

  if (uartp->rxstate == UART_RX_ACTIVE) {
    n = uart_lld_stop_receive(uartp);
    uartp->rxstate = UART_RX_IDLE;
  }
  else {
    n = 0;
  }
  osalSysUnlock();

  return n;
}

/**
 * @brief   Stops any ongoing receive operation.
 * @note    Stopping a receive operation also suppresses the receive callbacks.
 * @note    This function has to be invoked from a lock zone.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @return              The number of data frames not received by the
 *                      stopped receive operation.
 * @retval 0            There was no receive operation in progress.
 *
 * @iclass
 */
size_t uartStopReceiveI(UARTDriver *uartp) {

  osalDbgCheckClassI();
  osalDbgCheck(uartp != NULL);
  osalDbgAssert(uartp->state == UART_READY, "not active");

  if (uartp->rxstate == UART_RX_ACTIVE) {
    size_t n = uart_lld_stop_receive(uartp);
    uartp->rxstate = UART_RX_IDLE;
    return n;
  }
  return 0;
}

#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__)
/**
 * @brief   Performs a transmission on the UART peripheral.
 * @note    The function returns when the specified number of frames have been
 *          sent to the UART or on timeout.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in,out] np    number of data frames to transmit, on exit the number
 *                      of frames actually transmitted
 * @param[in] txbuf     the pointer to the transmit buffer
 * @param[in] timeout   operation timeout
 * @return              The operation status.
 * @retval MSG_OK       if the operation completed successfully.
 * @retval MSG_TIMEOUT  if the operation timed out.
 *
 * @api
 */
msg_t uartSendTimeout(UARTDriver *uartp, size_t *np,
                      const void *txbuf, systime_t timeout) {
  msg_t msg;

  osalDbgCheck((uartp != NULL) && (*np > 0U) && (txbuf != NULL));

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active");

  /* Transmission start.*/
  uartp->early = true;
  uart_lld_start_send(uartp, *np, txbuf);
  uartp->txstate = UART_TX_ACTIVE;

  /* Waiting for result.*/
  msg = osalThreadSuspendTimeoutS(&uartp->threadtx, timeout);
  if (msg != MSG_OK) {
    *np = uartStopSendI(uartp);
  }
  osalSysUnlock();

  return msg;
}

/**
 * @brief   Performs a transmission on the UART peripheral.
 * @note    The function returns when the specified number of frames have been
 *          physically transmitted or on timeout.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in,out] np    number of data frames to transmit, on exit the number
 *                      of frames actually transmitted
 * @param[in] txbuf     the pointer to the transmit buffer
 * @param[in] timeout   operation timeout
 * @return              The operation status.
 * @retval MSG_OK       if the operation completed successfully.
 * @retval MSG_TIMEOUT  if the operation timed out.
 *
 * @api
 */
msg_t uartSendFullTimeout(UARTDriver *uartp, size_t *np,
                          const void *txbuf, systime_t timeout) {
  msg_t msg;

  osalDbgCheck((uartp != NULL) && (*np > 0U) && (txbuf != NULL));

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->txstate != UART_TX_ACTIVE, "tx active");

  /* Transmission start.*/
  uartp->early = false;
  uart_lld_start_send(uartp, *np, txbuf);
  uartp->txstate = UART_TX_ACTIVE;

  /* Waiting for result.*/
  msg = osalThreadSuspendTimeoutS(&uartp->threadtx, timeout);
  if (msg != MSG_OK) {
    *np = uartStopSendI(uartp);
  }
  osalSysUnlock();

  return msg;
}

/**
 * @brief   Performs a receive operation on the UART peripheral.
 * @note    The function returns when the specified number of frames have been
 *          received or on error/timeout.
 * @note    The buffers are organized as uint8_t arrays for data sizes below
 *          or equal to 8 bits else it is organized as uint16_t arrays.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 * @param[in,out] np    number of data frames to receive, on exit the number
 *                      of frames actually received
 * @param[in] rxbuf     the pointer to the receive buffer
 * @param[in] timeout   operation timeout
 *
 * @return              The operation status.
 * @retval MSG_OK       if the operation completed successfully.
 * @retval MSG_TIMEOUT  if the operation timed out.
 * @retval MSG_RESET    in case of a receive error.
 *
 * @api
 */
msg_t uartReceiveTimeout(UARTDriver *uartp, size_t *np,
                         void *rxbuf, systime_t timeout) {
  msg_t msg;

  osalDbgCheck((uartp != NULL) && (*np > 0U) && (rxbuf != NULL));

  osalSysLock();
  osalDbgAssert(uartp->state == UART_READY, "is active");
  osalDbgAssert(uartp->rxstate != UART_RX_ACTIVE, "rx active");

  /* Receive start.*/
  uart_lld_start_receive(uartp, *np, rxbuf);
  uartp->rxstate = UART_RX_ACTIVE;

  /* Waiting for result.*/
  msg = osalThreadSuspendTimeoutS(&uartp->threadrx, timeout);
  if (msg != MSG_OK) {
    *np = uartStopReceiveI(uartp);
  }
  osalSysUnlock();

  return msg;
}
#endif

#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
/**
 * @brief   Gains exclusive access to the UART bus.
 * @details This function tries to gain ownership to the UART bus, if the bus
 *          is already being used then the invoking thread is queued.
 * @pre     In order to use this function the option @p UART_USE_MUTUAL_EXCLUSION
 *          must be enabled.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @api
 */
void uartAcquireBus(UARTDriver *uartp) {

  osalDbgCheck(uartp != NULL);

  osalMutexLock(&uartp->mutex);
}

/**
 * @brief   Releases exclusive access to the UART bus.
 * @pre     In order to use this function the option @p UART_USE_MUTUAL_EXCLUSION
 *          must be enabled.
 *
 * @param[in] uartp     pointer to the @p UARTDriver object
 *
 * @api
 */
void uartReleaseBus(UARTDriver *uartp) {

  osalDbgCheck(uartp != NULL);

  osalMutexUnlock(&uartp->mutex);
}
#endif

#endif /* HAL_USE_UART == TRUE */

/** @} */