# 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`