# RT-Thread IPC Mail Box - 類似於 pipe,主要用來傳輸資料 - 每一封郵件大小為 4 bytes(即 32 位元) ## 結構 :::success **File:** rtdef.h ::: ```c=642 #ifdef RT_USING_MAILBOX /** * mailbox structure */ struct rt_mailbox { struct rt_ipc_object parent; /**< inherit from ipc_object */ rt_uint32_t *msg_pool; /**< start address of message buffer */ rt_uint16_t size; /**< size of message pool */ rt_uint16_t entry; /**< index of messages in msg_pool */ rt_uint16_t in_offset; /**< input offset of the message buffer */ rt_uint16_t out_offset; /**< output offset of the message buffer */ rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox */ }; typedef struct rt_mailbox *rt_mailbox_t; #endif ``` - `msg_pool` 指向郵件堆的起點,`entry` 紀錄總郵件的數量 --- ## 建立 mail box ### 動態記憶體管理 #### `rt_mb_create` | 功能 | 回傳值 | | --- | ------ | | 建立 mail box | mail box | | `*name` | `size` | `flag` | | ------- | ------ | ------ | | 名字 | mail box 大小 | FIFO / PRIO | ```c=1348 /** * This function will create a mailbox object from system resource * * @param name the name of mailbox * @param size the size of mailbox * @param flag the flag of mailbox * * @return the created mailbox, RT_NULL on error happen */ rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) { rt_mailbox_t mb; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name); if (mb == RT_NULL) return mb; ``` - 首先 allocate 一塊給 mailbox ```c=+ /* set parent */ mb->parent.parent.flag = flag; /* init ipc object */ rt_ipc_object_init(&(mb->parent)); ``` - 填入 flag 及初始化 ```c=+ /* init mailbox */ mb->size = size; mb->msg_pool = RT_KERNEL_MALLOC(mb->size * sizeof(rt_uint32_t)); if (mb->msg_pool == RT_NULL) { /* delete mailbox object */ rt_object_delete(&(mb->parent.parent)); return RT_NULL; } ``` - 因使用動態記憶體的緣故,需 allocate 一塊給郵件堆 - 大小為一封一件的大小 * size ```c=+ mb->entry = 0; mb->in_offset = 0; mb->out_offset = 0; /* init an additional list of sender suspend thread */ rt_list_init(&(mb->suspend_sender_thread)); return mb; } RTM_EXPORT(rt_mb_create); ``` - 最後初始化值及等待鏈 --- ### 靜態記憶體管理 #### `rt_mb_init` | 功能 | 回傳值 | | --- | ------ | | 初始化 mail box | `RT_EOK` | | `mb` | `*name` | `*msgpool` | `size` | `flag` | | ---- | ------- | ---------- | ------ | ------ | | mail box 本體 | 名字 | 存放郵件的地方 | mail box 大小 | FIFO / PRIO | ```c=1278 /** * This function will initialize a mailbox and put it under control of resource * management. * * @param mb the mailbox object * @param name the name of mailbox * @param msgpool the begin address of buffer to save received mail * @param size the size of mailbox * @param flag the flag of mailbox * * @return the operation status, RT_EOK on successful */ rt_err_t rt_mb_init(rt_mailbox_t mb, const char *name, void *msgpool, rt_size_t size, rt_uint8_t flag) { RT_ASSERT(mb != RT_NULL); /* init object */ rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); /* set parent flag */ mb->parent.parent.flag = flag; /* init ipc object */ rt_ipc_object_init(&(mb->parent)); ``` - 這裡就不需要 allocate,可直接初始化來使用 ```c=+ /* init mailbox */ mb->msg_pool = msgpool; mb->size = size; mb->entry = 0; mb->in_offset = 0; mb->out_offset = 0; /* init an additional list of sender suspend thread */ rt_list_init(&(mb->suspend_sender_thread)); return RT_EOK; } RTM_EXPORT(rt_mb_init); ``` - 一樣,郵件堆可直接拿來用,初始化值及等待鏈 --- ## 刪除 mail box ### 動態記憶體管理 #### `rt_mb_delete` | 功能 | 回傳值 | | --- | ------ | | 刪除 mail box | `RT_EOK` | | `mb` | | ---- | | 欲刪除的 mail box | ```c=1395 /** * This function will delete a mailbox object and release the memory * * @param mb the mailbox object * * @return the error code */ rt_err_t rt_mb_delete(rt_mailbox_t mb) { RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(mb != RT_NULL); RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE); /* resume all suspended thread */ rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); /* also resume all mailbox private suspended thread */ rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); ``` - 首先將正在等待郵件的,與正在等待傳送的 thread 叫醒 ```c=+ /* free mailbox pool */ RT_KERNEL_FREE(mb->msg_pool); /* delete mailbox object */ rt_object_delete(&(mb->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_mb_delete); ``` - 歸還郵件堆,最後刪除 mail box --- ### 靜態記憶體管理 #### `rt_mb_detach` | 功能 | 回傳值 | | --- | ------ | | 刪除 mail box | `RT_EOK` | | `mb` | | ---- | | 欲刪除的 mail box | ```c=1321 /** * This function will detach a mailbox from resource management * * @param mb the mailbox object * * @return the operation status, RT_EOK on successful */ rt_err_t rt_mb_detach(rt_mailbox_t mb) { /* parameter check */ RT_ASSERT(mb != RT_NULL); RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent)); /* resume all suspended thread */ rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); /* also resume all mailbox private suspended thread */ rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); ``` - 首先將正在等待郵件的,與正在等待傳送的 thread 叫醒 ```c=+ /* detach mailbox object */ rt_object_detach(&(mb->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_mb_detach); ``` - 最後刪除 mail box(使用 `detach`) --- ### 傳送郵件 #### `rt_mb_send_wait` | 功能 | 回傳值 | | --- | ------ | | 傳送郵件 | `RT_EOK` | | `mb` | `value` | `timeout` | | ---- | ------- | --------- | | 欲傳送的 mailbox | 郵件內容 | 等待時間(如果需要)| ```c=1428 /** * This function will send a mail to mailbox object. If the mailbox is full, * current thread will be suspended until timeout. * * @param mb the mailbox object * @param value the mail * @param timeout the waiting time * * @return the error code */ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout) { struct rt_thread *thread; register rt_ubase_t temp; rt_uint32_t tick_delta; /* parameter check */ RT_ASSERT(mb != RT_NULL); RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); /* initialize delta tick */ tick_delta = 0; /* get current thread */ thread = rt_thread_self(); RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* for non-blocking call */ if (mb->entry == mb->size && timeout == 0) { rt_hw_interrupt_enable(temp); return -RT_EFULL; } /* mailbox is full */ while (mb->entry == mb->size) { /* reset error number in thread */ thread->error = RT_EOK; /* no waiting, return timeout */ if (timeout == 0) { /* enable interrupt */ rt_hw_interrupt_enable(temp); return -RT_EFULL; } ``` - 如果 mail box 滿了,且不等待 (`timeout==0`),回傳 `FULL` ```c=+ RT_DEBUG_IN_THREAD_CONTEXT; /* suspend current thread */ rt_ipc_list_suspend(&(mb->suspend_sender_thread), thread, mb->parent.parent.flag); /* has waiting time, start thread timer */ if (timeout > 0) { /* get the start tick of timer */ tick_delta = rt_tick_get(); RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n", thread->name)); /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } ``` - 若要等待,將 thread 掛上等待鏈,啟動一個 timer ```c=+ /* enable interrupt */ rt_hw_interrupt_enable(temp); /* re-schedule */ rt_schedule(); ``` - 再做一次調度 ```c=+ /* resume from suspend state */ if (thread->error != RT_EOK) { /* return error */ return thread->error; } /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { tick_delta = rt_tick_get() - tick_delta; timeout -= tick_delta; if (timeout < 0) timeout = 0; } } ``` - 如跳回來,重新計算 `timeout` ```c=+ /* set ptr */ mb->msg_pool[mb->in_offset] = value; /* increase input offset */ ++ mb->in_offset; if (mb->in_offset >= mb->size) mb->in_offset = 0; /* increase message entry */ mb->entry ++; ``` - 若可以寫入,將資料寫入,同時更新 `offset` 及 `entry` ```c=+ /* resume suspended thread */ if (!rt_list_isempty(&mb->parent.suspend_thread)) { rt_ipc_list_resume(&(mb->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); rt_schedule(); return RT_EOK; } /* enable interrupt */ rt_hw_interrupt_enable(temp); return RT_EOK; } RTM_EXPORT(rt_mb_send_wait); ``` - 如果有人在等待寄信,叫醒他,做一次調度 --- - 若是不想等待,可以使用 `rt_mb_send` ### `rt_mb_send` | 功能 | 回傳值 | | --- | ------ | | 傳送郵件(不等待)| `RT_EOK` | | `mb` | `value` | | ---- | ------- | | 欲傳送的 mailbox | 郵件內容 | ```c=1560 /** * This function will send a mail to mailbox object, if there are threads * suspended on mailbox object, it will be waked up. This function will return * immediately, if you want blocking send, use rt_mb_send_wait instead. * * @param mb the mailbox object * @param value the mail * * @return the error code */ rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value) { return rt_mb_send_wait(mb, value, 0); } RTM_EXPORT(rt_mb_send); ``` - 即 `timeout == 0` --- ## 接受郵件 ### `rt_mb_recv` | 功能 | 回傳值 | | --- | ------ | | 接受郵件 | `RT_EOK` | | `mb` | `*value` | `timeout` | | ---- | ------- | --------- | | 欲收信的 mailbox | 郵件內容 | 等待時間(如果需要)| ```c=1576 /** * This function will receive a mail from mailbox object, if there is no mail * in mailbox object, the thread shall wait for a specified time. * * @param mb the mailbox object * @param value the received mail will be saved in * @param timeout the waiting time * * @return the error code */ rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout) { struct rt_thread *thread; register rt_ubase_t temp; rt_uint32_t tick_delta; /* parameter check */ RT_ASSERT(mb != RT_NULL); RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); /* initialize delta tick */ tick_delta = 0; /* get current thread */ thread = rt_thread_self(); RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* for non-blocking call */ if (mb->entry == 0 && timeout == 0) { rt_hw_interrupt_enable(temp); return -RT_ETIMEOUT; } /* mailbox is empty */ while (mb->entry == 0) { /* reset error number in thread */ thread->error = RT_EOK; /* no waiting, return timeout */ if (timeout == 0) { /* enable interrupt */ rt_hw_interrupt_enable(temp); thread->error = -RT_ETIMEOUT; return -RT_ETIMEOUT; } ``` - 如果 mail box 沒東西,且不等待,回傳 `TIMEOUT` ```c=+ RT_DEBUG_IN_THREAD_CONTEXT; /* suspend current thread */ rt_ipc_list_suspend(&(mb->parent.suspend_thread), thread, mb->parent.parent.flag); /* has waiting time, start thread timer */ if (timeout > 0) { /* get the start tick of timer */ tick_delta = rt_tick_get(); RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_recv: start timer of thread:%s\n", thread->name)); /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } ``` - 若要等待,將 thread 掛上等待鏈,啟動一個 timer ```c=+ /* enable interrupt */ rt_hw_interrupt_enable(temp); /* re-schedule */ rt_schedule(); ``` - 再做一次調度 ```c=+ /* resume from suspend state */ if (thread->error != RT_EOK) { /* return error */ return thread->error; } /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { tick_delta = rt_tick_get() - tick_delta; timeout -= tick_delta; if (timeout < 0) timeout = 0; } } ``` - 如跳回來,重新計算 `timeout` ```c=+ /* fill ptr */ *value = mb->msg_pool[mb->out_offset]; /* increase output offset */ ++ mb->out_offset; if (mb->out_offset >= mb->size) mb->out_offset = 0; /* decrease message entry */ mb->entry --; ``` - 若可以讀取,將資料寫入,同時更新 `offset` 及 `entry` ```c=+ /* resume suspended thread */ if (!rt_list_isempty(&(mb->suspend_sender_thread))) { rt_ipc_list_resume(&(mb->suspend_sender_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); rt_schedule(); return RT_EOK; } /* enable interrupt */ rt_hw_interrupt_enable(temp); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); return RT_EOK; } RTM_EXPORT(rt_mb_recv); ``` - 如果有人在等待收信,叫醒他,做一次調度 ###### tags: `RT-Thread` `kernel` `ipc` `mailbox`