The Mailbox Peripheral
Mailbox Overview
The Mailbox Peripheral is a peripheral that facilitates communication between the ARM CPU and the VideoCore GPU. It starts at offset
0xB880
and has three relevent registers. The Read
register is at offset 0x00
from the mailbox base, and facilitates reading messages from the GPU. The low 4 bits
of the register denote which channel the message is from, and the high 28 bits are data. The Status
register is at offset 0x18
from the mailbox base. Bit 30 of this
register can tell you whether the Read
register is empty and bit 31 can tell you whether the Write
register is full. The Write
Register is at offset 0x20
, and has
similar form to the Read
register
Here is the layout of the mailbox peripheral:
0 4 8 12 16 20 24 28 32
+-------+-------------------------------------------------------+
0x00 |rd chn | read data |
+-------+-------------------------------------------------------+
0x04 | Unused |
... \\ \\
0x14 | Unused |
+-----------------------------------------------------------+-+-+
0x18 | status reserved |E|F|
+-----------------------------------------------------------+-+-+
0x1C | Unused |
+-----------------------------------------------------------+-+-+
0x20 |wt chn | write data |
+-----------------------------------------------------------+-+-+
A Channel is a number that tells you and the GPU what the information being sent through the mailbox means. We will only be needing channel 1, the framebuffer channel, and channel 8, the property channel.
Reading from the Mailbox
To read from the mailbox, you must follow these steps:
- In a loop, read the status register to make sure the the read register is not empty.
- Read the contents of the read register.
- Check the channel of the read message. If it is not what you were looking for, do something responsible with it (or discard it)
- If the channel is correct, get the data.
Here is the code for read:
mail_message_t mailbox_read(int channel) {
mail_status_t stat;
mail_message_t res;
// Make sure that the message is from the right channel
do {
// Make sure there is mail to recieve
do {
stat = *MAIL0_STATUS;
} while (stat.empty);
// Get the message
res = *MAIL0_READ;
} while (res.channel != channel);
return res;
}
Where mail_message_t
and mail_status_t
are defined as follows:
typedef struct {
uint8_t channel: 4;
uint32_t data: 28;
} mail_message_t;
typedef struct {
uint32_t reserved: 30;
uint8_t empty: 1;
uint8_t full:1;
} mail_status_t;
Writing to the Mailbox
Writing has a similar process to reading:
- In a loop, check the status register to see if the write register is not full
- Write the data to the write register
Here is the code:
void mailbox_send(mail_message_t msg, int channel) {
mail_status_t stat;
msg.channel = channel;
// Make sure you can send mail
do {
stat = *MAIL0_STATUS;
} while (stat.full);
// send the message
*MAIL0_WRITE = msg;
}