xref: /nrf52832-nimble/rt-thread/src/ipc.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-03-14     Bernard      the first version
9  * 2006-04-25     Bernard      implement semaphore
10  * 2006-05-03     Bernard      add RT_IPC_DEBUG
11  *                             modify the type of IPC waiting time to rt_int32_t
12  * 2006-05-10     Bernard      fix the semaphore take bug and add IPC object
13  * 2006-05-12     Bernard      implement mailbox and message queue
14  * 2006-05-20     Bernard      implement mutex
15  * 2006-05-23     Bernard      implement fast event
16  * 2006-05-24     Bernard      implement event
17  * 2006-06-03     Bernard      fix the thread timer init bug
18  * 2006-06-05     Bernard      fix the mutex release bug
19  * 2006-06-07     Bernard      fix the message queue send bug
20  * 2006-08-04     Bernard      add hook support
21  * 2009-05-21     Yi.qiu       fix the sem release bug
22  * 2009-07-18     Bernard      fix the event clear bug
23  * 2009-09-09     Bernard      remove fast event and fix ipc release bug
24  * 2009-10-10     Bernard      change semaphore and mutex value to unsigned value
25  * 2009-10-25     Bernard      change the mb/mq receive timeout to 0 if the
26  *                             re-calculated delta tick is a negative number.
27  * 2009-12-16     Bernard      fix the rt_ipc_object_suspend issue when IPC flag
28  *                             is RT_IPC_FLAG_PRIO
29  * 2010-01-20     mbbill       remove rt_ipc_object_decrease function.
30  * 2010-04-20     Bernard      move memcpy outside interrupt disable in mq
31  * 2010-10-26     yi.qiu       add module support in rt_mp_delete and rt_mq_delete
32  * 2010-11-10     Bernard      add IPC reset command implementation.
33  * 2011-12-18     Bernard      add more parameter checking in message queue
34  * 2013-09-14     Grissiom     add an option check in rt_event_recv
35  * 2018-10-02     Bernard      add 64bit support for mailbox
36  */
37 
38 #include <rtthread.h>
39 #include <rthw.h>
40 
41 #ifdef RT_USING_HOOK
42 extern void (*rt_object_trytake_hook)(struct rt_object *object);
43 extern void (*rt_object_take_hook)(struct rt_object *object);
44 extern void (*rt_object_put_hook)(struct rt_object *object);
45 #endif
46 
47 /**
48  * @addtogroup IPC
49  */
50 
51 /**@{*/
52 
53 /**
54  * This function will initialize an IPC object
55  *
56  * @param ipc the IPC object
57  *
58  * @return the operation status, RT_EOK on successful
59  */
rt_ipc_object_init(struct rt_ipc_object * ipc)60 rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc)
61 {
62     /* init ipc object */
63     rt_list_init(&(ipc->suspend_thread));
64 
65     return RT_EOK;
66 }
67 
68 /**
69  * This function will suspend a thread to a specified list. IPC object or some
70  * double-queue object (mailbox etc.) contains this kind of list.
71  *
72  * @param list the IPC suspended thread list
73  * @param thread the thread object to be suspended
74  * @param flag the IPC object flag,
75  *        which shall be RT_IPC_FLAG_FIFO/RT_IPC_FLAG_PRIO.
76  *
77  * @return the operation status, RT_EOK on successful
78  */
rt_ipc_list_suspend(rt_list_t * list,struct rt_thread * thread,rt_uint8_t flag)79 rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t        *list,
80                                        struct rt_thread *thread,
81                                        rt_uint8_t        flag)
82 {
83     /* suspend thread */
84     rt_thread_suspend(thread);
85 
86     switch (flag)
87     {
88     case RT_IPC_FLAG_FIFO:
89         rt_list_insert_before(list, &(thread->tlist));
90         break;
91 
92     case RT_IPC_FLAG_PRIO:
93         {
94             struct rt_list_node *n;
95             struct rt_thread *sthread;
96 
97             /* find a suitable position */
98             for (n = list->next; n != list; n = n->next)
99             {
100                 sthread = rt_list_entry(n, struct rt_thread, tlist);
101 
102                 /* find out */
103                 if (thread->current_priority < sthread->current_priority)
104                 {
105                     /* insert this thread before the sthread */
106                     rt_list_insert_before(&(sthread->tlist), &(thread->tlist));
107                     break;
108                 }
109             }
110 
111             /*
112              * not found a suitable position,
113              * append to the end of suspend_thread list
114              */
115             if (n == list)
116                 rt_list_insert_before(list, &(thread->tlist));
117         }
118         break;
119     }
120 
121     return RT_EOK;
122 }
123 
124 /**
125  * This function will resume the first thread in the list of a IPC object:
126  * - remove the thread from suspend queue of IPC object
127  * - put the thread into system ready queue
128  *
129  * @param list the thread list
130  *
131  * @return the operation status, RT_EOK on successful
132  */
rt_ipc_list_resume(rt_list_t * list)133 rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list)
134 {
135     struct rt_thread *thread;
136 
137     /* get thread entry */
138     thread = rt_list_entry(list->next, struct rt_thread, tlist);
139 
140     RT_DEBUG_LOG(RT_DEBUG_IPC, ("resume thread:%s\n", thread->name));
141 
142     /* resume it */
143     rt_thread_resume(thread);
144 
145     return RT_EOK;
146 }
147 
148 /**
149  * This function will resume all suspended threads in a list, including
150  * suspend list of IPC object and private list of mailbox etc.
151  *
152  * @param list of the threads to resume
153  *
154  * @return the operation status, RT_EOK on successful
155  */
rt_ipc_list_resume_all(rt_list_t * list)156 rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list)
157 {
158     struct rt_thread *thread;
159     register rt_ubase_t temp;
160 
161     /* wakeup all suspend threads */
162     while (!rt_list_isempty(list))
163     {
164         /* disable interrupt */
165         temp = rt_hw_interrupt_disable();
166 
167         /* get next suspend thread */
168         thread = rt_list_entry(list->next, struct rt_thread, tlist);
169         /* set error code to RT_ERROR */
170         thread->error = -RT_ERROR;
171 
172         /*
173          * resume thread
174          * In rt_thread_resume function, it will remove current thread from
175          * suspend list
176          */
177         rt_thread_resume(thread);
178 
179         /* enable interrupt */
180         rt_hw_interrupt_enable(temp);
181     }
182 
183     return RT_EOK;
184 }
185 
186 #ifdef RT_USING_SEMAPHORE
187 /**
188  * This function will initialize a semaphore and put it under control of
189  * resource management.
190  *
191  * @param sem the semaphore object
192  * @param name the name of semaphore
193  * @param value the init value of semaphore
194  * @param flag the flag of semaphore
195  *
196  * @return the operation status, RT_EOK on successful
197  */
rt_sem_init(rt_sem_t sem,const char * name,rt_uint32_t value,rt_uint8_t flag)198 rt_err_t rt_sem_init(rt_sem_t    sem,
199                      const char *name,
200                      rt_uint32_t value,
201                      rt_uint8_t  flag)
202 {
203     RT_ASSERT(sem != RT_NULL);
204 
205     /* init object */
206     rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name);
207 
208     /* init ipc object */
209     rt_ipc_object_init(&(sem->parent));
210 
211     /* set init value */
212     sem->value = value;
213 
214     /* set parent */
215     sem->parent.parent.flag = flag;
216 
217     return RT_EOK;
218 }
219 RTM_EXPORT(rt_sem_init);
220 
221 /**
222  * This function will detach a semaphore from resource management
223  *
224  * @param sem the semaphore object
225  *
226  * @return the operation status, RT_EOK on successful
227  *
228  * @see rt_sem_delete
229  */
rt_sem_detach(rt_sem_t sem)230 rt_err_t rt_sem_detach(rt_sem_t sem)
231 {
232     /* parameter check */
233     RT_ASSERT(sem != RT_NULL);
234     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
235     RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent));
236 
237     /* wakeup all suspend threads */
238     rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
239 
240     /* detach semaphore object */
241     rt_object_detach(&(sem->parent.parent));
242 
243     return RT_EOK;
244 }
245 RTM_EXPORT(rt_sem_detach);
246 
247 #ifdef RT_USING_HEAP
248 /**
249  * This function will create a semaphore from system resource
250  *
251  * @param name the name of semaphore
252  * @param value the init value of semaphore
253  * @param flag the flag of semaphore
254  *
255  * @return the created semaphore, RT_NULL on error happen
256  *
257  * @see rt_sem_init
258  */
rt_sem_create(const char * name,rt_uint32_t value,rt_uint8_t flag)259 rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
260 {
261     rt_sem_t sem;
262 
263     RT_DEBUG_NOT_IN_INTERRUPT;
264 
265     /* allocate object */
266     sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name);
267     if (sem == RT_NULL)
268         return sem;
269 
270     /* init ipc object */
271     rt_ipc_object_init(&(sem->parent));
272 
273     /* set init value */
274     sem->value = value;
275 
276     /* set parent */
277     sem->parent.parent.flag = flag;
278 
279     return sem;
280 }
281 RTM_EXPORT(rt_sem_create);
282 
283 /**
284  * This function will delete a semaphore object and release the memory
285  *
286  * @param sem the semaphore object
287  *
288  * @return the error code
289  *
290  * @see rt_sem_detach
291  */
rt_sem_delete(rt_sem_t sem)292 rt_err_t rt_sem_delete(rt_sem_t sem)
293 {
294     RT_DEBUG_NOT_IN_INTERRUPT;
295 
296     /* parameter check */
297     RT_ASSERT(sem != RT_NULL);
298     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
299     RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE);
300 
301     /* wakeup all suspend threads */
302     rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
303 
304     /* delete semaphore object */
305     rt_object_delete(&(sem->parent.parent));
306 
307     return RT_EOK;
308 }
309 RTM_EXPORT(rt_sem_delete);
310 #endif
311 
312 /**
313  * This function will take a semaphore, if the semaphore is unavailable, the
314  * thread shall wait for a specified time.
315  *
316  * @param sem the semaphore object
317  * @param time the waiting time
318  *
319  * @return the error code
320  */
rt_sem_take(rt_sem_t sem,rt_int32_t time)321 rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
322 {
323     register rt_base_t temp;
324     struct rt_thread *thread;
325 
326     /* parameter check */
327     RT_ASSERT(sem != RT_NULL);
328     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
329 
330     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent)));
331 
332     /* disable interrupt */
333     temp = rt_hw_interrupt_disable();
334 
335     RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n",
336                                 rt_thread_self()->name,
337                                 ((struct rt_object *)sem)->name,
338                                 sem->value));
339 
340     if (sem->value > 0)
341     {
342         /* semaphore is available */
343         sem->value --;
344 
345         /* enable interrupt */
346         rt_hw_interrupt_enable(temp);
347     }
348     else
349     {
350         /* no waiting, return with timeout */
351         if (time == 0)
352         {
353             rt_hw_interrupt_enable(temp);
354 
355             return -RT_ETIMEOUT;
356         }
357         else
358         {
359             /* current context checking */
360             RT_DEBUG_IN_THREAD_CONTEXT;
361 
362             /* semaphore is unavailable, push to suspend list */
363             /* get current thread */
364             thread = rt_thread_self();
365 
366             /* reset thread error number */
367             thread->error = RT_EOK;
368 
369             RT_DEBUG_LOG(RT_DEBUG_IPC, ("sem take: suspend thread - %s\n",
370                                         thread->name));
371 
372             /* suspend thread */
373             rt_ipc_list_suspend(&(sem->parent.suspend_thread),
374                                 thread,
375                                 sem->parent.parent.flag);
376 
377             /* has waiting time, start thread timer */
378             if (time > 0)
379             {
380                 RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n",
381                                             thread->name));
382 
383                 /* reset the timeout of thread timer and start it */
384                 rt_timer_control(&(thread->thread_timer),
385                                  RT_TIMER_CTRL_SET_TIME,
386                                  &time);
387                 rt_timer_start(&(thread->thread_timer));
388             }
389 
390             /* enable interrupt */
391             rt_hw_interrupt_enable(temp);
392 
393             /* do schedule */
394             rt_schedule();
395 
396             if (thread->error != RT_EOK)
397             {
398                 return thread->error;
399             }
400         }
401     }
402 
403     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));
404 
405     return RT_EOK;
406 }
407 RTM_EXPORT(rt_sem_take);
408 
409 /**
410  * This function will try to take a semaphore and immediately return
411  *
412  * @param sem the semaphore object
413  *
414  * @return the error code
415  */
rt_sem_trytake(rt_sem_t sem)416 rt_err_t rt_sem_trytake(rt_sem_t sem)
417 {
418     return rt_sem_take(sem, 0);
419 }
420 RTM_EXPORT(rt_sem_trytake);
421 
422 /**
423  * This function will release a semaphore, if there are threads suspended on
424  * semaphore, it will be waked up.
425  *
426  * @param sem the semaphore object
427  *
428  * @return the error code
429  */
rt_sem_release(rt_sem_t sem)430 rt_err_t rt_sem_release(rt_sem_t sem)
431 {
432     register rt_base_t temp;
433     register rt_bool_t need_schedule;
434 
435     /* parameter check */
436     RT_ASSERT(sem != RT_NULL);
437     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
438 
439     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent)));
440 
441     need_schedule = RT_FALSE;
442 
443     /* disable interrupt */
444     temp = rt_hw_interrupt_disable();
445 
446     RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n",
447                                 rt_thread_self()->name,
448                                 ((struct rt_object *)sem)->name,
449                                 sem->value));
450 
451     if (!rt_list_isempty(&sem->parent.suspend_thread))
452     {
453         /* resume the suspended thread */
454         rt_ipc_list_resume(&(sem->parent.suspend_thread));
455         need_schedule = RT_TRUE;
456     }
457     else
458         sem->value ++; /* increase value */
459 
460     /* enable interrupt */
461     rt_hw_interrupt_enable(temp);
462 
463     /* resume a thread, re-schedule */
464     if (need_schedule == RT_TRUE)
465         rt_schedule();
466 
467     return RT_EOK;
468 }
469 RTM_EXPORT(rt_sem_release);
470 
471 /**
472  * This function can get or set some extra attributions of a semaphore object.
473  *
474  * @param sem the semaphore object
475  * @param cmd the execution command
476  * @param arg the execution argument
477  *
478  * @return the error code
479  */
rt_sem_control(rt_sem_t sem,int cmd,void * arg)480 rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg)
481 {
482     rt_ubase_t level;
483 
484     /* parameter check */
485     RT_ASSERT(sem != RT_NULL);
486     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
487 
488     if (cmd == RT_IPC_CMD_RESET)
489     {
490         rt_ubase_t value;
491 
492         /* get value */
493         value = (rt_ubase_t)arg;
494         /* disable interrupt */
495         level = rt_hw_interrupt_disable();
496 
497         /* resume all waiting thread */
498         rt_ipc_list_resume_all(&sem->parent.suspend_thread);
499 
500         /* set new value */
501         sem->value = (rt_uint16_t)value;
502 
503         /* enable interrupt */
504         rt_hw_interrupt_enable(level);
505 
506         rt_schedule();
507 
508         return RT_EOK;
509     }
510 
511     return -RT_ERROR;
512 }
513 RTM_EXPORT(rt_sem_control);
514 #endif /* end of RT_USING_SEMAPHORE */
515 
516 #ifdef RT_USING_MUTEX
517 /**
518  * This function will initialize a mutex and put it under control of resource
519  * management.
520  *
521  * @param mutex the mutex object
522  * @param name the name of mutex
523  * @param flag the flag of mutex
524  *
525  * @return the operation status, RT_EOK on successful
526  */
rt_mutex_init(rt_mutex_t mutex,const char * name,rt_uint8_t flag)527 rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
528 {
529     /* parameter check */
530     RT_ASSERT(mutex != RT_NULL);
531 
532     /* init object */
533     rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name);
534 
535     /* init ipc object */
536     rt_ipc_object_init(&(mutex->parent));
537 
538     mutex->value = 1;
539     mutex->owner = RT_NULL;
540     mutex->original_priority = 0xFF;
541     mutex->hold  = 0;
542 
543     /* set flag */
544     mutex->parent.parent.flag = flag;
545 
546     return RT_EOK;
547 }
548 RTM_EXPORT(rt_mutex_init);
549 
550 /**
551  * This function will detach a mutex from resource management
552  *
553  * @param mutex the mutex object
554  *
555  * @return the operation status, RT_EOK on successful
556  *
557  * @see rt_mutex_delete
558  */
rt_mutex_detach(rt_mutex_t mutex)559 rt_err_t rt_mutex_detach(rt_mutex_t mutex)
560 {
561     /* parameter check */
562     RT_ASSERT(mutex != RT_NULL);
563     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
564     RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent));
565 
566     /* wakeup all suspend threads */
567     rt_ipc_list_resume_all(&(mutex->parent.suspend_thread));
568 
569     /* detach semaphore object */
570     rt_object_detach(&(mutex->parent.parent));
571 
572     return RT_EOK;
573 }
574 RTM_EXPORT(rt_mutex_detach);
575 
576 #ifdef RT_USING_HEAP
577 /**
578  * This function will create a mutex from system resource
579  *
580  * @param name the name of mutex
581  * @param flag the flag of mutex
582  *
583  * @return the created mutex, RT_NULL on error happen
584  *
585  * @see rt_mutex_init
586  */
rt_mutex_create(const char * name,rt_uint8_t flag)587 rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
588 {
589     struct rt_mutex *mutex;
590 
591     RT_DEBUG_NOT_IN_INTERRUPT;
592 
593     /* allocate object */
594     mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name);
595     if (mutex == RT_NULL)
596         return mutex;
597 
598     /* init ipc object */
599     rt_ipc_object_init(&(mutex->parent));
600 
601     mutex->value              = 1;
602     mutex->owner              = RT_NULL;
603     mutex->original_priority  = 0xFF;
604     mutex->hold               = 0;
605 
606     /* set flag */
607     mutex->parent.parent.flag = flag;
608 
609     return mutex;
610 }
611 RTM_EXPORT(rt_mutex_create);
612 
613 /**
614  * This function will delete a mutex object and release the memory
615  *
616  * @param mutex the mutex object
617  *
618  * @return the error code
619  *
620  * @see rt_mutex_detach
621  */
rt_mutex_delete(rt_mutex_t mutex)622 rt_err_t rt_mutex_delete(rt_mutex_t mutex)
623 {
624     RT_DEBUG_NOT_IN_INTERRUPT;
625 
626     /* parameter check */
627     RT_ASSERT(mutex != RT_NULL);
628     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
629     RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE);
630 
631     /* wakeup all suspend threads */
632     rt_ipc_list_resume_all(&(mutex->parent.suspend_thread));
633 
634     /* delete semaphore object */
635     rt_object_delete(&(mutex->parent.parent));
636 
637     return RT_EOK;
638 }
639 RTM_EXPORT(rt_mutex_delete);
640 #endif
641 
642 /**
643  * This function will take a mutex, if the mutex is unavailable, the
644  * thread shall wait for a specified time.
645  *
646  * @param mutex the mutex object
647  * @param time the waiting time
648  *
649  * @return the error code
650  */
rt_mutex_take(rt_mutex_t mutex,rt_int32_t time)651 rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
652 {
653     register rt_base_t temp;
654     struct rt_thread *thread;
655 
656     /* this function must not be used in interrupt even if time = 0 */
657     RT_DEBUG_IN_THREAD_CONTEXT;
658 
659     /* parameter check */
660     RT_ASSERT(mutex != RT_NULL);
661     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
662 
663     /* get current thread */
664     thread = rt_thread_self();
665 
666     /* disable interrupt */
667     temp = rt_hw_interrupt_disable();
668 
669     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent)));
670 
671     RT_DEBUG_LOG(RT_DEBUG_IPC,
672                  ("mutex_take: current thread %s, mutex value: %d, hold: %d\n",
673                   thread->name, mutex->value, mutex->hold));
674 
675     /* reset thread error */
676     thread->error = RT_EOK;
677 
678     if (mutex->owner == thread)
679     {
680         /* it's the same thread */
681         mutex->hold ++;
682     }
683     else
684     {
685 __again:
686         /* The value of mutex is 1 in initial status. Therefore, if the
687          * value is great than 0, it indicates the mutex is avaible.
688          */
689         if (mutex->value > 0)
690         {
691             /* mutex is available */
692             mutex->value --;
693 
694             /* set mutex owner and original priority */
695             mutex->owner             = thread;
696             mutex->original_priority = thread->current_priority;
697             mutex->hold ++;
698         }
699         else
700         {
701             /* no waiting, return with timeout */
702             if (time == 0)
703             {
704                 /* set error as timeout */
705                 thread->error = -RT_ETIMEOUT;
706 
707                 /* enable interrupt */
708                 rt_hw_interrupt_enable(temp);
709 
710                 return -RT_ETIMEOUT;
711             }
712             else
713             {
714                 /* mutex is unavailable, push to suspend list */
715                 RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n",
716                                             thread->name));
717 
718                 /* change the owner thread priority of mutex */
719                 if (thread->current_priority < mutex->owner->current_priority)
720                 {
721                     /* change the owner thread priority */
722                     rt_thread_control(mutex->owner,
723                                       RT_THREAD_CTRL_CHANGE_PRIORITY,
724                                       &thread->current_priority);
725                 }
726 
727                 /* suspend current thread */
728                 rt_ipc_list_suspend(&(mutex->parent.suspend_thread),
729                                     thread,
730                                     mutex->parent.parent.flag);
731 
732                 /* has waiting time, start thread timer */
733                 if (time > 0)
734                 {
735                     RT_DEBUG_LOG(RT_DEBUG_IPC,
736                                  ("mutex_take: start the timer of thread:%s\n",
737                                   thread->name));
738 
739                     /* reset the timeout of thread timer and start it */
740                     rt_timer_control(&(thread->thread_timer),
741                                      RT_TIMER_CTRL_SET_TIME,
742                                      &time);
743                     rt_timer_start(&(thread->thread_timer));
744                 }
745 
746                 /* enable interrupt */
747                 rt_hw_interrupt_enable(temp);
748 
749                 /* do schedule */
750                 rt_schedule();
751 
752                 if (thread->error != RT_EOK)
753                 {
754                 	/* interrupt by signal, try it again */
755                 	if (thread->error == -RT_EINTR) goto __again;
756 
757                     /* return error */
758                     return thread->error;
759                 }
760                 else
761                 {
762                     /* the mutex is taken successfully. */
763                     /* disable interrupt */
764                     temp = rt_hw_interrupt_disable();
765                 }
766             }
767         }
768     }
769 
770     /* enable interrupt */
771     rt_hw_interrupt_enable(temp);
772 
773     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent)));
774 
775     return RT_EOK;
776 }
777 RTM_EXPORT(rt_mutex_take);
778 
779 /**
780  * This function will release a mutex, if there are threads suspended on mutex,
781  * it will be waked up.
782  *
783  * @param mutex the mutex object
784  *
785  * @return the error code
786  */
rt_mutex_release(rt_mutex_t mutex)787 rt_err_t rt_mutex_release(rt_mutex_t mutex)
788 {
789     register rt_base_t temp;
790     struct rt_thread *thread;
791     rt_bool_t need_schedule;
792 
793     /* parameter check */
794     RT_ASSERT(mutex != RT_NULL);
795     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
796 
797     need_schedule = RT_FALSE;
798 
799     /* only thread could release mutex because we need test the ownership */
800     RT_DEBUG_IN_THREAD_CONTEXT;
801 
802     /* get current thread */
803     thread = rt_thread_self();
804 
805     /* disable interrupt */
806     temp = rt_hw_interrupt_disable();
807 
808     RT_DEBUG_LOG(RT_DEBUG_IPC,
809                  ("mutex_release:current thread %s, mutex value: %d, hold: %d\n",
810                   thread->name, mutex->value, mutex->hold));
811 
812     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent)));
813 
814     /* mutex only can be released by owner */
815     if (thread != mutex->owner)
816     {
817         thread->error = -RT_ERROR;
818 
819         /* enable interrupt */
820         rt_hw_interrupt_enable(temp);
821 
822         return -RT_ERROR;
823     }
824 
825     /* decrease hold */
826     mutex->hold --;
827     /* if no hold */
828     if (mutex->hold == 0)
829     {
830         /* change the owner thread to original priority */
831         if (mutex->original_priority != mutex->owner->current_priority)
832         {
833             rt_thread_control(mutex->owner,
834                               RT_THREAD_CTRL_CHANGE_PRIORITY,
835                               &(mutex->original_priority));
836         }
837 
838         /* wakeup suspended thread */
839         if (!rt_list_isempty(&mutex->parent.suspend_thread))
840         {
841             /* get suspended thread */
842             thread = rt_list_entry(mutex->parent.suspend_thread.next,
843                                    struct rt_thread,
844                                    tlist);
845 
846             RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n",
847                                         thread->name));
848 
849             /* set new owner and priority */
850             mutex->owner             = thread;
851             mutex->original_priority = thread->current_priority;
852             mutex->hold ++;
853 
854             /* resume thread */
855             rt_ipc_list_resume(&(mutex->parent.suspend_thread));
856 
857             need_schedule = RT_TRUE;
858         }
859         else
860         {
861             /* increase value */
862             mutex->value ++;
863 
864             /* clear owner */
865             mutex->owner             = RT_NULL;
866             mutex->original_priority = 0xff;
867         }
868     }
869 
870     /* enable interrupt */
871     rt_hw_interrupt_enable(temp);
872 
873     /* perform a schedule */
874     if (need_schedule == RT_TRUE)
875         rt_schedule();
876 
877     return RT_EOK;
878 }
879 RTM_EXPORT(rt_mutex_release);
880 
881 /**
882  * This function can get or set some extra attributions of a mutex object.
883  *
884  * @param mutex the mutex object
885  * @param cmd the execution command
886  * @param arg the execution argument
887  *
888  * @return the error code
889  */
rt_mutex_control(rt_mutex_t mutex,int cmd,void * arg)890 rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg)
891 {
892     /* parameter check */
893     RT_ASSERT(mutex != RT_NULL);
894     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
895 
896     return -RT_ERROR;
897 }
898 RTM_EXPORT(rt_mutex_control);
899 #endif /* end of RT_USING_MUTEX */
900 
901 #ifdef RT_USING_EVENT
902 /**
903  * This function will initialize an event and put it under control of resource
904  * management.
905  *
906  * @param event the event object
907  * @param name the name of event
908  * @param flag the flag of event
909  *
910  * @return the operation status, RT_EOK on successful
911  */
rt_event_init(rt_event_t event,const char * name,rt_uint8_t flag)912 rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
913 {
914     /* parameter check */
915     RT_ASSERT(event != RT_NULL);
916 
917     /* init object */
918     rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name);
919 
920     /* set parent flag */
921     event->parent.parent.flag = flag;
922 
923     /* init ipc object */
924     rt_ipc_object_init(&(event->parent));
925 
926     /* init event */
927     event->set = 0;
928 
929     return RT_EOK;
930 }
931 RTM_EXPORT(rt_event_init);
932 
933 /**
934  * This function will detach an event object from resource management
935  *
936  * @param event the event object
937  *
938  * @return the operation status, RT_EOK on successful
939  */
rt_event_detach(rt_event_t event)940 rt_err_t rt_event_detach(rt_event_t event)
941 {
942     /* parameter check */
943     RT_ASSERT(event != RT_NULL);
944     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
945     RT_ASSERT(rt_object_is_systemobject(&event->parent.parent));
946 
947     /* resume all suspended thread */
948     rt_ipc_list_resume_all(&(event->parent.suspend_thread));
949 
950     /* detach event object */
951     rt_object_detach(&(event->parent.parent));
952 
953     return RT_EOK;
954 }
955 RTM_EXPORT(rt_event_detach);
956 
957 #ifdef RT_USING_HEAP
958 /**
959  * This function will create an event object from system resource
960  *
961  * @param name the name of event
962  * @param flag the flag of event
963  *
964  * @return the created event, RT_NULL on error happen
965  */
rt_event_create(const char * name,rt_uint8_t flag)966 rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
967 {
968     rt_event_t event;
969 
970     RT_DEBUG_NOT_IN_INTERRUPT;
971 
972     /* allocate object */
973     event = (rt_event_t)rt_object_allocate(RT_Object_Class_Event, name);
974     if (event == RT_NULL)
975         return event;
976 
977     /* set parent */
978     event->parent.parent.flag = flag;
979 
980     /* init ipc object */
981     rt_ipc_object_init(&(event->parent));
982 
983     /* init event */
984     event->set = 0;
985 
986     return event;
987 }
988 RTM_EXPORT(rt_event_create);
989 
990 /**
991  * This function will delete an event object and release the memory
992  *
993  * @param event the event object
994  *
995  * @return the error code
996  */
rt_event_delete(rt_event_t event)997 rt_err_t rt_event_delete(rt_event_t event)
998 {
999     /* parameter check */
1000     RT_ASSERT(event != RT_NULL);
1001     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1002     RT_ASSERT(rt_object_is_systemobject(&event->parent.parent) == RT_FALSE);
1003 
1004     RT_DEBUG_NOT_IN_INTERRUPT;
1005 
1006     /* resume all suspended thread */
1007     rt_ipc_list_resume_all(&(event->parent.suspend_thread));
1008 
1009     /* delete event object */
1010     rt_object_delete(&(event->parent.parent));
1011 
1012     return RT_EOK;
1013 }
1014 RTM_EXPORT(rt_event_delete);
1015 #endif
1016 
1017 /**
1018  * This function will send an event to the event object, if there are threads
1019  * suspended on event object, it will be waked up.
1020  *
1021  * @param event the event object
1022  * @param set the event set
1023  *
1024  * @return the error code
1025  */
rt_event_send(rt_event_t event,rt_uint32_t set)1026 rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
1027 {
1028     struct rt_list_node *n;
1029     struct rt_thread *thread;
1030     register rt_ubase_t level;
1031     register rt_base_t status;
1032     rt_bool_t need_schedule;
1033 
1034     /* parameter check */
1035     RT_ASSERT(event != RT_NULL);
1036     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1037 
1038     if (set == 0)
1039         return -RT_ERROR;
1040 
1041     need_schedule = RT_FALSE;
1042 
1043     /* disable interrupt */
1044     level = rt_hw_interrupt_disable();
1045 
1046     /* set event */
1047     event->set |= set;
1048 
1049     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent)));
1050 
1051     if (!rt_list_isempty(&event->parent.suspend_thread))
1052     {
1053         /* search thread list to resume thread */
1054         n = event->parent.suspend_thread.next;
1055         while (n != &(event->parent.suspend_thread))
1056         {
1057             /* get thread */
1058             thread = rt_list_entry(n, struct rt_thread, tlist);
1059 
1060             status = -RT_ERROR;
1061             if (thread->event_info & RT_EVENT_FLAG_AND)
1062             {
1063                 if ((thread->event_set & event->set) == thread->event_set)
1064                 {
1065                     /* received an AND event */
1066                     status = RT_EOK;
1067                 }
1068             }
1069             else if (thread->event_info & RT_EVENT_FLAG_OR)
1070             {
1071                 if (thread->event_set & event->set)
1072                 {
1073                     /* save recieved event set */
1074                     thread->event_set = thread->event_set & event->set;
1075 
1076                     /* received an OR event */
1077                     status = RT_EOK;
1078                 }
1079             }
1080 
1081             /* move node to the next */
1082             n = n->next;
1083 
1084             /* condition is satisfied, resume thread */
1085             if (status == RT_EOK)
1086             {
1087                 /* clear event */
1088                 if (thread->event_info & RT_EVENT_FLAG_CLEAR)
1089                     event->set &= ~thread->event_set;
1090 
1091                 /* resume thread, and thread list breaks out */
1092                 rt_thread_resume(thread);
1093 
1094                 /* need do a scheduling */
1095                 need_schedule = RT_TRUE;
1096             }
1097         }
1098     }
1099 
1100     /* enable interrupt */
1101     rt_hw_interrupt_enable(level);
1102 
1103     /* do a schedule */
1104     if (need_schedule == RT_TRUE)
1105         rt_schedule();
1106 
1107     return RT_EOK;
1108 }
1109 RTM_EXPORT(rt_event_send);
1110 
1111 /**
1112  * This function will receive an event from event object, if the event is
1113  * unavailable, the thread shall wait for a specified time.
1114  *
1115  * @param event the fast event object
1116  * @param set the interested event set
1117  * @param option the receive option, either RT_EVENT_FLAG_AND or
1118  *        RT_EVENT_FLAG_OR should be set.
1119  * @param timeout the waiting time
1120  * @param recved the received event, if you don't care, RT_NULL can be set.
1121  *
1122  * @return the error code
1123  */
rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t * recved)1124 rt_err_t rt_event_recv(rt_event_t   event,
1125                        rt_uint32_t  set,
1126                        rt_uint8_t   option,
1127                        rt_int32_t   timeout,
1128                        rt_uint32_t *recved)
1129 {
1130     struct rt_thread *thread;
1131     register rt_ubase_t level;
1132     register rt_base_t status;
1133 
1134     RT_DEBUG_IN_THREAD_CONTEXT;
1135 
1136     /* parameter check */
1137     RT_ASSERT(event != RT_NULL);
1138     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1139 
1140     if (set == 0)
1141         return -RT_ERROR;
1142 
1143     /* init status */
1144     status = -RT_ERROR;
1145     /* get current thread */
1146     thread = rt_thread_self();
1147     /* reset thread error */
1148     thread->error = RT_EOK;
1149 
1150     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(event->parent.parent)));
1151 
1152     /* disable interrupt */
1153     level = rt_hw_interrupt_disable();
1154 
1155     /* check event set */
1156     if (option & RT_EVENT_FLAG_AND)
1157     {
1158         if ((event->set & set) == set)
1159             status = RT_EOK;
1160     }
1161     else if (option & RT_EVENT_FLAG_OR)
1162     {
1163         if (event->set & set)
1164             status = RT_EOK;
1165     }
1166     else
1167     {
1168         /* either RT_EVENT_FLAG_AND or RT_EVENT_FLAG_OR should be set */
1169         RT_ASSERT(0);
1170     }
1171 
1172     if (status == RT_EOK)
1173     {
1174         /* set received event */
1175         if (recved)
1176             *recved = (event->set & set);
1177 
1178         /* received event */
1179         if (option & RT_EVENT_FLAG_CLEAR)
1180             event->set &= ~set;
1181     }
1182     else if (timeout == 0)
1183     {
1184         /* no waiting */
1185         thread->error = -RT_ETIMEOUT;
1186     }
1187     else
1188     {
1189         /* fill thread event info */
1190         thread->event_set  = set;
1191         thread->event_info = option;
1192 
1193         /* put thread to suspended thread list */
1194         rt_ipc_list_suspend(&(event->parent.suspend_thread),
1195                             thread,
1196                             event->parent.parent.flag);
1197 
1198         /* if there is a waiting timeout, active thread timer */
1199         if (timeout > 0)
1200         {
1201             /* reset the timeout of thread timer and start it */
1202             rt_timer_control(&(thread->thread_timer),
1203                              RT_TIMER_CTRL_SET_TIME,
1204                              &timeout);
1205             rt_timer_start(&(thread->thread_timer));
1206         }
1207 
1208         /* enable interrupt */
1209         rt_hw_interrupt_enable(level);
1210 
1211         /* do a schedule */
1212         rt_schedule();
1213 
1214         if (thread->error != RT_EOK)
1215         {
1216             /* return error */
1217             return thread->error;
1218         }
1219 
1220         /* received an event, disable interrupt to protect */
1221         level = rt_hw_interrupt_disable();
1222 
1223         /* set received event */
1224         if (recved)
1225             *recved = thread->event_set;
1226     }
1227 
1228     /* enable interrupt */
1229     rt_hw_interrupt_enable(level);
1230 
1231     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(event->parent.parent)));
1232 
1233     return thread->error;
1234 }
1235 RTM_EXPORT(rt_event_recv);
1236 
1237 /**
1238  * This function can get or set some extra attributions of an event object.
1239  *
1240  * @param event the event object
1241  * @param cmd the execution command
1242  * @param arg the execution argument
1243  *
1244  * @return the error code
1245  */
rt_event_control(rt_event_t event,int cmd,void * arg)1246 rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg)
1247 {
1248     rt_ubase_t level;
1249 
1250     /* parameter check */
1251     RT_ASSERT(event != RT_NULL);
1252     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1253 
1254     if (cmd == RT_IPC_CMD_RESET)
1255     {
1256         /* disable interrupt */
1257         level = rt_hw_interrupt_disable();
1258 
1259         /* resume all waiting thread */
1260         rt_ipc_list_resume_all(&event->parent.suspend_thread);
1261 
1262         /* init event set */
1263         event->set = 0;
1264 
1265         /* enable interrupt */
1266         rt_hw_interrupt_enable(level);
1267 
1268         rt_schedule();
1269 
1270         return RT_EOK;
1271     }
1272 
1273     return -RT_ERROR;
1274 }
1275 RTM_EXPORT(rt_event_control);
1276 #endif /* end of RT_USING_EVENT */
1277 
1278 #ifdef RT_USING_MAILBOX
1279 /**
1280  * This function will initialize a mailbox and put it under control of resource
1281  * management.
1282  *
1283  * @param mb the mailbox object
1284  * @param name the name of mailbox
1285  * @param msgpool the begin address of buffer to save received mail
1286  * @param size the size of mailbox
1287  * @param flag the flag of mailbox
1288  *
1289  * @return the operation status, RT_EOK on successful
1290  */
rt_mb_init(rt_mailbox_t mb,const char * name,void * msgpool,rt_size_t size,rt_uint8_t flag)1291 rt_err_t rt_mb_init(rt_mailbox_t mb,
1292                     const char  *name,
1293                     void        *msgpool,
1294                     rt_size_t    size,
1295                     rt_uint8_t   flag)
1296 {
1297     RT_ASSERT(mb != RT_NULL);
1298 
1299     /* init object */
1300     rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name);
1301 
1302     /* set parent flag */
1303     mb->parent.parent.flag = flag;
1304 
1305     /* init ipc object */
1306     rt_ipc_object_init(&(mb->parent));
1307 
1308     /* init mailbox */
1309     mb->msg_pool   = msgpool;
1310     mb->size       = size;
1311     mb->entry      = 0;
1312     mb->in_offset  = 0;
1313     mb->out_offset = 0;
1314 
1315     /* init an additional list of sender suspend thread */
1316     rt_list_init(&(mb->suspend_sender_thread));
1317 
1318     return RT_EOK;
1319 }
1320 RTM_EXPORT(rt_mb_init);
1321 
1322 /**
1323  * This function will detach a mailbox from resource management
1324  *
1325  * @param mb the mailbox object
1326  *
1327  * @return the operation status, RT_EOK on successful
1328  */
rt_mb_detach(rt_mailbox_t mb)1329 rt_err_t rt_mb_detach(rt_mailbox_t mb)
1330 {
1331     /* parameter check */
1332     RT_ASSERT(mb != RT_NULL);
1333     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
1334     RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent));
1335 
1336     /* resume all suspended thread */
1337     rt_ipc_list_resume_all(&(mb->parent.suspend_thread));
1338     /* also resume all mailbox private suspended thread */
1339     rt_ipc_list_resume_all(&(mb->suspend_sender_thread));
1340 
1341     /* detach mailbox object */
1342     rt_object_detach(&(mb->parent.parent));
1343 
1344     return RT_EOK;
1345 }
1346 RTM_EXPORT(rt_mb_detach);
1347 
1348 #ifdef RT_USING_HEAP
1349 /**
1350  * This function will create a mailbox object from system resource
1351  *
1352  * @param name the name of mailbox
1353  * @param size the size of mailbox
1354  * @param flag the flag of mailbox
1355  *
1356  * @return the created mailbox, RT_NULL on error happen
1357  */
rt_mb_create(const char * name,rt_size_t size,rt_uint8_t flag)1358 rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
1359 {
1360     rt_mailbox_t mb;
1361 
1362     RT_DEBUG_NOT_IN_INTERRUPT;
1363 
1364     /* allocate object */
1365     mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name);
1366     if (mb == RT_NULL)
1367         return mb;
1368 
1369     /* set parent */
1370     mb->parent.parent.flag = flag;
1371 
1372     /* init ipc object */
1373     rt_ipc_object_init(&(mb->parent));
1374 
1375     /* init mailbox */
1376     mb->size     = size;
1377     mb->msg_pool = RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t));
1378     if (mb->msg_pool == RT_NULL)
1379     {
1380         /* delete mailbox object */
1381         rt_object_delete(&(mb->parent.parent));
1382 
1383         return RT_NULL;
1384     }
1385     mb->entry      = 0;
1386     mb->in_offset  = 0;
1387     mb->out_offset = 0;
1388 
1389     /* init an additional list of sender suspend thread */
1390     rt_list_init(&(mb->suspend_sender_thread));
1391 
1392     return mb;
1393 }
1394 RTM_EXPORT(rt_mb_create);
1395 
1396 /**
1397  * This function will delete a mailbox object and release the memory
1398  *
1399  * @param mb the mailbox object
1400  *
1401  * @return the error code
1402  */
rt_mb_delete(rt_mailbox_t mb)1403 rt_err_t rt_mb_delete(rt_mailbox_t mb)
1404 {
1405     RT_DEBUG_NOT_IN_INTERRUPT;
1406 
1407     /* parameter check */
1408     RT_ASSERT(mb != RT_NULL);
1409     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
1410     RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE);
1411 
1412     /* resume all suspended thread */
1413     rt_ipc_list_resume_all(&(mb->parent.suspend_thread));
1414 
1415     /* also resume all mailbox private suspended thread */
1416     rt_ipc_list_resume_all(&(mb->suspend_sender_thread));
1417 
1418     /* free mailbox pool */
1419     RT_KERNEL_FREE(mb->msg_pool);
1420 
1421     /* delete mailbox object */
1422     rt_object_delete(&(mb->parent.parent));
1423 
1424     return RT_EOK;
1425 }
1426 RTM_EXPORT(rt_mb_delete);
1427 #endif
1428 
1429 /**
1430  * This function will send a mail to mailbox object. If the mailbox is full,
1431  * current thread will be suspended until timeout.
1432  *
1433  * @param mb the mailbox object
1434  * @param value the mail
1435  * @param timeout the waiting time
1436  *
1437  * @return the error code
1438  */
rt_mb_send_wait(rt_mailbox_t mb,rt_ubase_t value,rt_int32_t timeout)1439 rt_err_t rt_mb_send_wait(rt_mailbox_t mb,
1440                          rt_ubase_t   value,
1441                          rt_int32_t   timeout)
1442 {
1443     struct rt_thread *thread;
1444     register rt_ubase_t temp;
1445     rt_uint32_t tick_delta;
1446 
1447     /* parameter check */
1448     RT_ASSERT(mb != RT_NULL);
1449     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
1450 
1451     /* initialize delta tick */
1452     tick_delta = 0;
1453     /* get current thread */
1454     thread = rt_thread_self();
1455 
1456     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent)));
1457 
1458     /* disable interrupt */
1459     temp = rt_hw_interrupt_disable();
1460 
1461     /* for non-blocking call */
1462     if (mb->entry == mb->size && timeout == 0)
1463     {
1464         rt_hw_interrupt_enable(temp);
1465 
1466         return -RT_EFULL;
1467     }
1468 
1469     /* mailbox is full */
1470     while (mb->entry == mb->size)
1471     {
1472         /* reset error number in thread */
1473         thread->error = RT_EOK;
1474 
1475         /* no waiting, return timeout */
1476         if (timeout == 0)
1477         {
1478             /* enable interrupt */
1479             rt_hw_interrupt_enable(temp);
1480 
1481             return -RT_EFULL;
1482         }
1483 
1484         RT_DEBUG_IN_THREAD_CONTEXT;
1485         /* suspend current thread */
1486         rt_ipc_list_suspend(&(mb->suspend_sender_thread),
1487                             thread,
1488                             mb->parent.parent.flag);
1489 
1490         /* has waiting time, start thread timer */
1491         if (timeout > 0)
1492         {
1493             /* get the start tick of timer */
1494             tick_delta = rt_tick_get();
1495 
1496             RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n",
1497                                         thread->name));
1498 
1499             /* reset the timeout of thread timer and start it */
1500             rt_timer_control(&(thread->thread_timer),
1501                              RT_TIMER_CTRL_SET_TIME,
1502                              &timeout);
1503             rt_timer_start(&(thread->thread_timer));
1504         }
1505 
1506         /* enable interrupt */
1507         rt_hw_interrupt_enable(temp);
1508 
1509         /* re-schedule */
1510         rt_schedule();
1511 
1512         /* resume from suspend state */
1513         if (thread->error != RT_EOK)
1514         {
1515             /* return error */
1516             return thread->error;
1517         }
1518 
1519         /* disable interrupt */
1520         temp = rt_hw_interrupt_disable();
1521 
1522         /* if it's not waiting forever and then re-calculate timeout tick */
1523         if (timeout > 0)
1524         {
1525             tick_delta = rt_tick_get() - tick_delta;
1526             timeout -= tick_delta;
1527             if (timeout < 0)
1528                 timeout = 0;
1529         }
1530     }
1531 
1532     /* set ptr */
1533     mb->msg_pool[mb->in_offset] = value;
1534     /* increase input offset */
1535     ++ mb->in_offset;
1536     if (mb->in_offset >= mb->size)
1537         mb->in_offset = 0;
1538     /* increase message entry */
1539     mb->entry ++;
1540 
1541     /* resume suspended thread */
1542     if (!rt_list_isempty(&mb->parent.suspend_thread))
1543     {
1544         rt_ipc_list_resume(&(mb->parent.suspend_thread));
1545 
1546         /* enable interrupt */
1547         rt_hw_interrupt_enable(temp);
1548 
1549         rt_schedule();
1550 
1551         return RT_EOK;
1552     }
1553 
1554     /* enable interrupt */
1555     rt_hw_interrupt_enable(temp);
1556 
1557     return RT_EOK;
1558 }
1559 RTM_EXPORT(rt_mb_send_wait);
1560 
1561 /**
1562  * This function will send a mail to mailbox object, if there are threads
1563  * suspended on mailbox object, it will be waked up. This function will return
1564  * immediately, if you want blocking send, use rt_mb_send_wait instead.
1565  *
1566  * @param mb the mailbox object
1567  * @param value the mail
1568  *
1569  * @return the error code
1570  */
rt_mb_send(rt_mailbox_t mb,rt_ubase_t value)1571 rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value)
1572 {
1573     return rt_mb_send_wait(mb, value, 0);
1574 }
1575 RTM_EXPORT(rt_mb_send);
1576 
1577 /**
1578  * This function will receive a mail from mailbox object, if there is no mail
1579  * in mailbox object, the thread shall wait for a specified time.
1580  *
1581  * @param mb the mailbox object
1582  * @param value the received mail will be saved in
1583  * @param timeout the waiting time
1584  *
1585  * @return the error code
1586  */
rt_mb_recv(rt_mailbox_t mb,rt_ubase_t * value,rt_int32_t timeout)1587 rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
1588 {
1589     struct rt_thread *thread;
1590     register rt_ubase_t temp;
1591     rt_uint32_t tick_delta;
1592 
1593     /* parameter check */
1594     RT_ASSERT(mb != RT_NULL);
1595     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
1596 
1597     /* initialize delta tick */
1598     tick_delta = 0;
1599     /* get current thread */
1600     thread = rt_thread_self();
1601 
1602     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent)));
1603 
1604     /* disable interrupt */
1605     temp = rt_hw_interrupt_disable();
1606 
1607     /* for non-blocking call */
1608     if (mb->entry == 0 && timeout == 0)
1609     {
1610         rt_hw_interrupt_enable(temp);
1611 
1612         return -RT_ETIMEOUT;
1613     }
1614 
1615     /* mailbox is empty */
1616     while (mb->entry == 0)
1617     {
1618         /* reset error number in thread */
1619         thread->error = RT_EOK;
1620 
1621         /* no waiting, return timeout */
1622         if (timeout == 0)
1623         {
1624             /* enable interrupt */
1625             rt_hw_interrupt_enable(temp);
1626 
1627             thread->error = -RT_ETIMEOUT;
1628 
1629             return -RT_ETIMEOUT;
1630         }
1631 
1632         RT_DEBUG_IN_THREAD_CONTEXT;
1633         /* suspend current thread */
1634         rt_ipc_list_suspend(&(mb->parent.suspend_thread),
1635                             thread,
1636                             mb->parent.parent.flag);
1637 
1638         /* has waiting time, start thread timer */
1639         if (timeout > 0)
1640         {
1641             /* get the start tick of timer */
1642             tick_delta = rt_tick_get();
1643 
1644             RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_recv: start timer of thread:%s\n",
1645                                         thread->name));
1646 
1647             /* reset the timeout of thread timer and start it */
1648             rt_timer_control(&(thread->thread_timer),
1649                              RT_TIMER_CTRL_SET_TIME,
1650                              &timeout);
1651             rt_timer_start(&(thread->thread_timer));
1652         }
1653 
1654         /* enable interrupt */
1655         rt_hw_interrupt_enable(temp);
1656 
1657         /* re-schedule */
1658         rt_schedule();
1659 
1660         /* resume from suspend state */
1661         if (thread->error != RT_EOK)
1662         {
1663             /* return error */
1664             return thread->error;
1665         }
1666 
1667         /* disable interrupt */
1668         temp = rt_hw_interrupt_disable();
1669 
1670         /* if it's not waiting forever and then re-calculate timeout tick */
1671         if (timeout > 0)
1672         {
1673             tick_delta = rt_tick_get() - tick_delta;
1674             timeout -= tick_delta;
1675             if (timeout < 0)
1676                 timeout = 0;
1677         }
1678     }
1679 
1680     /* fill ptr */
1681     *value = mb->msg_pool[mb->out_offset];
1682 
1683     /* increase output offset */
1684     ++ mb->out_offset;
1685     if (mb->out_offset >= mb->size)
1686         mb->out_offset = 0;
1687     /* decrease message entry */
1688     mb->entry --;
1689 
1690     /* resume suspended thread */
1691     if (!rt_list_isempty(&(mb->suspend_sender_thread)))
1692     {
1693         rt_ipc_list_resume(&(mb->suspend_sender_thread));
1694 
1695         /* enable interrupt */
1696         rt_hw_interrupt_enable(temp);
1697 
1698         RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent)));
1699 
1700         rt_schedule();
1701 
1702         return RT_EOK;
1703     }
1704 
1705     /* enable interrupt */
1706     rt_hw_interrupt_enable(temp);
1707 
1708     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent)));
1709 
1710     return RT_EOK;
1711 }
1712 RTM_EXPORT(rt_mb_recv);
1713 
1714 /**
1715  * This function can get or set some extra attributions of a mailbox object.
1716  *
1717  * @param mb the mailbox object
1718  * @param cmd the execution command
1719  * @param arg the execution argument
1720  *
1721  * @return the error code
1722  */
rt_mb_control(rt_mailbox_t mb,int cmd,void * arg)1723 rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg)
1724 {
1725     rt_ubase_t level;
1726 
1727     /* parameter check */
1728     RT_ASSERT(mb != RT_NULL);
1729     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
1730 
1731     if (cmd == RT_IPC_CMD_RESET)
1732     {
1733         /* disable interrupt */
1734         level = rt_hw_interrupt_disable();
1735 
1736         /* resume all waiting thread */
1737         rt_ipc_list_resume_all(&(mb->parent.suspend_thread));
1738         /* also resume all mailbox private suspended thread */
1739         rt_ipc_list_resume_all(&(mb->suspend_sender_thread));
1740 
1741         /* re-init mailbox */
1742         mb->entry      = 0;
1743         mb->in_offset  = 0;
1744         mb->out_offset = 0;
1745 
1746         /* enable interrupt */
1747         rt_hw_interrupt_enable(level);
1748 
1749         rt_schedule();
1750 
1751         return RT_EOK;
1752     }
1753 
1754     return -RT_ERROR;
1755 }
1756 RTM_EXPORT(rt_mb_control);
1757 #endif /* end of RT_USING_MAILBOX */
1758 
1759 #ifdef RT_USING_MESSAGEQUEUE
1760 struct rt_mq_message
1761 {
1762     struct rt_mq_message *next;
1763 };
1764 
1765 /**
1766  * This function will initialize a message queue and put it under control of
1767  * resource management.
1768  *
1769  * @param mq the message object
1770  * @param name the name of message queue
1771  * @param msgpool the beginning address of buffer to save messages
1772  * @param msg_size the maximum size of message
1773  * @param pool_size the size of buffer to save messages
1774  * @param flag the flag of message queue
1775  *
1776  * @return the operation status, RT_EOK on successful
1777  */
rt_mq_init(rt_mq_t mq,const char * name,void * msgpool,rt_size_t msg_size,rt_size_t pool_size,rt_uint8_t flag)1778 rt_err_t rt_mq_init(rt_mq_t     mq,
1779                     const char *name,
1780                     void       *msgpool,
1781                     rt_size_t   msg_size,
1782                     rt_size_t   pool_size,
1783                     rt_uint8_t  flag)
1784 {
1785     struct rt_mq_message *head;
1786     register rt_base_t temp;
1787 
1788     /* parameter check */
1789     RT_ASSERT(mq != RT_NULL);
1790 
1791     /* init object */
1792     rt_object_init(&(mq->parent.parent), RT_Object_Class_MessageQueue, name);
1793 
1794     /* set parent flag */
1795     mq->parent.parent.flag = flag;
1796 
1797     /* init ipc object */
1798     rt_ipc_object_init(&(mq->parent));
1799 
1800     /* set messasge pool */
1801     mq->msg_pool = msgpool;
1802 
1803     /* get correct message size */
1804     mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE);
1805     mq->max_msgs = pool_size / (mq->msg_size + sizeof(struct rt_mq_message));
1806 
1807     /* init message list */
1808     mq->msg_queue_head = RT_NULL;
1809     mq->msg_queue_tail = RT_NULL;
1810 
1811     /* init message empty list */
1812     mq->msg_queue_free = RT_NULL;
1813     for (temp = 0; temp < mq->max_msgs; temp ++)
1814     {
1815         head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool +
1816                                         temp * (mq->msg_size + sizeof(struct rt_mq_message)));
1817         head->next = mq->msg_queue_free;
1818         mq->msg_queue_free = head;
1819     }
1820 
1821     /* the initial entry is zero */
1822     mq->entry = 0;
1823 
1824     return RT_EOK;
1825 }
1826 RTM_EXPORT(rt_mq_init);
1827 
1828 /**
1829  * This function will detach a message queue object from resource management
1830  *
1831  * @param mq the message queue object
1832  *
1833  * @return the operation status, RT_EOK on successful
1834  */
rt_mq_detach(rt_mq_t mq)1835 rt_err_t rt_mq_detach(rt_mq_t mq)
1836 {
1837     /* parameter check */
1838     RT_ASSERT(mq != RT_NULL);
1839     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
1840     RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent));
1841 
1842     /* resume all suspended thread */
1843     rt_ipc_list_resume_all(&mq->parent.suspend_thread);
1844 
1845     /* detach message queue object */
1846     rt_object_detach(&(mq->parent.parent));
1847 
1848     return RT_EOK;
1849 }
1850 RTM_EXPORT(rt_mq_detach);
1851 
1852 #ifdef RT_USING_HEAP
1853 /**
1854  * This function will create a message queue object from system resource
1855  *
1856  * @param name the name of message queue
1857  * @param msg_size the size of message
1858  * @param max_msgs the maximum number of message in queue
1859  * @param flag the flag of message queue
1860  *
1861  * @return the created message queue, RT_NULL on error happen
1862  */
rt_mq_create(const char * name,rt_size_t msg_size,rt_size_t max_msgs,rt_uint8_t flag)1863 rt_mq_t rt_mq_create(const char *name,
1864                      rt_size_t   msg_size,
1865                      rt_size_t   max_msgs,
1866                      rt_uint8_t  flag)
1867 {
1868     struct rt_messagequeue *mq;
1869     struct rt_mq_message *head;
1870     register rt_base_t temp;
1871 
1872     RT_DEBUG_NOT_IN_INTERRUPT;
1873 
1874     /* allocate object */
1875     mq = (rt_mq_t)rt_object_allocate(RT_Object_Class_MessageQueue, name);
1876     if (mq == RT_NULL)
1877         return mq;
1878 
1879     /* set parent */
1880     mq->parent.parent.flag = flag;
1881 
1882     /* init ipc object */
1883     rt_ipc_object_init(&(mq->parent));
1884 
1885     /* init message queue */
1886 
1887     /* get correct message size */
1888     mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE);
1889     mq->max_msgs = max_msgs;
1890 
1891     /* allocate message pool */
1892     mq->msg_pool = RT_KERNEL_MALLOC((mq->msg_size + sizeof(struct rt_mq_message)) * mq->max_msgs);
1893     if (mq->msg_pool == RT_NULL)
1894     {
1895         rt_mq_delete(mq);
1896 
1897         return RT_NULL;
1898     }
1899 
1900     /* init message list */
1901     mq->msg_queue_head = RT_NULL;
1902     mq->msg_queue_tail = RT_NULL;
1903 
1904     /* init message empty list */
1905     mq->msg_queue_free = RT_NULL;
1906     for (temp = 0; temp < mq->max_msgs; temp ++)
1907     {
1908         head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool +
1909                                         temp * (mq->msg_size + sizeof(struct rt_mq_message)));
1910         head->next = mq->msg_queue_free;
1911         mq->msg_queue_free = head;
1912     }
1913 
1914     /* the initial entry is zero */
1915     mq->entry = 0;
1916 
1917     return mq;
1918 }
1919 RTM_EXPORT(rt_mq_create);
1920 
1921 /**
1922  * This function will delete a message queue object and release the memory
1923  *
1924  * @param mq the message queue object
1925  *
1926  * @return the error code
1927  */
rt_mq_delete(rt_mq_t mq)1928 rt_err_t rt_mq_delete(rt_mq_t mq)
1929 {
1930     RT_DEBUG_NOT_IN_INTERRUPT;
1931 
1932     /* parameter check */
1933     RT_ASSERT(mq != RT_NULL);
1934     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
1935     RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent) == RT_FALSE);
1936 
1937     /* resume all suspended thread */
1938     rt_ipc_list_resume_all(&(mq->parent.suspend_thread));
1939 
1940     /* free message queue pool */
1941     RT_KERNEL_FREE(mq->msg_pool);
1942 
1943     /* delete message queue object */
1944     rt_object_delete(&(mq->parent.parent));
1945 
1946     return RT_EOK;
1947 }
1948 RTM_EXPORT(rt_mq_delete);
1949 #endif
1950 
1951 /**
1952  * This function will send a message to message queue object, if there are
1953  * threads suspended on message queue object, it will be waked up.
1954  *
1955  * @param mq the message queue object
1956  * @param buffer the message
1957  * @param size the size of buffer
1958  *
1959  * @return the error code
1960  */
rt_mq_send(rt_mq_t mq,void * buffer,rt_size_t size)1961 rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size)
1962 {
1963     register rt_ubase_t temp;
1964     struct rt_mq_message *msg;
1965 
1966     /* parameter check */
1967     RT_ASSERT(mq != RT_NULL);
1968     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
1969     RT_ASSERT(buffer != RT_NULL);
1970     RT_ASSERT(size != 0);
1971 
1972     /* greater than one message size */
1973     if (size > mq->msg_size)
1974         return -RT_ERROR;
1975 
1976     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent)));
1977 
1978     /* disable interrupt */
1979     temp = rt_hw_interrupt_disable();
1980 
1981     /* get a free list, there must be an empty item */
1982     msg = (struct rt_mq_message *)mq->msg_queue_free;
1983     /* message queue is full */
1984     if (msg == RT_NULL)
1985     {
1986         /* enable interrupt */
1987         rt_hw_interrupt_enable(temp);
1988 
1989         return -RT_EFULL;
1990     }
1991     /* move free list pointer */
1992     mq->msg_queue_free = msg->next;
1993 
1994     /* enable interrupt */
1995     rt_hw_interrupt_enable(temp);
1996 
1997     /* the msg is the new tailer of list, the next shall be NULL */
1998     msg->next = RT_NULL;
1999     /* copy buffer */
2000     rt_memcpy(msg + 1, buffer, size);
2001 
2002     /* disable interrupt */
2003     temp = rt_hw_interrupt_disable();
2004     /* link msg to message queue */
2005     if (mq->msg_queue_tail != RT_NULL)
2006     {
2007         /* if the tail exists, */
2008         ((struct rt_mq_message *)mq->msg_queue_tail)->next = msg;
2009     }
2010 
2011     /* set new tail */
2012     mq->msg_queue_tail = msg;
2013     /* if the head is empty, set head */
2014     if (mq->msg_queue_head == RT_NULL)
2015         mq->msg_queue_head = msg;
2016 
2017     /* increase message entry */
2018     mq->entry ++;
2019 
2020     /* resume suspended thread */
2021     if (!rt_list_isempty(&mq->parent.suspend_thread))
2022     {
2023         rt_ipc_list_resume(&(mq->parent.suspend_thread));
2024 
2025         /* enable interrupt */
2026         rt_hw_interrupt_enable(temp);
2027 
2028         rt_schedule();
2029 
2030         return RT_EOK;
2031     }
2032 
2033     /* enable interrupt */
2034     rt_hw_interrupt_enable(temp);
2035 
2036     return RT_EOK;
2037 }
2038 RTM_EXPORT(rt_mq_send);
2039 
2040 /**
2041  * This function will send an urgent message to message queue object, which
2042  * means the message will be inserted to the head of message queue. If there
2043  * are threads suspended on message queue object, it will be waked up.
2044  *
2045  * @param mq the message queue object
2046  * @param buffer the message
2047  * @param size the size of buffer
2048  *
2049  * @return the error code
2050  */
rt_mq_urgent(rt_mq_t mq,void * buffer,rt_size_t size)2051 rt_err_t rt_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size)
2052 {
2053     register rt_ubase_t temp;
2054     struct rt_mq_message *msg;
2055 
2056     /* parameter check */
2057     RT_ASSERT(mq != RT_NULL);
2058     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
2059     RT_ASSERT(buffer != RT_NULL);
2060     RT_ASSERT(size != 0);
2061 
2062     /* greater than one message size */
2063     if (size > mq->msg_size)
2064         return -RT_ERROR;
2065 
2066     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent)));
2067 
2068     /* disable interrupt */
2069     temp = rt_hw_interrupt_disable();
2070 
2071     /* get a free list, there must be an empty item */
2072     msg = (struct rt_mq_message *)mq->msg_queue_free;
2073     /* message queue is full */
2074     if (msg == RT_NULL)
2075     {
2076         /* enable interrupt */
2077         rt_hw_interrupt_enable(temp);
2078 
2079         return -RT_EFULL;
2080     }
2081     /* move free list pointer */
2082     mq->msg_queue_free = msg->next;
2083 
2084     /* enable interrupt */
2085     rt_hw_interrupt_enable(temp);
2086 
2087     /* copy buffer */
2088     rt_memcpy(msg + 1, buffer, size);
2089 
2090     /* disable interrupt */
2091     temp = rt_hw_interrupt_disable();
2092 
2093     /* link msg to the beginning of message queue */
2094     msg->next = mq->msg_queue_head;
2095     mq->msg_queue_head = msg;
2096 
2097     /* if there is no tail */
2098     if (mq->msg_queue_tail == RT_NULL)
2099         mq->msg_queue_tail = msg;
2100 
2101     /* increase message entry */
2102     mq->entry ++;
2103 
2104     /* resume suspended thread */
2105     if (!rt_list_isempty(&mq->parent.suspend_thread))
2106     {
2107         rt_ipc_list_resume(&(mq->parent.suspend_thread));
2108 
2109         /* enable interrupt */
2110         rt_hw_interrupt_enable(temp);
2111 
2112         rt_schedule();
2113 
2114         return RT_EOK;
2115     }
2116 
2117     /* enable interrupt */
2118     rt_hw_interrupt_enable(temp);
2119 
2120     return RT_EOK;
2121 }
2122 RTM_EXPORT(rt_mq_urgent);
2123 
2124 /**
2125  * This function will receive a message from message queue object, if there is
2126  * no message in message queue object, the thread shall wait for a specified
2127  * time.
2128  *
2129  * @param mq the message queue object
2130  * @param buffer the received message will be saved in
2131  * @param size the size of buffer
2132  * @param timeout the waiting time
2133  *
2134  * @return the error code
2135  */
rt_mq_recv(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t timeout)2136 rt_err_t rt_mq_recv(rt_mq_t    mq,
2137                     void      *buffer,
2138                     rt_size_t  size,
2139                     rt_int32_t timeout)
2140 {
2141     struct rt_thread *thread;
2142     register rt_ubase_t temp;
2143     struct rt_mq_message *msg;
2144     rt_uint32_t tick_delta;
2145 
2146     /* parameter check */
2147     RT_ASSERT(mq != RT_NULL);
2148     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
2149     RT_ASSERT(buffer != RT_NULL);
2150     RT_ASSERT(size != 0);
2151 
2152     /* initialize delta tick */
2153     tick_delta = 0;
2154     /* get current thread */
2155     thread = rt_thread_self();
2156     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mq->parent.parent)));
2157 
2158     /* disable interrupt */
2159     temp = rt_hw_interrupt_disable();
2160 
2161     /* for non-blocking call */
2162     if (mq->entry == 0 && timeout == 0)
2163     {
2164         rt_hw_interrupt_enable(temp);
2165 
2166         return -RT_ETIMEOUT;
2167     }
2168 
2169     /* message queue is empty */
2170     while (mq->entry == 0)
2171     {
2172         RT_DEBUG_IN_THREAD_CONTEXT;
2173 
2174         /* reset error number in thread */
2175         thread->error = RT_EOK;
2176 
2177         /* no waiting, return timeout */
2178         if (timeout == 0)
2179         {
2180             /* enable interrupt */
2181             rt_hw_interrupt_enable(temp);
2182 
2183             thread->error = -RT_ETIMEOUT;
2184 
2185             return -RT_ETIMEOUT;
2186         }
2187 
2188         /* suspend current thread */
2189         rt_ipc_list_suspend(&(mq->parent.suspend_thread),
2190                             thread,
2191                             mq->parent.parent.flag);
2192 
2193         /* has waiting time, start thread timer */
2194         if (timeout > 0)
2195         {
2196             /* get the start tick of timer */
2197             tick_delta = rt_tick_get();
2198 
2199             RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n",
2200                                         thread->name));
2201 
2202             /* reset the timeout of thread timer and start it */
2203             rt_timer_control(&(thread->thread_timer),
2204                              RT_TIMER_CTRL_SET_TIME,
2205                              &timeout);
2206             rt_timer_start(&(thread->thread_timer));
2207         }
2208 
2209         /* enable interrupt */
2210         rt_hw_interrupt_enable(temp);
2211 
2212         /* re-schedule */
2213         rt_schedule();
2214 
2215         /* recv message */
2216         if (thread->error != RT_EOK)
2217         {
2218             /* return error */
2219             return thread->error;
2220         }
2221 
2222         /* disable interrupt */
2223         temp = rt_hw_interrupt_disable();
2224 
2225         /* if it's not waiting forever and then re-calculate timeout tick */
2226         if (timeout > 0)
2227         {
2228             tick_delta = rt_tick_get() - tick_delta;
2229             timeout -= tick_delta;
2230             if (timeout < 0)
2231                 timeout = 0;
2232         }
2233     }
2234 
2235     /* get message from queue */
2236     msg = (struct rt_mq_message *)mq->msg_queue_head;
2237 
2238     /* move message queue head */
2239     mq->msg_queue_head = msg->next;
2240     /* reach queue tail, set to NULL */
2241     if (mq->msg_queue_tail == msg)
2242         mq->msg_queue_tail = RT_NULL;
2243 
2244     /* decrease message entry */
2245     mq->entry --;
2246 
2247     /* enable interrupt */
2248     rt_hw_interrupt_enable(temp);
2249 
2250     /* copy message */
2251     rt_memcpy(buffer, msg + 1, size > mq->msg_size ? mq->msg_size : size);
2252 
2253     /* disable interrupt */
2254     temp = rt_hw_interrupt_disable();
2255     /* put message to free list */
2256     msg->next = (struct rt_mq_message *)mq->msg_queue_free;
2257     mq->msg_queue_free = msg;
2258     /* enable interrupt */
2259     rt_hw_interrupt_enable(temp);
2260 
2261     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent)));
2262 
2263     return RT_EOK;
2264 }
2265 RTM_EXPORT(rt_mq_recv);
2266 
2267 /**
2268  * This function can get or set some extra attributions of a message queue
2269  * object.
2270  *
2271  * @param mq the message queue object
2272  * @param cmd the execution command
2273  * @param arg the execution argument
2274  *
2275  * @return the error code
2276  */
rt_mq_control(rt_mq_t mq,int cmd,void * arg)2277 rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg)
2278 {
2279     rt_ubase_t level;
2280     struct rt_mq_message *msg;
2281 
2282     /* parameter check */
2283     RT_ASSERT(mq != RT_NULL);
2284     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
2285 
2286     if (cmd == RT_IPC_CMD_RESET)
2287     {
2288         /* disable interrupt */
2289         level = rt_hw_interrupt_disable();
2290 
2291         /* resume all waiting thread */
2292         rt_ipc_list_resume_all(&mq->parent.suspend_thread);
2293 
2294         /* release all message in the queue */
2295         while (mq->msg_queue_head != RT_NULL)
2296         {
2297             /* get message from queue */
2298             msg = (struct rt_mq_message *)mq->msg_queue_head;
2299 
2300             /* move message queue head */
2301             mq->msg_queue_head = msg->next;
2302             /* reach queue tail, set to NULL */
2303             if (mq->msg_queue_tail == msg)
2304                 mq->msg_queue_tail = RT_NULL;
2305 
2306             /* put message to free list */
2307             msg->next = (struct rt_mq_message *)mq->msg_queue_free;
2308             mq->msg_queue_free = msg;
2309         }
2310 
2311         /* clean entry */
2312         mq->entry = 0;
2313 
2314         /* enable interrupt */
2315         rt_hw_interrupt_enable(level);
2316 
2317         rt_schedule();
2318 
2319         return RT_EOK;
2320     }
2321 
2322     return -RT_ERROR;
2323 }
2324 RTM_EXPORT(rt_mq_control);
2325 #endif /* end of RT_USING_MESSAGEQUEUE */
2326 
2327 /**@}*/
2328