04

はじめに

Githubで公開されている,GitHub - bztsrc/raspi3-tutorial: Bare metal Raspberry Pi 3 tutorialsのコードを読んでいく.今回は04_mailboxesを理解することを目的とする.

Mailboxについて

Mailboxの役割はARMとVideoCore間の通信を補助すること.Mailboxは2つ用意されていて,以下のようにそれぞれ用途が違う.

MB 0: ARMから書き込むと,その情報がVCに伝わりVCが応答を返す
MB 1: VCから書き込むと,その情報がARMに伝わりARMが応答を返す

今回はARMから書き込むことを想定しているのでMB 0だけ扱う.

具体的な手順は以下の通り
ARMからVCに通信する方法(ARMからMB1に書き込み,応答を読む)

  1. リクエストとレスポンス用のバッファを用意する
  2. バッファに必要なパラメータを書き込む
  3. リクエスト可能になるのを待つ(メールボックスのstatusレジスタを見る)
  4. メールボックスにライトリクエストを出す(メールボックスのWriteレジスタにバッファのアドレスの上位28ビットとチャンネル4ビットを書き込む)
  5. VCが処理を終えてステータスが読み取り可能になるのを待つ(メールボックスのstatusレジスタを見る)
  6. メールボックスのリード情報を読む(メールボックスのReadレジスタの上位28ビットはバッファのアドレス,下位4ビットはチャンネルが書き込まれてるはずなのでそれを読む)
  7. リード情報に含まれるチャンネルが,ライトリクエストのチャンネルと同じであれば良し
  8. バッファの中に,VCによってデータが書き込まれているはず

対象ファイル:mbox.h, mbox.c, main.c
キーワード:mailbox interface

mbox.h

/* a properly aligned buffer */
extern volatile unsigned int mbox[36];

#define MBOX_REQUEST    0

/* channels */
#define MBOX_CH_POWER   0
#define MBOX_CH_FB      1
#define MBOX_CH_VUART   2
#define MBOX_CH_VCHIQ   3
#define MBOX_CH_LEDS    4
#define MBOX_CH_BTNS    5
#define MBOX_CH_TOUCH   6
#define MBOX_CH_COUNT   7
#define MBOX_CH_PROP    8

/* tags */
#define MBOX_TAG_GETSERIAL      0x10004
#define MBOX_TAG_LAST           0

int mbox_call(unsigned char ch);

mbox.c

#include "gpio.h"

/* mailbox message buffer */
volatile unsigned int  __attribute__((aligned(16))) mbox[36];

#define VIDEOCORE_MBOX  (MMIO_BASE+0x0000B880)
#define MBOX_READ       ((volatile unsigned int*)(VIDEOCORE_MBOX+0x0))
#define MBOX_POLL       ((volatile unsigned int*)(VIDEOCORE_MBOX+0x10))
#define MBOX_SENDER     ((volatile unsigned int*)(VIDEOCORE_MBOX+0x14))
#define MBOX_STATUS     ((volatile unsigned int*)(VIDEOCORE_MBOX+0x18))
#define MBOX_CONFIG     ((volatile unsigned int*)(VIDEOCORE_MBOX+0x1C))
#define MBOX_WRITE      ((volatile unsigned int*)(VIDEOCORE_MBOX+0x20))
#define MBOX_RESPONSE   0x80000000
#define MBOX_FULL       0x80000000
#define MBOX_EMPTY      0x40000000

/**
 * Make a mailbox call. Returns 0 on failure, non-zero on success
 */
int mbox_call(unsigned char ch)
{
    unsigned int r = (((unsigned int)((unsigned long)&mbox)&~0xF) | (ch&0xF));
    /* wait until we can write to the mailbox */
    do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_FULL);
    /* write the address of our message to the mailbox with channel identifier */
    *MBOX_WRITE = r;
    /* now wait for the response */
    while(1) {
        /* is there a response? */
        do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_EMPTY);
        /* is it a response to our message? */
        if(r == *MBOX_READ)
            /* is it a valid successful response? */
            return mbox[1]==MBOX_RESPONSE;
    }
    return 0;
}

cソースファイル(main.c)

#include "uart.h"
#include "mbox.h"

void main()
{
    // set up serial console
    uart_init();
    
    // get the board's unique serial number with a mailbox call
    mbox[0] = 8*4;                  // length of the message
    mbox[1] = MBOX_REQUEST;         // this is a request message
    
    mbox[2] = MBOX_TAG_GETSERIAL;   // get serial number command
    mbox[3] = 8;                    // buffer size
    mbox[4] = 8;
    mbox[5] = 0;                    // clear output buffer
    mbox[6] = 0;

    mbox[7] = MBOX_TAG_LAST;

    // send the message to the GPU and receive answer
    if (mbox_call(MBOX_CH_PROP)) {
        uart_puts("My serial number is: ");
        uart_hex(mbox[6]);
        uart_hex(mbox[5]);
        uart_puts("\n");
    } else {
        uart_puts("Unable to query serial!\n");
    }

    // echo everything back
    while(1) {
        uart_send(uart_getc());
    }
}