Blame view

RIOT/core/include/msg.h 11.7 KB
a752c7ab   elopes   add first test an...
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  /*
   * Copyright (C) 2014 Freie Universität Berlin
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @defgroup    core_msg  Messaging / IPC
   * @ingroup     core
   * @brief       Messaging API for inter process communication
   *
   * Messages
   * ========
   * IPC messages consist of a sender PID, a type, and some content. The sender
   * PID will be set by the IPC internally and is not required to be set by the
   * user. The type helps the receiver to multiplex different message types and
   * should be set to a system-wide unique value. The content can either be
   * provided as a 32-bit integer or a pointer.
   *
   * Blocking vs non-blocking
   * ========================
   * Messages can be sent and received blocking and non-blocking. Both can be
   * used combined: A message send while blocking the sender thread can be
   * received with the non-blocking variant and vice-versa.
   *
   * Blocking IPC
   * ------------
   * For the blocking variant use @ref msg_send() or @ref msg_receive()
   * respectively.
   *
   * Additionally, one can use @ref msg_send_receive() to simultaneously block
   * the sending thread and expect a response from the receiving thread. In this
   * case, the receiving thread must use @ref msg_reply() to reply to the message
   * of the sender thread.
   *
   * ~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
   * #include <inttypes.h>
   * #include <stdio.h>
   *
   * #include "msg.h"
   * #include "thread.h"
   *
   * static kernel_pid_t rcv_pid;
   * static char rcv_stack[THREAD_STACKSIZE_DEFAULT];
   *
   * static void *rcv(void *arg)
   * {
   *     msg_t msg_req, msg_resp;
   *
   *     (void)arg;
   *     while (1) {
   *         msg_receive(&msg_req);
   *         msg_resp.content.value = msg_req.content.value + 1;
   *         msg_reply(&msg_req, &msg_resp);
   *     }
   *     return NULL;
   * }
   *
   * int main(void)
   * {
   *     msg_t msg_req, msg_resp;
   *
   *     msg_resp.content.value = 0;
   *     rcv_pid = thread_create(rcv_stack, sizeof(rcv_stack),
   *                             THREAD_PRIORITY_MAIN - 1, 0, rcv, NULL, "rcv");
   *     while (1) {
   *         msg_req.content.value = msg_resp.content.value;
   *         msg_send_receive(&msg_req, &msg_resp, rcv_pid);
   *         printf("Result: %" PRIu32 "\n", msg_resp.content.value);
   *     }
   *     return 0;
   * }
   * ~~~~~~~~~~~~~~~~~~~~~~~~
   *
   * Non-blocking IPC
   * ----------------
   * For the non-blocking variant use @ref msg_try_send() or
   * @ref msg_try_receive() respectively. If a message is sent in synchronous
   * mode or the message queue (see below) of the receiving thread is full
   * messages sent this way will be dropped.
   *
   * You can use the example on asynchronous IPC below - but without the queue -
   * to get an impression of how to use non-blocking IPC.
   *
   * Synchronous vs Asynchronous
   * ===========================
   * RIOT's IPC supports both synchronous and asynchronous IPC.
   *
   * Synchronous IPC
   * ---------------
   * Synchronous IPC is the default mode i.e. is active when the receiving thread
   * has no message queue initialized. Messages that can't be delivered when
   * sending non-blocking (because the receiver already received a message) or
   * which are sent when the receiver is not receive-blocked will be dropped.
   *
   * Asynchronous IPC
   * ----------------
   * To use asynchronous IPC one needs to initialize a message queue using
   * @ref msg_init_queue() (note that it **must** be of a size equal to a power of
   * two). Messages sent to a thread with a message queue that isn't full are
   * never dropped and the sending never blocks, even when using @ref msg_send().
   * If the queue is full and the sending thread has a higher priority than the
   * receiving thread the send-behavior is equivalent to synchronous mode.
   *
   * ~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
   * #include <inttypes.h>
   * #include <stdio.h>
   *
   * #include "msg.h"
   * #include "thread.h"
   *
   * #define RCV_QUEUE_SIZE  (8)
   *
   * static kernel_pid_t rcv_pid;
   * static char rcv_stack[THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF];
   * static msg_t rcv_queue[RCV_QUEUE_SIZE];
   *
   * static void *rcv(void *arg)
   * {
   *     msg_t msg;
   *
   *     (void)arg;
   *     msg_init_queue(rcv_queue, RCV_QUEUE_SIZE);
   *     while (1) {
   *         msg_receive(&msg);
   *         printf("Received %" PRIu32 "\n", msg.content.value);
   *     }
   *     return NULL;
   * }
   *
   * int main(void)
   * {
   *     msg_t msg;
   *
   *     msg.content.value = 0;
   *     rcv_pid = thread_create(rcv_stack, sizeof(rcv_stack),
   *                             THREAD_PRIORITY_MAIN - 1, 0, rcv, NULL, "rcv");
   *     while (1) {
   *         if (msg_try_send(&msg, rcv_pid) == 0) {
   *             printf("Receiver queue full.\n");
   *         }
   *         msg.content.value++;
   *     }
   *     return 0;
   * }
   * ~~~~~~~~~~~~~~~~~~~~~~~~
   *
   * Timing & messages
   * =================
   * Timing out the reception of a message or sending messages at a certain time
   * is out of scope for the basic IPC provided by the kernel. See the
   * @ref sys_xtimer "xtimer" module on information for these functionalities.
   *
   * @{
   *
   * @file
   * @brief       Messaging API for inter process communication
   *
   * @author      Kaspar Schleiser <kaspar@schleiser.de>
   * @author      Kévin Roussel <Kevin.Roussel@inria.fr>
   */
  
  #ifndef MSG_H
  #define MSG_H
  
  #include <stdint.h>
  #include <stdbool.h>
  #include "kernel_types.h"
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**
   * @brief Describes a message object which can be sent between threads.
   *
   * User can set type and one of content.ptr and content.value. (content is a union)
   * The meaning of type and the content fields is totally up to the user,
   * the corresponding fields are never read by the kernel.
   *
   */
  typedef struct {
      kernel_pid_t sender_pid;    /**< PID of sending thread. Will be filled in
                                       by msg_send. */
      uint16_t type;              /**< Type field. */
      union {
          void *ptr;              /**< Pointer content field. */
          uint32_t value;         /**< Value content field. */
      } content;                  /**< Content of the message. */
  } msg_t;
  
  
  /**
   * @brief Send a message (blocking).
   *
   * This function sends a message to another thread. The ``msg_t`` structure has
   * to be allocated (e.g. on the stack) before calling the function and can be
   * freed afterwards. If called from an interrupt, this function will never
   * block.
   *
   * @param[in] m             Pointer to preallocated ``msg_t`` structure, must
   *                          not be NULL.
   * @param[in] target_pid    PID of target thread
   *
   * @return 1, if sending was successful (message delivered directly or to a
   *            queue)
   * @return 0, if called from ISR and receiver cannot receive the message now
   *            (it is not waiting or it's message queue is full)
   * @return -1, on error (invalid PID)
   */
  int msg_send(msg_t *m, kernel_pid_t target_pid);
  
  
  /**
   * @brief Send a message (non-blocking).
   *
   * This function sends a message to another thread. The ``msg_t`` structure has
   * to be allocated (e.g. on the stack) before calling the function and can be
   * freed afterwards. This function will never block.
   *
   * @param[in] m             Pointer to preallocated ``msg_t`` structure, must
   *                          not be NULL.
   * @param[in] target_pid    PID of target thread
   *
   * @return 1, if sending was successful (message delivered directly or to a
   *         queue)
   * @return 0, if receiver is not waiting or has a full message queue
   * @return -1, on error (invalid PID)
   */
  int msg_try_send(msg_t *m, kernel_pid_t target_pid);
  
  
  /**
   * @brief Send a message to the current thread.
   * @details Will work only if the thread has a message queue.
   *
   * Will be automatically chosen instead of @c msg_send
   * if @c target_pid == @c thread_pid.
   * This function never blocks.
   *
   * @param  m pointer to message structure
   *
   * @return 1 if sending was successful
   * @return 0 if the thread's message queue is full (or inexistent)
   */
  int msg_send_to_self(msg_t *m);
  
  /**
   * Value of msg_t::sender_pid if the sender was an interrupt service routine.
   */
  #define KERNEL_PID_ISR (KERNEL_PID_LAST + 1)
  
  /**
   * @brief Send message from interrupt.
   *
   * Will be automatically chosen instead of msg_send() if called from an
   * interrupt/ISR.
   *
   * The value of ``m->sender_pid`` is set to @ref KERNEL_PID_ISR.
   *
   * @see msg_sent_by_int()
   *
   * @param[in] m             Pointer to preallocated @ref msg_t structure, must
   *                          not be NULL.
   * @param[in] target_pid    PID of target thread.
   *
   * @return 1, if sending was successful
   * @return 0, if receiver is not waiting and ``block == 0``
   * @return -1, on error (invalid PID)
   */
  int msg_send_int(msg_t *m, kernel_pid_t target_pid);
  
  /**
   * @brief Test if the message was sent inside an ISR.
   * @see msg_send_int()
   * @param[in] m The message in question.
   * @returns `== 0` if *not* sent by an ISR
   * @returns `!= 0` if sent by an ISR
   */
  static inline int msg_sent_by_int(const msg_t *m)
  {
      return (m->sender_pid == KERNEL_PID_ISR);
  }
  
  /**
   * @brief Receive a message.
   *
   * This function blocks until a message was received.
   *
   * @param[out] m    Pointer to preallocated ``msg_t`` structure, must not be
   *                  NULL.
   *
   * @return  1, Function always succeeds or blocks forever.
   */
  int msg_receive(msg_t *m);
  
  /**
   * @brief Try to receive a message.
   *
   * This function does not block if no message can be received.
   *
   * @param[out] m    Pointer to preallocated ``msg_t`` structure, must not be
   *                  NULL.
   *
   * @return  1, if a message was received
   * @return  -1, otherwise.
   */
  int msg_try_receive(msg_t *m);
  
  /**
   * @brief Send a message, block until reply received.
   *
   * This function sends a message to *target_pid* and then blocks until target
   * has sent a reply which is then stored in *reply*.
   *
   * @pre     @p target_pid is not the PID of the current thread.
   *
   * @param[in] m             Pointer to preallocated ``msg_t`` structure with
   *                          the message to send, must not be NULL.
   * @param[out] reply        Pointer to preallocated msg. Reply will be written
   *                          here, must not be NULL. Can be identical to @p m.
   * @param[in] target_pid    The PID of the target process
   *
   * @return  1, if successful.
   */
  int msg_send_receive(msg_t *m, msg_t *reply, kernel_pid_t target_pid);
  
  /**
   * @brief Replies to a message.
   *
   * Sender must have sent the message with msg_send_receive().
   *
   * @param[in] m         message to reply to, must not be NULL.
   * @param[out] reply    message that target will get as reply, must not be NULL.
   *
   * @return 1, if successful
   * @return -1, on error
   */
  int msg_reply(msg_t *m, msg_t *reply);
  
  /**
   * @brief Replies to a message from interrupt.
   *
   * An ISR can obviously not receive messages, however a thread might delegate
   * replying to a message to an ISR.
   *
   * @param[in] m         message to reply to, must not be NULL.
   * @param[out] reply    message that target will get as reply, must not be NULL.
   *
   * @return 1, if successful
   * @return -1, on error
   */
  int msg_reply_int(msg_t *m, msg_t *reply);
  
  /**
   * @brief Check how many messages are available in the message queue
   *
   * @return Number of messages available in our queue on success
   * @return -1, if no caller's message queue is initialized
   */
  int msg_avail(void);
  
  /**
   * @brief Initialize the current thread's message queue.
   *
   * @pre @p num **MUST BE A POWER OF TWO!**
   *
   * @param[in] array Pointer to preallocated array of ``msg_t`` structures, must
   *                  not be NULL.
   * @param[in] num   Number of ``msg_t`` structures in array.
   *                  **MUST BE POWER OF TWO!**
   */
  void msg_init_queue(msg_t *array, int num);
  
  /**
   * @brief   Prints the message queue of the current thread.
   */
  void msg_queue_print(void);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* MSG_H */
  /** @} */