390 likes | 551 Views
嵌入式设备驱动. 陈文智 wzchen@cad.zju.edu.cn 浙江大学计算机学院 2005 年 4 月. 提纲. 1 、 Linux 下设备驱动程序简介 2 、设备驱动程序结构 3 、设备驱动实验 实验一:编写一个简单的驱动程序 实验二:设计和实现一个 KED&LED 驱动程序 实验三:静态编译驱动程序,连接到内核 实验四:使用中断方式的驱动程序设计. 1 、 Linux 下设备驱动程序简介. 系统调用是操作系统内核和应用程序之间的接口 设备驱动程序是操作系统内核和机器硬件之间的接口
E N D
嵌入式设备驱动 陈文智 wzchen@cad.zju.edu.cn 浙江大学计算机学院 2005年4月
提纲 • 1、Linux下设备驱动程序简介 • 2、设备驱动程序结构 • 3、设备驱动实验 • 实验一:编写一个简单的驱动程序 • 实验二:设计和实现一个KED&LED驱动程序 • 实验三:静态编译驱动程序,连接到内核 • 实验四:使用中断方式的驱动程序设计
1、Linux下设备驱动程序简介 • 系统调用是操作系统内核和应用程序之间的接口 • 设备驱动程序是操作系统内核和机器硬件之间的接口 • Linux设备驱动的特点是可以以模块的形式加载各种设备类型 • 因为嵌入式设备往往具有大量的独有外设,开发人员需要把很多精力放在设备驱动方面
1.1 Linux设备的分类 • 字符设备 • 以字节为单位逐个进行I/O操作 • 字符设备中的缓存是可有可无 • 不支持随机访问 • 如串口设备/dev/cua0和/dev/cua1 • 块设备 • 块设备的存取是通过buffer、cache来进行 • 可以进行随机访问 • 例如IDE硬盘设备/dev/hda • 可以支持可安装文件系统 • 网络设备 • 通过BSD套接口访问
1.2 Linux设备文件 • Linux抽象了对硬件的处理,所有的硬件设备都可以作为普通文件一样来看待 • 可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作 • 对用户来说,设备文件与普通文件并无区别
1.3 主设备号和次设备号 • 主设备号:标识该设备的种类,也标识了该设备所使用的驱动程序 • 次设备号:标识使用同一设备驱动程序的不同硬件设备 • 所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号可以从/proc/devices文件中得到
1.4 Linux设备驱动代码的分布 • ~/block • ~/char • ~/cdrom • ~/pci • ~/scsi • ~/net • ~/sound 注:IDE接口的CD驱动位于drivers/block/ide-cd.c中,而SCSI CD驱动位于drivers/scsi/scsi.c中
1.5 Linux设备驱动程序的特点 • 设备驱动是内核的一部分,影响内核稳定 • 为内核或其从属子系统提供一个标准接口 • 使用标准的内核服务如内存分配、中断和等待队列等 • 大多数Linux设备驱动可以动态可加载 • Linux设备驱动程序可配置 • 驱动程序维护其控制的设备,该设备即使不存在也不影响系统的运行,此时设备驱动只是占用少量系统内存,不会对系统造成什么危害
2、设备驱动程序结构 • Linux的设备驱动程序与外界的接口可以分成三部分: • 与操作系统内核的接口 • 与系统引导的接口 • 与设备的接口 • Linux设备驱动程序的代码结构大致可以分为如下几个部分: • 驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。
2.1驱动程序的注册与注销 • 注册 • register_chrdev( );//在~fs/devices.c • register_blkdev( );//在~fs/block_dev.c • 注销 • unregister_chrdev( ) • unregister_blkdev( )
Linux内核将通过file_operations结构访问驱动程序提供的函数 • 字符设备的读写直接使用函数read( )和write( ) • 块设备需要调用函数block_read( )和block_write( ) • ioctl( )的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析
2.3 设备的轮询和中断处理 • 轮询 • 内核定期对设备的状态进行查询 • 消耗不少的内核资源 • 如果设备驱动被连接进入内核,这时使用轮询方式将会带来灾难性的后果 • 中断 • 设备驱动向内核注册其使用的中断 • 内核负责把硬件产生的中断传递给相应的设备驱动 • 设备驱动在其中断处理过程中做得越少越好
问题: • 如何查看设备驱动所对应的中断号及类型 ?
3、设备驱动试验 • 实验一:编写一个简单的驱动程序 • 实验二:设计和实现一个KED&LED实验 • 实验三:动态加载和静态编译驱动到内核 • 实验四:使用中断方式的驱动程序设计
实验一:编写简单的驱动程序(1) • 1) 任务: 在XSBase开发板上编写一个简单的字符设备驱动程序。该字符设备具备4个基本操作:xsbase_open()、xsbase_write()、 xsbase_read() 、xsbase_release(),实现向这个新建的字符设备先写入一些数据,然后再从这个设备中读取这些数据。
实验一:编写简单的驱动程序(2) • 2)主要数据结构和全局变量 • 系统~fs/devices.c的struc device_struct结构 • 创建一个xsbase.c文件,其中包含一些必要的头文件、宏和全局变量
实验一:编写简单的驱动程序(3) • 主要接口函数 • file_operations{ }结构的实例 注:这种结构的声明方法是一种标记化格式声明,便于移植。
实验一:编写简单的驱动程序(4) • xsbase_open()和xsbase_release()
实验一:编写简单的驱动程序(5) • xsbase_read()
实验一:编写简单的驱动程序(6) • xsbase_write()
实验一:编写简单的驱动程序(7) • 3)模块加载:是两种将驱动加入内核的方式之一 • 编写init_module() …… res = register_chrdev(0, "xsbase", &chr_fops); …… if(xsbase_major == 0) xsbase_major = res; • 编写cleanup_module() …… unregister_chrdev(xsbase_major, "xsbase"); ……
实验一:编写简单的驱动程序(8) • 4)驱动安装过程 • 新建xsbase.c文件,实现主要数据结构和函数接口。 • 编译生成xsbase.o • 把目标文件加载到内核 • 在Minicom中使用ZMODEM协议发送文件 • 执行
实验一:编写简单的驱动程序(9) • 创建一个设备文件 • 测试 • OVER
实验二:编写KED&LED驱动(1) • 在这个实验中,通过设计和实现一个KED&LED驱动程序,来控制目标板上的一组LED灯,在安装完驱动后,运行测试程序,就能点亮LED灯。
实验三:静态编译驱动到内核(1) • 在确定自己代码位置的前提下,建立自己的源码目录、文件、Makefile、Config.in等。 • 修改上层的config.in文件,把自己的驱动加入到内核配置系统中。 • 修改上层Makefile,把自己的程序加入到内核编译系统中。 • 确保自己的初始化函数被调用。 • drivers/char/mem.c中的chr_dev_init() 或drivers/block/ll_rw_blk.c中的blk_dev_init() • 用宏module_init来申明你的初始化函数
实验三:静态编译驱动到内核(2) • 驱动位置和目录结构
实验三:静态编译驱动到内核(3) • 修改配置文件 • 1)drivers/xsbase/Config.in
实验三:静态编译驱动到内核(4) • 2) arch/arm/config.in • 编写Makefile • 1)drivers/xsbase/Makefile
实验三:静态编译驱动到内核(5) • 2)drivers/Makefile • 3)Makefile
实验四:中断方式的驱动程序(1) • 注册中断处理程序 • 什么叫中断信号线(IRQ号)? • 何时请求IRQ号更合适? • 初始化时 • 设备第一次打开时
实验四:中断方式的驱动程序(2) • 编写中断处理程序
实验四:中断方式的驱动程序(3) • 中断处理机制的实现
实验四:中断方式的驱动程序(4) • 中断控制 这些函数更新可编程中断控制器(PIC)中指定中断的掩码,因而,这样就可以在所有的处理器上禁止或者启动 IRQ。对这些函数的调用是可以嵌套的——如果 disable_irq() 被成功调用两次,在 IRQ 真正重新打开之前,需要执行两次 enable_irq() 调用。
驱动设计中涉及的一些具体问题 • 用户空间和内核空间 • /proc文件系统