上帝说要有光,于是便有了光。
本文主要介绍Mailbox驱动、Mailbox通信和framebuffer的初始化。
核心实现部分涉及四个文件,驱动模块mailbox.rs
,mailbox通信消息msg.rs
,全局framebuffer相关framebuffer.rs
以及提供顶层显示的gpu.rs
。
实现的步骤考虑自底向上,首先完成驱动接口,然后实现消息抽象并借助mailbox向GPU请求FrameBuffer空间。最后在framebuffer基础上实现高层次绘制函数。
1. Mailbox驱动
channel定义:
1 |
|
然后是寄存器和顶层定义
1 |
|
驱动对外提供两个方法:读和写。
- 根据channel号,从mailbox寄存器读取有效的buffer地址
- 根据channe号和buffer地址,写mailbox寄存器
1 |
|
2. 核间通信:消息缓存 + Tags
实现好mailbox的驱动之后,下一步就是在msg.rs
实现消息抽象。对外提供的接口是需要调用者提供Tag切片的pub fn send_messages(tags: &mut [Tag]) -> OsResult<()>;
。
2.1. 消息缓存与Tag格式
为了向GPU请求framebuffer,首先需要能够正确配置核间通信的消息缓存。消息缓存包含了两层抽象如下:
- 消息缓存
- tag缓存
1 |
|
下面是请求GPU分配framebuffer使用到的tag定义。
如上图可见,
2.1.1. Allocate buffer
- Tag: 0x00040001
- Request:
- Length: 4
- Value:
- u32: alignment in bytes
- Response:
- Length: 8
- Value:
- u32: frame buffer base address in bytes
- u32: frame buffer size in bytes
2.1.2. Set physical (display) width/height
physical display size是内存中分配的framebuffer大小,并非发送给显示设备的分辨率。
- Tag: 0x00048003
- Request:
- Length: 8
- Value:
- u32: width in pixels
- u32: height in pixels
- Response:
- Length: 8
- Value:
- u32: width in pixels
- u32: height in pixels
2.1.3. Set virtual (buffer) width/height
virtual buffer是GPU发送给显示设备的内存部分。
- Tag: 0x00048004
- Request:
- Length: 8
- Value:
- u32: width in pixels
- u32: height in pixels
- Response:
- Length: 8
- Value:
- u32: width in pixels
- u32: height in pixels
The response may not be the same as the request so it must be checked. May be the previous width/height or 0 for unsupported.
2.1.4. Set pixel order
- Tag: 0x00048006
- Request:
- Length: 4
- Value:
- u32: state (as above)
- Response:
- Length: 4
- Value:
- u32: state (as above)
- Request:
2.1.5. Set depth
- Tag: 0x00048005
- Request:
- Length: 4
- Value:
- u32: bits per pixel
- Response:
- Length: 4
- Value:
- u32: bits per pixel
2.2. 定义格式
由于整个消息缓存包含了两层抽象,且两层抽象的大小都是可变的。比如不同类型tag的大小不同,因此使用什么数据结构表示tag较为麻烦。
我的实现从为了发送消息,接口需要用户提供什么信息这个角度出发。具体而言,接口只需要用户提供要发送的tag,自己负责设置缓存。更进一步,依据接口最小化原则,只需要用户提供 tag的类型/id 和 tag的值。
于是,向用户导出下面的结构体:
1 |
|
基于这两个结构体就能实现send_message
函数,主要逻辑就是依据官方的格式把消息缓存配置出来。然后用之前实现的mailbox驱动进行发送。
1 |
|
3. 请求分配framebuffer
在能够发送消息之后,下一步实现的是framebuffer初始化和绘制基础设施。framebuffer是一个全局结构,因此外部需要套一层Mutex。
1 |
|
内部的framebuffer结构体,记录了真正的元数据,以及指向buffer的指针。同时提供了初始化的方法
1 |
|
这里配置的virtual buffer和physical buffer使用相同的配置,我使用的3.5寸HDMI显示屏分辨率是480 x 320。共发送7个tag进行framebuffer初始化配置,分别为:
- 设置物理宽高
- 设置虚拟宽高
- 设置pixel深度
- 设置虚拟起始偏移0,0
- 获取一行字节数
- 分配framebuffer
- 设置pixel顺序(这个好像目前不起作用)
对于属性设置tag,需要和分配framebuffer的tag存放于单次消息中,不然会被gpu侧忽略。让我盲目debug了半天才发现。
4. 顶层绘图测试
最后是gpu.rs
中顶层的绘图测试函数,这里测试非常简单,就是将整个屏幕铺满一种颜色。
1 |
|
这几个模块实现之后,就可以随意填充屏幕了,如下面这样:
]
绿色的,不是很好看。接下来嘛,自然就是基于基本的绘图功能,实现一个较为高级的渲染基础设施:字体渲染。下一篇传送门