1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @brief Constants and Structures Needed to Declare a Thread Queue |
---|
5 | * |
---|
6 | * This include file contains all the constants and structures |
---|
7 | * needed to declare a thread queue. |
---|
8 | */ |
---|
9 | |
---|
10 | /* |
---|
11 | * COPYRIGHT (c) 1989-2014. |
---|
12 | * On-Line Applications Research Corporation (OAR). |
---|
13 | * |
---|
14 | * The license and distribution terms for this file may be |
---|
15 | * found in the file LICENSE in this distribution or at |
---|
16 | * http://www.rtems.org/license/LICENSE. |
---|
17 | */ |
---|
18 | |
---|
19 | #ifndef _RTEMS_SCORE_THREADQ_H |
---|
20 | #define _RTEMS_SCORE_THREADQ_H |
---|
21 | |
---|
22 | #include <rtems/score/chain.h> |
---|
23 | #include <rtems/score/isrlock.h> |
---|
24 | #include <rtems/score/object.h> |
---|
25 | #include <rtems/score/priority.h> |
---|
26 | #include <rtems/score/rbtree.h> |
---|
27 | #include <rtems/score/states.h> |
---|
28 | #include <rtems/score/watchdogticks.h> |
---|
29 | |
---|
30 | #ifdef __cplusplus |
---|
31 | extern "C" { |
---|
32 | #endif |
---|
33 | |
---|
34 | struct Per_CPU_Control; |
---|
35 | |
---|
36 | struct Scheduler_Node; |
---|
37 | |
---|
38 | /** |
---|
39 | * @defgroup ScoreThreadQueue Thread Queue Handler |
---|
40 | * |
---|
41 | * @ingroup Score |
---|
42 | * |
---|
43 | * This handler provides the capability to have threads block in |
---|
44 | * ordered sets. The sets may be ordered using the FIFO or priority |
---|
45 | * discipline. |
---|
46 | */ |
---|
47 | /**@{*/ |
---|
48 | |
---|
49 | typedef struct _Thread_Control Thread_Control; |
---|
50 | |
---|
51 | typedef struct Thread_queue_Context Thread_queue_Context; |
---|
52 | |
---|
53 | typedef struct Thread_queue_Queue Thread_queue_Queue; |
---|
54 | |
---|
55 | typedef struct Thread_queue_Operations Thread_queue_Operations; |
---|
56 | |
---|
57 | /** |
---|
58 | * @brief Thread queue enqueue callout. |
---|
59 | * |
---|
60 | * @param[in] queue The actual thread queue. |
---|
61 | * @param[in] the_thread The thread to enqueue. |
---|
62 | * @param[in] cpu_self The current processor. |
---|
63 | * @param[in] queue_context The thread queue context of the lock acquire. |
---|
64 | * |
---|
65 | * @see _Thread_queue_Context_set_enqueue_callout(). |
---|
66 | */ |
---|
67 | typedef void ( *Thread_queue_Enqueue_callout )( |
---|
68 | Thread_queue_Queue *queue, |
---|
69 | Thread_Control *the_thread, |
---|
70 | struct Per_CPU_Control *cpu_self, |
---|
71 | Thread_queue_Context *queue_context |
---|
72 | ); |
---|
73 | |
---|
74 | /** |
---|
75 | * @brief Thread queue deadlock callout. |
---|
76 | * |
---|
77 | * @param the_thread The thread that detected the deadlock. |
---|
78 | * |
---|
79 | * @see _Thread_queue_Context_set_deadlock_callout(). |
---|
80 | */ |
---|
81 | typedef void ( *Thread_queue_Deadlock_callout )( |
---|
82 | Thread_Control *the_thread |
---|
83 | ); |
---|
84 | |
---|
85 | #if defined(RTEMS_MULTIPROCESSING) |
---|
86 | /** |
---|
87 | * @brief Multiprocessing (MP) support callout for thread queue operations. |
---|
88 | * |
---|
89 | * @param the_proxy The thread proxy of the thread queue operation. A thread |
---|
90 | * control is actually a thread proxy if and only if |
---|
91 | * _Objects_Is_local_id( the_proxy->Object.id ) is false. |
---|
92 | * @param mp_id Object identifier of the object containing the thread queue. |
---|
93 | * |
---|
94 | * @see _Thread_queue_Context_set_MP_callout(). |
---|
95 | */ |
---|
96 | typedef void ( *Thread_queue_MP_callout )( |
---|
97 | Thread_Control *the_proxy, |
---|
98 | Objects_Id mp_id |
---|
99 | ); |
---|
100 | #endif |
---|
101 | |
---|
102 | #if defined(RTEMS_SMP) |
---|
103 | /** |
---|
104 | * @brief The thread queue gate is an SMP synchronization means. |
---|
105 | * |
---|
106 | * The gates are added to a list of requests. A busy wait is performed to make |
---|
107 | * sure that preceding requests are carried out. Each predecessor notifies its |
---|
108 | * successor about on request completion. |
---|
109 | * |
---|
110 | * @see _Thread_queue_Gate_add(), _Thread_queue_Gate_wait(), and |
---|
111 | * _Thread_queue_Gate_open(). |
---|
112 | */ |
---|
113 | typedef struct { |
---|
114 | Chain_Node Node; |
---|
115 | |
---|
116 | Atomic_Uint go_ahead; |
---|
117 | } Thread_queue_Gate; |
---|
118 | #endif |
---|
119 | |
---|
120 | typedef struct { |
---|
121 | /** |
---|
122 | * @brief The lock context for the thread queue acquire and release |
---|
123 | * operations. |
---|
124 | */ |
---|
125 | ISR_lock_Context Lock_context; |
---|
126 | |
---|
127 | #if defined(RTEMS_SMP) |
---|
128 | /** |
---|
129 | * @brief Data to support thread queue enqueue operations. |
---|
130 | */ |
---|
131 | struct { |
---|
132 | /** |
---|
133 | * @brief Gate to synchronize thread wait lock requests. |
---|
134 | * |
---|
135 | * @see _Thread_Wait_acquire_critical() and _Thread_Wait_tranquilize(). |
---|
136 | */ |
---|
137 | Thread_queue_Gate Gate; |
---|
138 | |
---|
139 | /** |
---|
140 | * @brief The thread queue in case the thread is blocked on a thread queue. |
---|
141 | */ |
---|
142 | Thread_queue_Queue *queue; |
---|
143 | } Wait; |
---|
144 | #endif |
---|
145 | } Thread_queue_Lock_context; |
---|
146 | |
---|
147 | #if defined(RTEMS_SMP) |
---|
148 | /** |
---|
149 | * @brief A thread queue link from one thread to another specified by the |
---|
150 | * thread queue owner and thread wait queue relationships. |
---|
151 | */ |
---|
152 | typedef struct { |
---|
153 | /** |
---|
154 | * @brief Node to register this link in the global thread queue links lookup |
---|
155 | * tree. |
---|
156 | */ |
---|
157 | RBTree_Node Registry_node; |
---|
158 | |
---|
159 | /** |
---|
160 | * @brief The source thread queue determined by the thread queue owner. |
---|
161 | */ |
---|
162 | Thread_queue_Queue *source; |
---|
163 | |
---|
164 | /** |
---|
165 | * @brief The target thread queue determined by the thread wait queue of the |
---|
166 | * source owner. |
---|
167 | */ |
---|
168 | Thread_queue_Queue *target; |
---|
169 | |
---|
170 | /** |
---|
171 | * @brief Node to add this link to a thread queue path. |
---|
172 | */ |
---|
173 | Chain_Node Path_node; |
---|
174 | |
---|
175 | /** |
---|
176 | * @brief The owner of this thread queue link. |
---|
177 | */ |
---|
178 | Thread_Control *owner; |
---|
179 | |
---|
180 | /** |
---|
181 | * @brief The queue lock context used to acquire the thread wait lock of the |
---|
182 | * owner. |
---|
183 | */ |
---|
184 | Thread_queue_Lock_context Lock_context; |
---|
185 | } Thread_queue_Link; |
---|
186 | #endif |
---|
187 | |
---|
188 | /** |
---|
189 | * @brief Thread queue context for the thread queue methods. |
---|
190 | * |
---|
191 | * @see _Thread_queue_Context_initialize(). |
---|
192 | */ |
---|
193 | struct Thread_queue_Context { |
---|
194 | /** |
---|
195 | * @brief The lock context for the thread queue acquire and release |
---|
196 | * operations. |
---|
197 | */ |
---|
198 | Thread_queue_Lock_context Lock_context; |
---|
199 | |
---|
200 | /** |
---|
201 | * @brief The thread state for _Thread_queue_Enqueue(). |
---|
202 | */ |
---|
203 | States_Control thread_state; |
---|
204 | |
---|
205 | /** |
---|
206 | * @brief The enqueue callout for _Thread_queue_Enqueue(). |
---|
207 | * |
---|
208 | * The callout is invoked after the release of the thread queue lock with |
---|
209 | * thread dispatching disabled. Afterwards the thread is blocked. This |
---|
210 | * callout must be used to install the thread watchdog for timeout handling. |
---|
211 | * |
---|
212 | * @see _Thread_queue_Enqueue_do_nothing_extra(). |
---|
213 | * _Thread_queue_Add_timeout_ticks(), and |
---|
214 | * _Thread_queue_Add_timeout_realtime_timespec(). |
---|
215 | */ |
---|
216 | Thread_queue_Enqueue_callout enqueue_callout; |
---|
217 | |
---|
218 | /** |
---|
219 | * @brief Interval to wait. |
---|
220 | * |
---|
221 | * May be used by the enqueue callout to register a timeout handler. |
---|
222 | */ |
---|
223 | union { |
---|
224 | /** |
---|
225 | * @brief The timeout in ticks. |
---|
226 | */ |
---|
227 | Watchdog_Interval ticks; |
---|
228 | |
---|
229 | /** |
---|
230 | * @brief The timeout argument, e.g. pointer to struct timespec. |
---|
231 | */ |
---|
232 | const void *arg; |
---|
233 | } Timeout; |
---|
234 | |
---|
235 | #if defined(RTEMS_SMP) |
---|
236 | /** |
---|
237 | * @brief Representation of a thread queue path from a start thread queue to |
---|
238 | * the terminal thread queue. |
---|
239 | * |
---|
240 | * The start thread queue is determined by the object on which a thread intends |
---|
241 | * to block. The terminal thread queue is the thread queue reachable via |
---|
242 | * thread queue links whose owner is not blocked on a thread queue. The thread |
---|
243 | * queue links are determined by the thread queue owner and thread wait queue |
---|
244 | * relationships. |
---|
245 | */ |
---|
246 | struct { |
---|
247 | /** |
---|
248 | * @brief The chain of thread queue links defining the thread queue path. |
---|
249 | */ |
---|
250 | Chain_Control Links; |
---|
251 | |
---|
252 | /** |
---|
253 | * @brief The start of a thread queue path. |
---|
254 | */ |
---|
255 | Thread_queue_Link Start; |
---|
256 | |
---|
257 | /** |
---|
258 | * @brief In case of a deadlock, a link for the first thread on the path |
---|
259 | * that tries to enqueue on a thread queue. |
---|
260 | */ |
---|
261 | Thread_queue_Link Deadlock; |
---|
262 | } Path; |
---|
263 | #endif |
---|
264 | |
---|
265 | /** |
---|
266 | * @brief Block to manage thread priority changes due to a thread queue |
---|
267 | * operation. |
---|
268 | */ |
---|
269 | struct { |
---|
270 | /** |
---|
271 | * @brief A priority action list. |
---|
272 | */ |
---|
273 | Priority_Actions Actions; |
---|
274 | |
---|
275 | /** |
---|
276 | * @brief Count of threads to update the priority via |
---|
277 | * _Thread_Priority_update(). |
---|
278 | */ |
---|
279 | size_t update_count; |
---|
280 | |
---|
281 | /** |
---|
282 | * @brief Threads to update the priority via _Thread_Priority_update(). |
---|
283 | * |
---|
284 | * Currently, a maximum of two threads need an update in one rush, for |
---|
285 | * example the thread of the thread queue operation and the owner of the |
---|
286 | * thread queue. |
---|
287 | */ |
---|
288 | Thread_Control *update[ 2 ]; |
---|
289 | } Priority; |
---|
290 | |
---|
291 | /** |
---|
292 | * @brief Invoked in case of a detected deadlock. |
---|
293 | * |
---|
294 | * Must be initialized for _Thread_queue_Enqueue() in case the |
---|
295 | * thread queue may have an owner, e.g. for mutex objects. |
---|
296 | * |
---|
297 | * @see _Thread_queue_Context_set_deadlock_callout(). |
---|
298 | */ |
---|
299 | Thread_queue_Deadlock_callout deadlock_callout; |
---|
300 | |
---|
301 | #if defined(RTEMS_MULTIPROCESSING) |
---|
302 | /** |
---|
303 | * @brief Callout to unblock the thread in case it is actually a thread |
---|
304 | * proxy. |
---|
305 | * |
---|
306 | * This field is only used on multiprocessing configurations. Used by |
---|
307 | * thread queue extract and unblock methods for objects with multiprocessing |
---|
308 | * (MP) support. |
---|
309 | * |
---|
310 | * @see _Thread_queue_Context_set_MP_callout(). |
---|
311 | */ |
---|
312 | Thread_queue_MP_callout mp_callout; |
---|
313 | #endif |
---|
314 | }; |
---|
315 | |
---|
316 | /** |
---|
317 | * @brief Thread priority queue. |
---|
318 | */ |
---|
319 | typedef struct { |
---|
320 | #if defined(RTEMS_SMP) |
---|
321 | /** |
---|
322 | * @brief Node to enqueue this queue in the FIFO chain of the corresponding |
---|
323 | * heads structure. |
---|
324 | * |
---|
325 | * @see Thread_queue_Heads::Heads::Fifo. |
---|
326 | */ |
---|
327 | Chain_Node Node; |
---|
328 | #endif |
---|
329 | |
---|
330 | /** |
---|
331 | * @brief The actual thread priority queue. |
---|
332 | */ |
---|
333 | Priority_Aggregation Queue; |
---|
334 | |
---|
335 | /** |
---|
336 | * @brief This priority queue is added to a scheduler node of the owner in |
---|
337 | * case of priority inheritance. |
---|
338 | */ |
---|
339 | struct Scheduler_Node *scheduler_node; |
---|
340 | } Thread_queue_Priority_queue; |
---|
341 | |
---|
342 | /** |
---|
343 | * @brief Thread queue heads. |
---|
344 | * |
---|
345 | * Each thread is equipped with spare thread queue heads in case it is not |
---|
346 | * enqueued on a thread queue. The first thread enqueued on a thread queue |
---|
347 | * will give its spare thread queue heads to that thread queue. The threads |
---|
348 | * arriving at the queue will add their thread queue heads to the free chain of |
---|
349 | * the queue heads provided by the first thread enqueued. Once a thread is |
---|
350 | * dequeued it use the free chain to get new spare thread queue heads. |
---|
351 | * |
---|
352 | * Uses a leading underscore in the structure name to allow forward |
---|
353 | * declarations in standard header files provided by Newlib and GCC. |
---|
354 | */ |
---|
355 | typedef struct _Thread_queue_Heads { |
---|
356 | /** This union contains the data structures used to manage the blocked |
---|
357 | * set of tasks which varies based upon the discipline. |
---|
358 | */ |
---|
359 | union { |
---|
360 | /** |
---|
361 | * @brief This is the FIFO discipline list. |
---|
362 | * |
---|
363 | * On SMP configurations this FIFO is used to enqueue the per scheduler |
---|
364 | * instance priority queues of this structure. This ensures FIFO fairness |
---|
365 | * among the highest priority thread of each scheduler instance. |
---|
366 | */ |
---|
367 | Chain_Control Fifo; |
---|
368 | |
---|
369 | #if !defined(RTEMS_SMP) |
---|
370 | /** |
---|
371 | * @brief This is the set of threads for priority discipline waiting. |
---|
372 | */ |
---|
373 | Thread_queue_Priority_queue Priority; |
---|
374 | #endif |
---|
375 | } Heads; |
---|
376 | |
---|
377 | /** |
---|
378 | * @brief A chain with free thread queue heads providing the spare thread |
---|
379 | * queue heads for a thread once it is dequeued. |
---|
380 | */ |
---|
381 | Chain_Control Free_chain; |
---|
382 | |
---|
383 | /** |
---|
384 | * @brief A chain node to add these thread queue heads to the free chain of |
---|
385 | * the thread queue heads dedicated to the thread queue of an object. |
---|
386 | */ |
---|
387 | Chain_Node Free_node; |
---|
388 | |
---|
389 | #if defined(RTEMS_SMP) |
---|
390 | /** |
---|
391 | * @brief One priority queue per scheduler instance. |
---|
392 | */ |
---|
393 | Thread_queue_Priority_queue Priority[ RTEMS_ZERO_LENGTH_ARRAY ]; |
---|
394 | #endif |
---|
395 | } Thread_queue_Heads; |
---|
396 | |
---|
397 | struct Thread_queue_Queue { |
---|
398 | /** |
---|
399 | * @brief Lock to protect this thread queue. |
---|
400 | * |
---|
401 | * It may be used to protect additional state of the object embedding this |
---|
402 | * thread queue. |
---|
403 | * |
---|
404 | * Must be the first component of this structure to be able to re-use |
---|
405 | * implementation parts for structures defined by Newlib <sys/lock.h>. |
---|
406 | * |
---|
407 | * @see _Thread_queue_Acquire(), _Thread_queue_Acquire_critical() and |
---|
408 | * _Thread_queue_Release(). |
---|
409 | */ |
---|
410 | #if defined(RTEMS_SMP) |
---|
411 | SMP_ticket_lock_Control Lock; |
---|
412 | #endif |
---|
413 | |
---|
414 | /** |
---|
415 | * @brief The thread queue heads. |
---|
416 | * |
---|
417 | * This pointer is NULL, if and only if no threads are enqueued. The first |
---|
418 | * thread to enqueue will give its spare thread queue heads to this thread |
---|
419 | * queue. |
---|
420 | */ |
---|
421 | Thread_queue_Heads *heads; |
---|
422 | |
---|
423 | /** |
---|
424 | * @brief The thread queue owner. |
---|
425 | */ |
---|
426 | Thread_Control *owner; |
---|
427 | |
---|
428 | /** |
---|
429 | * @brief The thread queue name. |
---|
430 | */ |
---|
431 | const char *name; |
---|
432 | }; |
---|
433 | |
---|
434 | /** |
---|
435 | * @brief Thread queue action operation. |
---|
436 | * |
---|
437 | * @param[in] queue The actual thread queue. |
---|
438 | * @param[in] the_thread The thread. |
---|
439 | * @param[in] queue_context The thread queue context providing the thread queue |
---|
440 | * action set to perform. Returns the thread queue action set to perform on |
---|
441 | * the thread queue owner or the empty set in case there is nothing to do. |
---|
442 | */ |
---|
443 | typedef void ( *Thread_queue_Priority_actions_operation )( |
---|
444 | Thread_queue_Queue *queue, |
---|
445 | Priority_Actions *priority_actions |
---|
446 | ); |
---|
447 | |
---|
448 | /** |
---|
449 | * @brief Thread queue enqueue operation. |
---|
450 | * |
---|
451 | * A potential thread to update the priority due to priority inheritance is |
---|
452 | * returned via the thread queue context. This thread is handed over to |
---|
453 | * _Thread_Priority_update(). |
---|
454 | * |
---|
455 | * @param[in] queue The actual thread queue. |
---|
456 | * @param[in] the_thread The thread to enqueue on the queue. |
---|
457 | */ |
---|
458 | typedef void ( *Thread_queue_Enqueue_operation )( |
---|
459 | Thread_queue_Queue *queue, |
---|
460 | Thread_Control *the_thread, |
---|
461 | Thread_queue_Context *queue_context |
---|
462 | ); |
---|
463 | |
---|
464 | /** |
---|
465 | * @brief Thread queue extract operation. |
---|
466 | * |
---|
467 | * @param[in] queue The actual thread queue. |
---|
468 | * @param[in] the_thread The thread to extract from the thread queue. |
---|
469 | */ |
---|
470 | typedef void ( *Thread_queue_Extract_operation )( |
---|
471 | Thread_queue_Queue *queue, |
---|
472 | Thread_Control *the_thread, |
---|
473 | Thread_queue_Context *queue_context |
---|
474 | ); |
---|
475 | |
---|
476 | /** |
---|
477 | * @brief Thread queue surrender operation. |
---|
478 | * |
---|
479 | * This operation must dequeue and return the first thread on the queue. |
---|
480 | * |
---|
481 | * @param[in] queue The actual thread queue. |
---|
482 | * @param[in] heads The thread queue heads. It must not be NULL. |
---|
483 | * @param[in] previous_owner The previous owner of the thread queue. |
---|
484 | * |
---|
485 | * @return The previous first thread on the queue. |
---|
486 | */ |
---|
487 | typedef Thread_Control *( *Thread_queue_Surrender_operation )( |
---|
488 | Thread_queue_Queue *queue, |
---|
489 | Thread_queue_Heads *heads, |
---|
490 | Thread_Control *previous_owner, |
---|
491 | Thread_queue_Context *queue_context |
---|
492 | ); |
---|
493 | |
---|
494 | /** |
---|
495 | * @brief Thread queue first operation. |
---|
496 | * |
---|
497 | * @param[in] heads The thread queue heads. |
---|
498 | * |
---|
499 | * @retval NULL No thread is present on the thread queue. |
---|
500 | * @retval first The first thread of the thread queue according to the insert |
---|
501 | * order. This thread remains on the thread queue. |
---|
502 | */ |
---|
503 | typedef Thread_Control *( *Thread_queue_First_operation )( |
---|
504 | Thread_queue_Heads *heads |
---|
505 | ); |
---|
506 | |
---|
507 | /** |
---|
508 | * @brief Thread queue operations. |
---|
509 | * |
---|
510 | * @see _Thread_wait_Set_operations(). |
---|
511 | */ |
---|
512 | struct Thread_queue_Operations { |
---|
513 | /** |
---|
514 | * @brief Thread queue priority actions operation. |
---|
515 | */ |
---|
516 | Thread_queue_Priority_actions_operation priority_actions; |
---|
517 | |
---|
518 | /** |
---|
519 | * @brief Thread queue enqueue operation. |
---|
520 | * |
---|
521 | * Called by object routines to enqueue the thread. |
---|
522 | */ |
---|
523 | Thread_queue_Enqueue_operation enqueue; |
---|
524 | |
---|
525 | /** |
---|
526 | * @brief Thread queue extract operation. |
---|
527 | * |
---|
528 | * Called by object routines to extract a thread from a thread queue. |
---|
529 | */ |
---|
530 | Thread_queue_Extract_operation extract; |
---|
531 | |
---|
532 | /** |
---|
533 | * @brief Thread queue surrender operation. |
---|
534 | */ |
---|
535 | Thread_queue_Surrender_operation surrender; |
---|
536 | |
---|
537 | /** |
---|
538 | * @brief Thread queue first operation. |
---|
539 | */ |
---|
540 | Thread_queue_First_operation first; |
---|
541 | }; |
---|
542 | |
---|
543 | /** |
---|
544 | * This is the structure used to manage sets of tasks which are blocked |
---|
545 | * waiting to acquire a resource. |
---|
546 | */ |
---|
547 | typedef struct { |
---|
548 | #if defined(RTEMS_SMP) |
---|
549 | #if defined(RTEMS_DEBUG) |
---|
550 | /** |
---|
551 | * @brief The index of the owning processor of the thread queue lock. |
---|
552 | * |
---|
553 | * The thread queue lock may be acquired via the thread lock also. This path |
---|
554 | * is not covered by this field. In case the lock is not owned directly via |
---|
555 | * _Thread_queue_Acquire(), then the value of this field is |
---|
556 | * SMP_LOCK_NO_OWNER. |
---|
557 | * |
---|
558 | * Must be before the queue component of this structure to be able to re-use |
---|
559 | * implementation parts for structures defined by Newlib <sys/lock.h>. |
---|
560 | */ |
---|
561 | uint32_t owner; |
---|
562 | #endif |
---|
563 | |
---|
564 | #if defined(RTEMS_PROFILING) |
---|
565 | /** |
---|
566 | * @brief SMP lock statistics in case SMP and profiling are enabled. |
---|
567 | * |
---|
568 | * Must be before the queue component of this structure to be able to re-use |
---|
569 | * implementation parts for structures defined by Newlib <sys/lock.h>. |
---|
570 | */ |
---|
571 | SMP_lock_Stats Lock_stats; |
---|
572 | #endif |
---|
573 | #endif |
---|
574 | |
---|
575 | /** |
---|
576 | * @brief The actual thread queue. |
---|
577 | */ |
---|
578 | Thread_queue_Queue Queue; |
---|
579 | } Thread_queue_Control; |
---|
580 | |
---|
581 | /**@}*/ |
---|
582 | |
---|
583 | #ifdef __cplusplus |
---|
584 | } |
---|
585 | #endif |
---|
586 | |
---|
587 | #endif |
---|
588 | /* end of include file */ |
---|