-
Notifications
You must be signed in to change notification settings - Fork 387
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #480 from Oveln/master
三阶段blog
- Loading branch information
Showing
1 changed file
with
119 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
--- | ||
title: 2024秋冬季开源操作系统训练营第三阶段总结报告_郑昱可 | ||
date: 2024-06-29 15:33:10 | ||
categories: | ||
- report | ||
tags: | ||
- Oveln | ||
- 2024春夏季开源操作系统训练营 | ||
- 第三阶段 | ||
--- | ||
# 内容 | ||
|
||
三阶段的项目我选择了Rust for Linux驱动开发方向。 | ||
|
||
具体内容是用Rust重写PL011串口驱动,并能通过跨内核驱动框架,使驱动在arceos和linux运行 | ||
|
||
这次的项目基于Raspi4b or Raspi3b in Qemu进行开发。 | ||
|
||
我使用的开发的仓库地址是(R4L_DRV)[https://github.com/Oveln/R4L_DRV] | ||
|
||
# 练习部分 | ||
|
||
## 内容 | ||
|
||
练习部分分三个 | ||
- 用Rust for Linux写一个简单的杂项设备驱动,能做到输入和输出 | ||
- 用跨内核驱动框架,实现一个设备驱动,能控制Raspi4b上的某个引脚,从而控制LED的亮灭 | ||
|
||
## 遇到的困难 | ||
|
||
此前我完全没有接触过驱动的编写,对硬件的原理知之甚少。经过很长一段时间的RTFM,以及了解硬件编程相关的知识。 | ||
|
||
同时对Linux设备驱动相关的内容也进行了比较充足的了解。 | ||
|
||
## 收获 | ||
|
||
练习部分的收获主要是 | ||
- 第一次接触了Linux的设备驱动,对文件树中的/dev/xxx的设备文件有了认知 | ||
- 更加明白了什么叫Every thing is File | ||
- 稍微看的懂电路原理图了x | ||
- 对很长的文档的恐惧逐渐消退 | ||
|
||
# 实战部分:使用Rust重写PL011驱动 | ||
|
||
## 问题一 | ||
|
||
串口驱动的内容相较于简单的GPIO繁琐且复杂,需要我进一步的了解Linux的串口子系统tty-uart_driver-uart_port相关的注册关系。 | ||
|
||
在经过一些努力 | ||
- RTFS | ||
- 找网上的资料学习(特别感谢野火的文档教程 | ||
|
||
这个问题逐渐的被解决了 | ||
|
||
## 问题二 | ||
|
||
Rust for Linux经过数年的发展,在某些方面有了完备的抽象。但这次项目所涉及的uart_driver、uart_port、console等结构体没有被支持。 | ||
|
||
这个问题没什么简单的办法,在Rust for Linux社区中也没有找到好的实现。只能自己做。 | ||
|
||
### 如何抽象函数指针 | ||
|
||
比如,在uart_port中有很多void*类型的函数指针,如何对这些函数指针进行良好的Rust抽象就成了一大问题。 | ||
|
||
我在R4L的代码中找到的解决方案是 | ||
|
||
```c | ||
struct ops { | ||
void* print_int(int) | ||
void* print_char(char) | ||
} | ||
``` | ||
|
||
以上代码可以通过Rust进行如下封装 | ||
|
||
```rust | ||
pub struct OperationsVtable<T>(marker::PhantomData<T>); | ||
pub trait Ops { | ||
fn print_int(x: i32); | ||
fn print_char(x: char); | ||
} | ||
impl<T: Ops> OperationsVtable<T> { | ||
unsafe extern "C" fn print_int(x: c_int) { | ||
T::print_int(x); | ||
} | ||
unsafe extern "C" fn print_char(x: c_char) { | ||
T::print_char(x); | ||
} | ||
|
||
const VTABLE: bindings::ops = bindings::ops { | ||
print_int: Some(Self::print_int), | ||
print_char: Some(Self::print_char) | ||
}; | ||
|
||
pub(crate) unsafe fn build() -> *const bindings::ops { | ||
&Self::VTABLE as *const _ | ||
} | ||
} | ||
|
||
// 在定义了上面三段后就可以定义一个自己的Ops | ||
|
||
struct MyOps; | ||
|
||
impl Ops for MyOps { | ||
... | ||
} | ||
|
||
// 之后想要得到MyOps的对应的C的ops结构体只需要 | ||
|
||
let ops = OperationsVtable<MyOps>::build(); | ||
|
||
``` | ||
|
||
|
||
## 现在的成果和收获 | ||
|
||
对Linux的C代码中的serial_core.h中的uart_driver和uart_port以及console做了比较完整的抽象。 | ||
|
||
我也能够比较好的使用Rust中的Pin使数据固定在内存上的某个地方了。 |