Linux驱动程序设计
linux驱动开发(一)

linux驱动开发(⼀)1:驱动开发环境要进⾏linux驱动开发我们⾸先要有linux内核的源码树,并且这个linux内核的源码树要和开发板中的内核源码树要⼀直;⽐如说我们开发板中⽤的是linux kernel内核版本为2.6.35.7,在我们ubuntu虚拟机上必须要有同样版本的源码树,我们再编译好驱动的的时候,使⽤modinfo XXX命令会打印出⼀个版本号,这个版本号是与使⽤的源码树版本有关,如果开发板中源码树中版本与modinfo的版本信息不⼀致使⽆法安装驱动的;我们开发板必须设置好nfs挂载;这些在根⽂件系统⼀章有详细的介绍;2:开发驱动常⽤的⼏个命令lsmod :list moduel 把我们机器上所有的驱动打印出来,insmod:安装驱动rmmod:删除驱动modinfo:打印驱动信息3:写linux驱动⽂件和裸机程序有很⼤的不同,虽然都是操作硬件设备,但是由于写裸机程序的时候是我们直接写代码操作硬件设备,这只有⼀个层次;⽽我们写驱动程序⾸先要让linux内核通过⼀定的接⼝对接,并且要在linux内核注册,应⽤程序还要通过内核跟应⽤程序的接⼝相关api来对接;4:驱动的编译模式是固定的,以后编译驱动的就是就按照这个模式来套即可,下⾯我们来分下⼀下驱动的编译规则:#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build# 开发板的linux内核的源码树⽬录KERN_DIR = /root/driver/kernelobj-m += module_test.oall:make -C $(KERN_DIR) M=`pwd` modulescp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: cleanclean:make -C $(KERN_DIR) M=`pwd` modules cleanmake -C $(KERN_DIR) M=`PWD` modules这句话代码的作⽤就是到 KERN_DIR这个⽂件夹中 make modules把当前⽬录赋值给M,M作为参数传到主⽬录的Makefile中,实际上是主⽬录的makefile中有⽬标modules,下⾯有⼀定的规则来编译驱动;#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build我们在ubuntu中编译内核的时候⽤这两句代码,因为在ubuntu中为我们保留了⼀份linux内核的源码树,我们编译的时候直接调⽤那个源码树的主Makefile以及⼀些头⽂件、内核函数等;了解规则以后,我们设置好KERN_DIR、obj-m这两个变量以后直接make就可以了;经过编译会得到下⾯⼀些⽂件:下⾯我们可以使⽤lsmod命令来看⼀下我们ubuntu机器现有的⼀些驱动可以看到有很多的驱动,下⾯我们使⽤insmod XXX命令来安装驱动,在使⽤lsmod命令看⼀下实验现象可以看到我们刚才安装的驱动放在了第⼀个位置;使⽤modinfo来打印⼀下驱动信息modinfo xxx.ko这⾥注意vermagic 这个的1.8.0-41是你⽤的linux内核源码树的版本号,只有这个编译的版本号与运⾏的linux内核版本⼀致的时候,驱动程序才会被安装注意license:GPL linux内核开元项⽬的许可证⼀般都是GPL这⾥尽量设置为GPL,否则有些情况下会出现错误;下⾯使⽤rmmod xxx删除驱动;-------------------------------------------------------------------------------------5:下⾯我们分析⼀下驱动。
设计Linux系统网络设备驱动程序

1 n u x / i in c 1U d e / 1 n u x / i
net vi e. 。 de c h
■
网络物 理 设备 媒介
2 初 始 化
网 络 设 备 的 初 始 化 主 要 是 由
设 备 媒 介 层
图 1 iu L n x网络驱 动程 序体 系结 构 图
开藏豸 统世界奠 ; ≯
维普资讯
开 放 系统 世界
一 [ i u ] Ln x 技 术 开 发 一
・
序 会 很 轻 松 , 并 且 能 够 形 成 固 定 的 模
备 接 口 f r (hi_ v = 0: t s de < o t s de hi _ v
数 h — a rd
s art x m  ̄ t _ t,
d vc e i e数 据 结 构 中 的 i i n t函 数 指 针 所 指 的 初 始 化 函 数 来 完 成 的 。当 内 核 启 动 或 加 载 网 络 驱 动 模 块 的 时 候 ,就 会 调 用 初 始 化 过 程 。这 个 过 程 将 首 先 检 测 网 络 物
计 Li nux 防 火 墙 和 网 络 入 侵 检 测 系 统 时, 可 以在 网络 驱 动程 序 的基 础上 拦 截 网 络 数 据 包 ,继 而 对 其 进 行 分 析 。由 于 Li nux是 开 放 源 代 码 的 ,所 以 给 我 们 提 供 了一个分析 和改造 网络驱r c e i e tu td vc
网络 设 备接 口层
ne _de c t vie)
的 详 细 内容 , 请 参 看 /
数 据包 发 送 h r tr mi a d sa tx t O
中 断处 理 ( 据 包 数 接收)
Linux2.6内核中的Framebuffer驱动程序设计

Linux2.6内核中的Framebuffer驱动程序设计虽然Framebuffer驱动技术在PC上已经逐渐被淘汰,但是在嵌入式等考虑成本的平台下,由于其使用简单,成本低廉的优势,使用相当的广泛。
在Linux2.4和Linux2.6内核之间,Framebuffer的框架结构发生了很大的变化,网络上很多介绍Framebuffer的文档都是基于2.4内核下的,这就使得在2.6内核开发Framebuffer驱动增加了难度,本文介绍2.6内核下如何编写Framebuffer驱动,以适应最新版本的Linux。
Framebuffer是出现在Linux 2.2.xx及以后版本内核当中的一种驱动程序接口,这种接口将显示设备抽象为帧缓冲区设备。
帧缓冲区为图像硬件设备提供了一种抽象化处理,它代表了一些视频硬件设备,允许应用软件通过定义明确的界面来访问图像硬件设备。
这样软件无须了解任何涉及硬件底层驱动的东西(如硬件寄存器)。
它允许上层应用程序在图形模式下直接对显示缓冲区进行读写和I/O控制等操作。
通过专门的设备节点可对该设备进行访问,如/dev/fb*。
用户可以将它看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以进行读写操作,而读写操作可以反映到LCD。
二、 Framebuffer驱动的主要数据结构fb_fix_screeninfo记录了帧缓冲设备和指定显示模式的固件信息。
它包含了屏幕缓冲区的物理地址和长度等信息。
fb_var_screeninfo记录了帧缓冲设备和指定显示模式的可修改信息。
它包括显示屏幕的分辨率、每个像素的比特数和一些时序变量。
其中变量 xres定义了屏幕一行所占的像素数,yres定义了屏幕一列所占的像素数。
fb_info info是Linux为帧缓冲设备定义的驱动层接口。
它不仅包含了底层函数,而且还有记录设备状态的数据。
每个帧缓冲设备都与一个fb_info结构相对应。
其中成员变量包含fb_fix_screeninfo、fb_var_screeninfo这两个数据结构,另外还有Framebuffer的回调函数。
一、如何编写LinuxPCI驱动程序

⼀、如何编写LinuxPCI驱动程序PCI的世界是⼴阔的,充满了(⼤部分令⼈不快的)惊喜。
由于每个CPU体系结构实现不同的芯⽚集,并且PCI设备有不同的需求(“特性”),因此Linux内核中的PCI⽀持并不像⼈们希望的那么简单。
这篇简短的⽂章介绍⽤于PCI设备驱动程序的Linux APIs。
1.1 PCI驱动程序结构PCI驱动程序通过pci_register_driver()在系统中"发现"PCI设备。
事实上,恰恰相反。
当PCI通⽤代码发现⼀个新设备时,具有匹配“描述”的驱动程序将被通知。
详情如下。
pci_register_driver()将设备的⼤部分探测留给PCI层,并⽀持在线插⼊/删除设备[因此在单个驱动程序中⽀持热插拔PCI、CardBus和Express-Card]。
pci_register_driver()调⽤需要传⼊⼀个函数指针表,从⽽指⽰驱动程序的更⾼⼀级结构体。
⼀旦驱动程序知道了⼀个PCI设备并获得了所有权,驱动程序通常需要执⾏以下初始化:启⽤设备请求MMIO / IOP资源设置DMA掩码⼤⼩(⽤于⼀致性DMA和流式DMA)分配和初始化共享控制数据(pci_allocate_coherent())访问设备配置空间(如果需要)注册IRQ处理程序(request_irq())初始化non-PCI(即LAN/SCSI/等芯⽚部分)启⽤DMA /处理引擎当使⽤设备完成时,可能需要卸载模块,驱动程序需要采取以下步骤:禁⽌设备产⽣irq释放IRQ (free_irq())停⽌所有DMA活动释放DMA缓冲区(包括流式DMA和⼀致性DMA)从其他⼦系统注销(例如scsi或netdev)释放MMIO / IOP资源禁⽤该设备下⾯⼏节将介绍这些主题中的⼤部分。
其余部分请查看LDD3或<linux/pci.h>。
如果PCI⼦系统没有配置(没有设置CONFIG_PCI),下⾯描述的⼤多数PCI函数都被定义为内联函数,要么完全空,要么只是返回⼀个适当的错误代码,以避免在驱动程序中出现⼤量ifdefs。
信号发生模块的Linux驱动程序设计

3 1 信号发生模块硬件 电路 .
#i ld ncu e< l u /f. i x s h> n
#i l d nc u e< l u /e' l . > i x l 0h n I T
i t r gs e n e i tr
—
驱动程序从 字面上可 以理 解为一类 程序 , 这类 程序的目的一般是驱 动硬件 正常工作 , 以通常 所 所
律 工 作 。 首先 介 绍 嵌 入 式 驱 动 程序 的作 用及 模 型 , 过 对信 号发 生 电路 原 理 的 介 绍 , 通 以基 于 0 A —17的 工 业 M PL 3 控 制 开发 板 为 平 台 , 细地 阐述 了基 于 嵌 入 式 L u 作 系统 的 信 号 发 生 模 块 的 驱 动程 序 设 计 及 其 测 试 程 序 的 详 i x操 n
生模 块 的驱 动 程 序设 计 。
2 L n x下 设 备 驱 动 程序 iu
动属于字符 型设备 驱 动程 序。在对 字符 设 备发 出
读、 写请求 时 , 际 的硬件 10一般 就 紧接 着发 生 实 1 '
了。应用程序可以用与存取文件相 同的系统调用来
打开、 读写及关闭 。字符设 备驱 动程序一 般要包 含 oe 、l era 、re pn c s、 dw i 等几个系统调用 。 o e t
编写 , 实现 了设计 中所需的 ±1 并 0V方波信号的输 出, 为以后更深一步地学 习驱动开发 奠定了基础 。
关键 词 : 嵌 入 式 ; 符 设 备驱 动 ; 号发 生模 块 ; 波 字 信 方 中 图分 类 号 : P 9 文 献标 识码 : 文章 编 号 :10 — 3 (0 0 1 - 8 - T3 A 003 2 2 1 ) 10 80 9 0 4
嵌入式linux按键驱动程序的设计

数 结 有 个, 一 据 构 三 第 个fileo peration 结
构, 这里终端设备为他提供的值是tty_fops 第一个tty_driver , 终端设备为他提供的值
add_timer(&scant imer),/ / 这里启 动时
间队例 。
一层是键盘扫描码 ,这是根据键盘的不同而 是console ,另一个是 t t y l disc 数据结构。 不同的,第二层是系统扫描码,这是对键盘 比较统一的编码,第三层是 ASI C C 码, 从 控制台为他提供的值是ttyl disc_NT TY, 在一个进程需要从键盘上读取值时 , 系统扫描码到 ASI C C 码的转换,我们可以 照抄标准PS / 2 键盘的码表转换函数,从第 li n u x 首先通过控制台的读操作函数,然后 调用tty- disc的read_chan 函数从链路规则中 一层,到第二层的转换 ,我们需要自己提 J 读数据 ,如果终端 设备的缓冲区中有数据 , 供。这里由于我们只设计了8 个按键,因此 可以简单的用一个swich case 语句完成,不 则将数据回显出来 ,如果缓冲区中没有数 需要复杂的转换。值的注意的是这几函数都 据 ,则进程睡眠等待中断发生。
初始化键盘。这里要参考硬件手册,如初始 化LH7A404 的GPIOF 的第一个引脚, 通过 相关的寄存器配置把他设置为中断上升沿触 发方式,然后通过l i n u x 内核提供的函数
/ / wait- for- release () 是定义 好的每
10 秒要执行的函数,就是在这个函数中检测 GP IOF I 引脚是否断开。
是scan-code 的回 数, 须 态 译 调函 必 静 编 进
内核 。
的驱动程序, 以让我们的系统更加的简捷高 效。本文在研究了与终端相关的驱动程序 后, 给出了 在lh 7a404 下设计按键驱动程序
基于嵌入式Linux的设备驱动程序设计

# i d fM 0DULE f e n # d f eM 0DULE ei n 『
口, 它为应用程序屏蔽 了硬件 的 细节 。在应 用程 序看来 ,
硬 件设 备 只是 一个 设 备 文 件 , 用程 序 可 以 像 操作 普 通 文 应 件 一 样 对 硬 件 设 备 进 行 操 作 。设 备 驱 动 程 序 是 内核 的 一
_
s a i o f t es l e k ) t tc lf t t l e ( s
_
—
s a i sz t tc s ie
_ —
tt est
_
r a ( ed )
s a i sz tt s ie ) t tc s ie e t wrt (
性 , 使用 J 不 TAG 即 实 现 了多 片 D P软件 调 试 ; 个 调试 S 整
参 考 文 献
Ei丁 刚 . 述 软 件 开 发 中 模 拟 器 与仿 真 器 的 区别 [ B O ] l 详 E / L.
ht:/ tp / www1 t c r. n, 0 4—0 .i o c 2 0 . n 1—0 . 1
[]曹 长 江 , 双 宝 , 飚 . 于 US 2 0总线 的 TMS 2 V 5 0 5 马 詹 基 B. 30 C 42 HP 自举 的实 现 . E / ] t :/ e sew r .o c , I [ B OL .ht / n w .e o l cm.n p d
[ ]陈 朝 阳 , 峥 , 胜 江 . 用 Fah实 现 D P对 多 个 程 序 有 选 4 薛 郭 利 l s S
择 的 加 载 。 t : / w.s d w . o 2 0 0 0 . h t / ww i o n c m, 0 4— 4— 1 p p
嵌入式linux驱动开发流程

三、设备的中断和轮询处理
对于不支持中断的设备,读写时需要轮询设备状态,以及是否需要继续进行数据传输。例如,打印机。如果设备支持中断,则可按照中断方式进行。
struct file_operations Key7279_fops =
{
.open = Key7279_Open,
.ioctl = Key7279_Ioctl,
.release = Key7279_Close,
.read = Key7279_Read,
};
1、 设备的打开和释放
模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。通过request_irq()函数来注册中断,free_irq()函数来释放。
四、驱动程序的测试
对驱动程序的调试可以通过打印的方式来进行,就是通过在驱动程序中添加printk()打印函数,来跟踪驱动程序的执行过程,以此来判断问题。
◇ 设备的打开和释放。
ห้องสมุดไป่ตู้◇ 设备的读写操作。
◇ 设备的控制操作。
◇ 设备的中断和轮询处理。
Linux主要将设备分为三类:字符设备、块设备和网络设备。字符设备是指发送和接收数据以字符的形式进行,没有缓冲区的设备;块设备是指发送和接收数据以整个数据缓冲区的形式进行的设备;网络设备是指网络设备访问的BSD socket 接口。下面以字符设备为例,写出其驱动编写框架:
二、 构造file_operations结构中要用到的各个成员函数
Linux操作系统将所有的设备都看成文件,以操作文件的方式访问设备。应用程序不能直接操作硬件,使用统一的接口函数调用硬件驱动程序,这组接口被成为系统调用。每个系统调用中都有一个与之对应的函数(open、release、read、write、ioctl等),在字符驱动程序中,这些函数集合在一个file_operations类型的数据结构中。以一个键盘驱动程序为例:
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
4-2设备的分类和特点
块设备特点
块设备通过位于 /dev 目录的文件系统结点来存取
库
称为系统调用,执行
其他库函数的实现
swi指令进入内核
内核
系统调用的异常处理
其他功能
驱动程序 open read write ioctl ……
物理设备控制器 物理设备
4层软件关系说明
(1)应用程序使用库函数提供的open函数打开设备文 件
(2)库根据open函数传入的参数执行“swi”指令,引起 CPU异常,进入内核 (3)内核的异常处理函数根据这些参数找到相应的驱动 程序,返回一个文件描述符给库,进而返回给应用程序
内核态与用户态切换:可通过软件中断实 现
内核态和用户态
驱动程序作为系统内核的一部分,其工作在内核态,而 应用程序工作在用户态,即不能直接通过指针,把用户空 间的数据地址传递给内核(MMU映射地址不一样)。
需要经过转换,把用户态“看到的空间”转换成内核态 可访问的地址。Linux系统提供了一系列方便的函数实现这 种转换,如:__get_user、__put_user、 •__copy_from_user、__copy_to_user
вторник, 19 мая 2020 г.
内核态和用户态
大多数OS(包括Linux)把内核和运行在其上的应用程序 分为两个层次管理,即用户态和内核态
内核态有较高的权限,可以控制处理器内存的映射和分 配方式等等————对应于ARM的svc模式
用户态:只能运行系统上的应用程序————对应于 ARM的usr模式
вторник, 19 мая 2020 г.
本章目标
了解Linux设备驱动程序的基础知识 掌握Linux驱动模块的构造和装载方法
вторник, 19 мая 2020 г.
本章结构
Linux驱动程序概述
设备驱动程序简介
设备 设备驱动的Hello World模块 内核驱动模块和应用程序对比
网卡 软件设备:环回接口(loopback)
内核调用一套和数据包传输相关的函数
вторник, 19 мая 2020 г.
4-3 构造和运行模块
驱动程序加入内核的方法
把所有需要的功能都编译到内核中 生成的内核镜像(Image)文件会很大 如果我们要在现有的内核中新增或删除功 能,将不得不重新编译和装载内核。
вторник, 19 мая 2020 г.
课程目标
掌握嵌入式Linux设备驱动程序的基本原 理、架构和设计方法
字符设备驱动 块设备驱动 网络设备驱动
掌握Linux设备驱动开发中常用的机制和 内核资源
中断顶/底半部处理 内核定时器和延时操作 并发控制在内核中的应用 内存管理和分配 阻塞型I/O和非阻塞型I/O
教学内容
第一章 嵌入式系统概述 第二章 学习板硬件及开发环境的建立 第三章 构建嵌入式Linux系统 第四章 嵌入式Linux设备驱动 第五章 嵌入式Linux串口和网络编程 第六章 嵌入式Linux图形编程
вторник, 19 мая 2020 г.
第四章 嵌入式Linux驱动程序
4.1 嵌入式Linux设备驱动简介 4.2 设备的分类及特点 4.3 构造和运行驱动程序模块
вторник, 19 мая 2020 г.
4-3 驱动程序加入内核的方法
Linux提供了机制被称为模块(Module)的 机制
提供了对许多模块支持, 包括但不限于, 设备驱动
每个模块由目标代码组成( 没有连接成一个完整可执行程 序)
insmod 将模块动态加载到正在运行内核 rmmod 程序移除模块
编译和装载驱动模块
вторник, 19 мая 2020 г.
驱动程序的作用
操作系统
应
write
用
ioctl
程
序
read
驱 动 程 序
硬 件 设 备
ioctl
为什么要学嵌入式Linux驱动程序开发?
高需求
内核代码的大部分 新芯片、新设备
高门槛
需要具有硬件知识 需要了解内核基础知识 需要了解内核中的并发控制和同步 复杂的软件结构框架
块设备和字符设备的区别仅仅在于内核内部管理数据的 方式
块设备有专门的接口,块设备的接口必须支持挂装 (mount)文件系统。
应用程序一般通过文件系统来访问块设备上的内容
вторник, 19 мая 2020 г.
4-2设备的分类和特点
网络设备特点
通过单独的网络接口来访问
任何一个网络事务都通过一个网络接口访问,即一个能 够和其他主机交换数据的设备。
高回报
вторник, 19 мая 2020 г.
4-1设备驱动程序简介
驱动程序的特点 操控硬件,是应用程序和硬件设备之间的一
个接口
隐藏硬件细节,提高应用软件的可移植性 提供安全性 开发模式
内核态驱动 用户态驱动
提供机制,而不是提供策略
机制:驱动程序能实现什么功能 策略:用户如何使用这些功能
4-2设备的分类和特点
设备分类
字符设备(char device) 块设备(block device) 网络设备(network device)
вторник, 19 мая 2020 г.
4-2设备的分类和特点
字符设备特点
像字节流一样来存取的设备( 如同文件 ) 通过/dev下的文件系统结点来访问。 通常至少需要实现 open, close, read, 和 write 等系
统调用 只能顺序访问数据通道,不能前后移动访问指针。
特例:比如framebuffer设备就是这样的设备,应 用程序可以使用mmap或lseek访问图像的各个区 域
вторник, 19 мая 2020 г.
Linux内核结构
Linux内核结构
应用程序、库、内核、驱动程序的关系
应用程序
open read write ioctl ……调用其他库函数