通用邮箱框架¶
- 作者:
Jassi Brar <jaswinder.singh@linaro.org>
本文档旨在帮助开发人员为 API 编写客户端和控制器驱动程序。但在我们开始之前,让我们注意,客户端(尤其是)和控制器驱动程序很可能具有非常强的平台特定性,因为远程固件很可能是专有的,并实现非标准协议。因此,即使两个平台都采用例如 PL320 控制器,客户端驱动程序也不能在它们之间共享。甚至 PL320 驱动程序也可能需要适应一些平台特定的怪癖。因此,API 的主要目的是避免为每个平台编写类似的代码副本。话虽如此,没有什么可以阻止远程固件也基于 Linux 并使用相同的 API。然而,这些对我们本地都没有帮助,因为我们只处理客户端的协议级别。
实现过程中所做的一些选择是这个“通用”框架的这种特殊性的结果。
控制器驱动程序(请参阅 include/linux/mailbox_controller.h)¶
分配 mbox_controller 和 mbox_chan 数组。填充 mbox_chan_ops,除了 peek_data() 之外,所有都是强制性的。控制器驱动程序可能会通过获取 IRQ 或轮询某些硬件标志来知道消息已被远程消耗,或者它可能永远不知道(客户端通过协议知道)。首选方法按优先级排序为 IRQ -> 轮询 -> 无,控制器驱动程序应通过 'txdone_irq' 或 'txdone_poll' 或两者都不设置。
客户端驱动程序(请参阅 include/linux/mailbox_client.h)¶
客户端可能希望在阻塞模式下运行(在返回之前同步发送消息)或非阻塞/异步模式(提交消息和一个回调函数到 API 并立即返回)。
struct demo_client {
struct mbox_client cl;
struct mbox_chan *mbox;
struct completion c;
bool async;
/* ... */
};
/*
* This is the handler for data received from remote. The behaviour is purely
* dependent upon the protocol. This is just an example.
*/
static void message_from_remote(struct mbox_client *cl, void *mssg)
{
struct demo_client *dc = container_of(cl, struct demo_client, cl);
if (dc->async) {
if (is_an_ack(mssg)) {
/* An ACK to our last sample sent */
return; /* Or do something else here */
} else { /* A new message from remote */
queue_req(mssg);
}
} else {
/* Remote f/w sends only ACK packets on this channel */
return;
}
}
static void sample_sent(struct mbox_client *cl, void *mssg, int r)
{
struct demo_client *dc = container_of(cl, struct demo_client, cl);
complete(&dc->c);
}
static void client_demo(struct platform_device *pdev)
{
struct demo_client *dc_sync, *dc_async;
/* The controller already knows async_pkt and sync_pkt */
struct async_pkt ap;
struct sync_pkt sp;
dc_sync = kzalloc(sizeof(*dc_sync), GFP_KERNEL);
dc_async = kzalloc(sizeof(*dc_async), GFP_KERNEL);
/* Populate non-blocking mode client */
dc_async->cl.dev = &pdev->dev;
dc_async->cl.rx_callback = message_from_remote;
dc_async->cl.tx_done = sample_sent;
dc_async->cl.tx_block = false;
dc_async->cl.tx_tout = 0; /* doesn't matter here */
dc_async->cl.knows_txdone = false; /* depending upon protocol */
dc_async->async = true;
init_completion(&dc_async->c);
/* Populate blocking mode client */
dc_sync->cl.dev = &pdev->dev;
dc_sync->cl.rx_callback = message_from_remote;
dc_sync->cl.tx_done = NULL; /* operate in blocking mode */
dc_sync->cl.tx_block = true;
dc_sync->cl.tx_tout = 500; /* by half a second */
dc_sync->cl.knows_txdone = false; /* depending upon protocol */
dc_sync->async = false;
/* ASync mailbox is listed second in 'mboxes' property */
dc_async->mbox = mbox_request_channel(&dc_async->cl, 1);
/* Populate data packet */
/* ap.xxx = 123; etc */
/* Send async message to remote */
mbox_send_message(dc_async->mbox, &ap);
/* Sync mailbox is listed first in 'mboxes' property */
dc_sync->mbox = mbox_request_channel(&dc_sync->cl, 0);
/* Populate data packet */
/* sp.abc = 123; etc */
/* Send message to remote in blocking mode */
mbox_send_message(dc_sync->mbox, &sp);
/* At this point 'sp' has been sent */
/* Now wait for async chan to be done */
wait_for_completion(&dc_async->c);
}