参考文章
VSCode SSH 连接远程ubuntu Linux 主机
ubuntu 20.04 qemu linux6.0.1 开发环境搭建
ubuntu 20.04 qemu linux6.0.1 制作ext4根文件系统
嵌入式Linux 开发经验:platform_driver_register 的使用方法
嵌入式Linux 开发经验:注册一个 misc 设备
- 通过以上的文章,应该可以搭建一个 基于 qemu 的 Linux 设备驱动开发验证平台,开发方法是 VS Code 远程连接 ubuntu 20.04,Linux 内核 在 ubuntu 主机上。
测试环境搭建
-
ubuntu 20.04
-
VMware Workstation Pro 16
-
基于qemu(模拟器),vexpress-a9 平台
-
Linux 6.0.10 (当前最新版本)
-
编写一个简单的用户态应用程序,打开与关闭 Linux 内核注册的misc 驱动设备,掌握misc 设备使用方法:打开与关闭的方法
用户态应用程序
-
Linux 系统分为内核态与用户态,驱动一般写在内核态,用户态想访问内核态注册的驱动设备,需要通过 文件操作 API,如 open、close、read、write、ioctl 等
-
Linux misc 设备,一般不提供 read 与 write,而是提供 open 、close、 ioctl 等接口,open 用于打开设备, close 用于关闭设备, 而 ioctl 虽然是一个 API,但是可以通过 命令,实现多个操作
-
假如 内核态的驱动设备 是个【空调】,开机后内核注册了这个设备,就像是安装在了家里,如果不【上电】,空调就无法工作,用户需要【遥控器】去操作这个【空调】,用户程序的操作就类似于【遥控器】的操作,不是直接打开【空调】盖子上电,通过【遥控器】的命令就可以实现多个操作
编写用户程序
-
用户程序不需要放在 Linux 内核目录下编译,可以单独放在其他的目录,上篇注册了一个 misc 的设备,本篇写个简单的 用户态程序,打开或者关闭这个 misc 设备
-
编写 led_control.c,位置可以随便放,我放在
/home/zhangsz/linux/apps/led_control
目下 -
当前只有打开与关闭的操作,后续再实现 ioctl 控制操作
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define LED_CONTROL_DEVICE_NAME "/dev/led_misc"
int led_dev_fd = -1;
/* 打开操作 API,这个 API其实比较的底层,也就是用户高层的应用,可以调用这个 API */
int led_dev_init(void)
{
int fd;
fd = open(LED_CONTROL_DEVICE_NAME, O_RDWR);
if (fd < 0)
{
printf("%s : open device error\n", __func__);
return -1;
}
led_dev_fd = fd;
printf("%s : ok\n", __func__);
return 0;
}
/* 关闭操作 API,这个 API其实比较的底层,也就是用户高层的应用,可以调用这个 API */
int led_dev_deinit(void)
{
if (close(led_dev_fd) != 0)
{
printf("%s : error\n", __func__);
return -1;
}
printf("%s : ok\n", __func__);
return 0;
}
/* API 调用的测试,实际用户程序当然不只是这么简单 */
int main(int argc, char **argv)
{
printf("%s : enter\n", __func__);
led_dev_init();
led_dev_deinit();
printf("%s : exit\n", __func__);
return 0;
}
编译方法
-
可以直接使用命令行编译,也可以编写Makefile,因为当前的用户程序就是一个文件,所以写个简单的Makefile编译,后面就不用一直输入较长的gcc 命令来编译了
-
新建 Maekfile 文件,放在
/home/zhangsz/linux/apps/led_control
目下
all:
arm-linux-gnueabihf-gcc led_control.c -o led_control
clean:
rm -rf *.o led_control
-
Makefile 注意缩进使用的 TAB(制表符),而不是空格
-
然后在Linux shell 中 输入 make 就可以编译了
zhangsz@zhangsz:~/linux/apps/led_control$ make
arm-linux-gnueabihf-gcc led_control.c -o led_control
- 这里生成了可执行的文件
led_control
,这个文件需要放在 Linux 根文件系统中,在 shell 手动执行这个文件才能工作,当然后期可以写个自动调用的shell 脚步放在板子上自动调用执行
运行用户程序
-
这里是 基于 qemu 的 ext4 的 镜像,只要把 镜像文件挂载到某个目录下,然后把
led_control
复制进去,然后 umount 后,这个文件就加入到根文件系统镜像里面了 -
相关操作
/* apps led_control 路径 */
zhangsz@zhangsz:~/linux/apps/led_control$ ls
led_control led_control.c Makefile
zhangsz@zhangsz:~/linux/apps/led_control$ cd ../../rootfs/
zhangsz@zhangsz:~/linux/rootfs$ ls
1130 boot_qemu.sh ext4_rootfs make_rootfs.sh rootfs.ext4.img rootfs_mnt vexpress-v2p-ca9.dtb zImage
/* ext4 根文件系统镜像文件,使用 mount 挂载到一个目录 */
zhangsz@zhangsz:~/linux/rootfs$ sudo mount rootfs.ext4.img rootfs_mnt/
[sudo] password for zhangsz:
/* led_control 复制到 根文件系统镜像文件挂载的目录内 */
zhangsz@zhangsz:~/linux/rootfs$ sudo cp ../apps/led_control/led_control rootfs_mnt/home/root/
/* umount 后,文件就复制进根文件系统镜像文件中了 */
zhangsz@zhangsz:~/linux/rootfs$ sudo umount rootfs_mnt
- 运行qemu :
./boot_qemu.sh rootfs.ext4.img
,这里使用的 shell 脚本,具体命令行为
qemu-system-arm -M vexpress-a9 -m 512M -dtb vexpress-v2p-ca9.dtb -kernel zImage -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd rootfs.ext4.img
- 注意启动 qemu 前, 设备树 dbt 文件
vexpress-v2p-ca9.dtb
与内核文件zImage
都在同一个目录下
zhangsz@zhangsz:~/linux/rootfs$ ls
1130 boot_qemu.sh ext4_rootfs make_rootfs.sh rootfs.ext4.img rootfs_mnt vexpress-v2p-ca9.dtb zImage
-
进入qemu Linux shell,通过
ls sys/class/misc/ -la
可以查看当前注册的 misc 设备,这里是led_misc
-
进入
/home/root/
,运行./led_control
,可以看到 设备 打开 与关闭的信息,说明 用户应用程序成功的打开与关闭了 内核驱动 注册的led_misc
设备
小结
-
本篇记录Linux 用户态 应用程序的编写编译方法,通过标准文件接口 open close 对 内核注册的 misc 设备进行 打开与关闭。
-
misc 设备最有用的一般是 ioctl 命令,用户可以通过自定义实现多个 基于ioctl 的 cmd 命令,实现各种各样的操作。