Linux设备驱动开发详解讲座
- 格式:pdf
- 大小:1.11 MB
- 文档页数:34
一、设备驱动程序概述自Linux在中国发展以来,得到了许多公司的青睐。
在国内的玩家也越来越多了,但目前还是停留在玩的水平上,很少有玩家对Linux的系统进行研究。
因为它的开放,我们可以随时拿来“把玩”。
这也是Linux一个无可比拟的优势,这样我们可以修改后再加入到里面。
但很少有专门的书籍讲到Linux驱动程序的开发,像上海这样的大城市也很少有讲Linux驱动开发的资料,唉,谁让这个是人家的东西呢,我们还是得跟着人家跑。
我现在讲的这些驱动开发的细节,并不特定哪个版本的内核,这只是大体的思路与步骤。
因为大家都知道Linux 2.6.x 与Linux 2.4.x是有不少改动的。
所以,具体的大家可以去参考Linux Device Driver 2.4 和Linux Device Driver 2.6这几本书。
这是我们学习开发驱动必不可少的东西。
好了,下面就开始学习吧。
根据设备的行为,我们可以把设备分为字符设备和块设备,还有网络设备。
字符设备是以字节为单位进行顺序读写,数据缓冲系统对它们的访问不提供缓存。
而块设备则是允许随机访问与读写,每次读写的数据量都是数据块长度的整数倍,并且访问还会经过缓冲区缓存系统才能实现。
与Unix版本不同的是:Linux的内核允许不是数据块长度整数倍的数据量被读取,用官方的语言就是:但这种不同只是纯粹学术方面的东西。
大多数设备驱动程序都要通过文件系统来进行访问的,但网络设备是不同的。
/dev子目录里都是关于设备的特殊文件,但看起来它们与普通的目录没有什么两样。
如下:$ ls -l /dev...brw-rw--- 1 root disk 22, 1 May 5 1998 hdc1crw-rw--- 1 root daemon 6 0 May 5 1998 lp0与普通文件有所不同是开头的“C” 和“B”,即char 和block的意思,即字符设备和块设备。
再后面的“22,1” 和“6,0”即设备的主设备号和次设备号,主设备号表明它是哪一种设备,这与你在Windows里添加硬件时看到的那些是一个意思。
linux设备驱动(27)usb驱动-热插拔详解1 热插拔的基本概念1.1 usb热插拔的硬件原理在USB集线器(hub)的每个下游端⼝的D+和D-上,分别接了⼀个15K欧姆的下拉电阻到地。
这样,在集线器的端⼝悬空时,就被这两个下拉电阻拉到了低电平。
⽽在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。
对于全速和⾼速设备,上拉电阻是接在D+上;⽽低速设备则是上拉电阻接在D-上。
这样,当设备插⼊到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的⼀条拉⾼了。
集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上⼀层的集线器报告给USB主控制器),这样就检测到设备的插⼊了。
USB⾼速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到⾼速模式的。
在⾼速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
1.2 热插拔的概念热插拔(hot-plugging或Hot Swap)即带电插拔,热插拔功能就是允许⽤户在不关闭系统,不切断电源的情况下取出和更换损坏的硬盘、电源或板卡等部件,从⽽提⾼了系统对灾难的及时恢复能⼒、扩展性和灵活性等,例如⼀些⾯向⾼端应⽤的磁盘镜像系统都可以提供磁盘的热插拔功能。
具体⽤学术的说法就是:热替换(Hot replacement)、热添加(hot expansion)和热升级(hot upgrade)1.3 热插拔的优点在系统开机情况下将损坏的模块移除,还可以在开机情况下做更新或扩容⽽不影响系统操作。
由于热插拔零件的可靠度提升,还可以将它们⽤做断电器,⽽且因为热插拔能够⾃动恢复,有很多热插拔芯⽚为系统提供线路供电情况的信号,以便系统做故障分析,因此减少了成本。
2 热插拔的实现2.1 Linux下USB HUB的驱动的实现和分析:在系统初始化的时候在usb_init函数中调⽤usb_hub_init函数,就进⼊了hub的初始化。
LINUX设备驱动开发详解概述LINUX设备驱动开发是一项非常重要的任务,它使得硬件设备能够与操作系统进行有效地交互。
本文将详细介绍LINUX设备驱动开发的基本概念、流程和常用工具,帮助读者了解设备驱动开发的要点和技巧。
设备驱动的基本概念设备驱动是连接硬件设备和操作系统的桥梁,它负责处理硬件设备的输入和输出,并提供相应的接口供操作系统调用。
设备驱动一般由设备驱动程序和设备配置信息组成。
设备驱动程序是编写解决设备驱动的代码,它负责完成设备初始化、IO操作、中断处理、设备状态管理等任务。
设备驱动程序一般由C语言编写,使用Linux内核提供的API函数进行开发。
设备配置信息是定义硬件设备的相关参数和寄存器配置的文件,它告诉操作系统如何与硬件设备进行交互。
设备配置信息一般以设备树或者直接编码在设备驱动程序中。
设备驱动的开发流程设备驱动的开发流程包括设备初始化、设备注册、设备操作函数编写和设备驱动注册等几个主要步骤。
下面将详细介绍这些步骤。
设备初始化设备初始化是设备驱动开发的第一步,它包括硬件初始化和内存分配两个主要任务。
硬件初始化是对硬件设备进行基本的初始化工作,包括寄存器配置、中断初始化等。
通过操作设备的寄存器,将设备设置为所需的状态。
内存分配是为设备驱动程序分配内存空间以便于执行。
在设备初始化阶段,通常需要为设备驱动程序分配一块连续的物理内存空间。
设备注册设备注册是将设备驱动程序与设备对象进行关联的过程,它使得操作系统能够正确地管理设备。
设备注册包括设备号分配、设备文件创建等操作。
设备号是设备在系统中的唯一标识符,通过设备号可以找到设备对象对应的设备驱动程序。
设备号分配通常由操作系统负责,设备驱动程序通过注册函数来获取设备号。
设备文件是用户通过应用程序访问设备的接口,它是操作系统中的一个特殊文件。
设备文件的创建需要通过设备号和驱动程序的注册函数来完成。
设备操作函数编写设备操作函数是设备驱动程序的核心部分,它包括设备打开、设备关闭、读和写等操作。
Linux设备驱动开发入门本文以快捷而简单的方式讲解如何像一个内核开发者那样开发linux设备驱动源作者: Xavier Calbet版权:GNU Free Documentation License 翻译: 顾宏军()中文版权:创作共用.署名-非商业用途-保持一致知识准备要开发Linux 设备驱动,需要掌握以下知识:•C 编程 需要掌握深入一些的C 语言知识,比如,指针的使用,位处理函数,等。
•微处理器编程 需要理解微机的内部工作原理:存贮器地址,中断,等。
这些内容对一个汇编程序员应该比较熟悉。
Linux 下有好几种不同的设备。
为简单起见,本文只涉及以模块形式加载的字符设备。
使用2.6.x 的内核。
(特别是Debian Sarge 使用的2.6.8内核。
)用户空间和内核空间当你开发设备驱动时,需要理解“用户空间”和内核空间之间的区别。
1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:•内核空间 :Linux 操作系统,特别是它的内核,用一种简单而有效的方法管理机器的硬件,给用户提供一个简捷而统一的编程接口。
同样的,内核,特别是它的设备驱动程序,是连接最终用户/程序员和硬件的一坐桥或者说是接口。
任何子程序或者函数只要是内核的一部分(例如:模块,和设备驱动),那它也就是内核空间的一部分。
•用户空间. 最终用户的应用程序,像UNIX 的shell 或者其它的GUI 的程序(例如,gedit),都是用户空间的一部分。
很显然,这些应用程序需要和系统的硬件进行交互。
但是,他们不是直接进行,而是通过内核支持的函数进行。
它们的关系可以通过下图表示:图1: 应用程序驻留在用户空间, 模块和设备驱动驻留在内核空间26:27:28:29:30:31:32:33:34:35:36:37:38:39:40:用户空间和内核空间之间的接口函数内核在用户空间提供了很多子程序或者函数,它们允许用户应用程序员和硬件进行交互。
原子嵌入式linux驱动开发详解原子嵌入式Linux驱动开发详解:Linux操作系统一直都是工业控制、物联网、安防等领域中嵌入式设备的首选操作系统。
Linux系统的优良特性使其成为用户和开发者的首选,而Linux内核驱动则是面向嵌入式应用领域核心技术之一。
它是嵌入式设备在硬件及软件之间接口的重要组成部分。
本文将详细介绍使用原子嵌入式Linux驱动进行嵌入式设备驱动的开发,并且介绍使用原子嵌入式Linux驱动实现并行的多线程驱动。
一、嵌入式设备驱动的基本原理:所谓嵌入式设备驱动,就是处理器与外部设备之间进行数据传递的程序,将设备中的信息读取到处理器中,或将处理器中的信息发送至设备中。
嵌入式设备驱动的核心逻辑是控制输入输出模块,以完成外部信息的读取和发送任务。
在Linux系统下,设备驱动一般以内核模块存在,片上驱动是一个相对独立的模块,不妨做一番详细的介绍。
二、原子嵌入式Linux驱动的使用:原子嵌入式Linux驱动根据功能的不同划分成了两类,即原子操作和读写自旋锁。
这两类驱动的使用方法不同,且有自己的特殊应用场景。
1、原子操作:在多线程的情况下,通过锁来保证同一时间只能有一个线程操作共享资源是一种常见的方法。
原子操作则是一种替代锁的方式,在多线程操作共享资源的情况下采用原子操作方式相对于锁来说会更加高效。
原子操作是一种特殊的指令操作,执行完原子操作之后,CPU不允许其他线程读写该地址的值,因此可以避免竞争。
下面是一个使用原子操作的例子:radio_chan = atomic_read(&radio->chan);digital_chan =atomic_read(&radio->digital_chan);radio_write_register(radio, 0x0011, 2,&radio_chan);radio_write_register(radio, 0x5111, 2,&digital_chan);在上述代码中,使用了atomic_read来获得变量radio_chan和digital_chan的值,这两个变量是共享资源,这里使用原子操作来避免竞争和冲突。
嵌入式Linux设备驱动程序开发分析摘要:为了探讨嵌入式linux设备驱动程序开发,文中对其设备驱动程序完成了以下分析:linux设备驱动程序开发过程;基本组成结构;设备驱动程序的框架。
关键词:嵌入式;linux设备;驱动程序;开发过程中图分类号:tp311.521 设备驱动程序1.1 linux设备驱动程序开发过程linux操作系统的主要设备是块设备、字符设备和网络设备这三类类型的文。
字符设备能够保证在文件存取时减少缓存垃圾,这样一来就能使字符设备能够驱动程序能够像访问文件一样的字符设备以此来负责实现这些行为,并实现操作。
块设备可以看作是类似磁盘这样的文件系统的宿主。
同时能被linux允许一次传输的字节数目不限,在读取设备时也能像读取字符设备那样并且能使两者的读取数方式是一致。
而网络设备异于其他两者,因为其设备面向的上一层是一个网络协议层,要想实现数据访问就必须得需要通过bsd套接口。
但实际上,无论所有嵌入式linux设备的驱动程序有多少不同,都会有一些共性,所以在开发过程中,能够实现任何类型的驱动程序通用化,这些特性举例如下:(1)读/写。
输入和输出是几乎所有设备都支持的两种基本操作,并由各个驱动程序自身来完成。
接口是由系统规定好并实行读/写操作的,这样一来就能直接由驱动程序来实践并完成具体的操作和功能。
一旦当驱动程序逐渐初始化的过程中,那么则需要注册读/写函数到操作系统的接口中。
(2)中断。
作为计算机中的一个非常重要的功能,中断处理程序也应当同读写一样注册到系统中,因为使操作系统在程序无响应时能够提供使驱动程序中断的能力。
这样一来操作系统会在硬件中断发生后自动调用驱动程序并处理程序。
(3)时钟。
许多开发设备驱动程序时上也会运用到时钟,由于驱动程序必须由操作系统提供定时机制,所以在注册时钟函数时通常是在预定的时问过了之后。
完成一个linux嵌入式设备驱动程序的流程如下:给主、次设备号下定义,或实现动态获取;完成初始化或清除驱动函数→设计好预定要实现的文件的各种操作→审核定义file—operations结构→调试所需的文件操作→向内核保证实现中断服务并注册→用命令将驱动编译到内核并完成加载→优化生成设备节点的文件。
Linux设备驱动开发详解-第6章字符设备驱动(⼀)-globalmem1 驱动程序设计之前奏 (2)1.1 应⽤程序、库、内核、驱动程序的关系 (2)1.2 设备类型 (2)1.3 设备⽂件 (2)1.4 主设备号和从设备号 (2)1.5 驱动程序与应⽤程序的区别 (3)1.6 ⽤户态与内核态 (3)1.7 Linux驱动程序功能 (3)2 字符设备驱动程序框架 (3)2.1 file_operations结构体 (4)2.2 驱动程序初始化和退出 (5)2.3 将驱动程序模块注册到内核 (5)2.4 应⽤字符设备驱动程序 (5)3 globalmem虚拟设备实例描述 (6)3.1 头⽂件、宏及设备结构体 (6)3.2 加载与卸载设备驱动 (6)3.3 读写函数 (8)3.4 seek()函数 (9)3.5 ioctl()函数 (10)3.6 globalmem完整实例 (12)4 测试应⽤程序 (17)4.1 应⽤程序接⼝函数 (17)4.2 应⽤程序 (18)5 实验步骤 (19)5.1 编译加载globalmem 模块 (19)5.2 编译测试应⽤程序 (20)6 扩展 (21)1 驱动程序设计之前奏㈠应⽤程序、库、内核、驱动程序的关系㈡设备类型㈢设备⽂件㈣主设备号与从设备号㈤驱动程序与应⽤程序的区别㈥⽤户态与内核态㈦Linux驱动程序功能1.1 应⽤程序、库、内核、驱动程序的关系■应⽤程序调⽤应⽤程序函数库完成功能■应⽤程序以⽂件形式访问各种资源■应⽤程序函数库部分函数直接完成功能部分函数通过系统调⽤由内核完成■内核处理系统调⽤,调⽤设备驱动程序■设备驱动直接与硬件通信1.2 设备类型■字符设备对字符设备发出读/写请求时,实际的硬件I/O操作⼀般紧接着发⽣■块设备块设备与之相反,它利⽤系统内存作为缓冲区■⽹络设备⽹络设备是⼀类特殊的设备,它不像字符设备或块设备那样通过对应的设备⽂件节点访问,也不能直接通过read或write进⾏数据访问请求1.3 设备⽂件■设备类型、主从设备号是内核与设备驱动程序通信时使⽤的■应⽤程序使⽤设备⽂件节点访问对应设备■每个主从设备号确定的设备都对应⼀个⽂件节点■每个设备⽂件都有其⽂件属性(c或者b)■每个设备⽂件都有2个设备号(后⾯详述)主设备号:⽤于标识驱动程序从设备号:⽤于标识同⼀驱动程序的不同硬件■设备⽂件的主设备号必须与设备驱动程序在登记时申请的主设备号⼀致■系统调⽤是内核与应⽤程序之间的接⼝■设备驱动程序是内核与硬件之间的接⼝1.4 主设备号和从设备号■在设备管理中,除了设备类型外,内核还需要⼀对被称为主从设备号的参数,才能唯⼀标识⼀个设备■主设备号相同的设备使⽤相同的驱动程序■从设备号⽤于区分具体设备的实例例:PC的IDE设备,主设备号⽤于标识该硬盘,从设备号⽤于标识每个分区■在/dev⽬录下使⽤ll命令(ls -l)可以查看各个设备的设备类型、主从设备号等■cat /proc/devices可以查看系统中所有设备对应的主设备号1.5 驱动程序与应⽤程序的区别■应⽤程序以main开始■驱动程序没有main,它以⼀个模块初始化函数作为⼊⼝■应⽤程序从头到尾执⾏⼀个任务■驱动程序完成初始化之后不再运⾏,等待系统调⽤■应⽤程序可以使⽤GLIBC等标准C函数库■驱动程序不能使⽤标准C库1.6 ⽤户态与内核态■驱动程序是内核的⼀部分,⼯作在内核态■应⽤程序⼯作在⽤户态■数据空间访问问题★⽆法通过指针直接将⼆者的数据地址进⾏传递★系统提供⼀系列函数帮助完成数据空间转换get_userput_usercopy_from_usercopy_to_user1.7 Linux驱动程序功能■对设备初始化和释放■把数据从内核传送到硬件和从硬件读取数据■读取应⽤程序传送给设备⽂件的数据和回送应⽤程序请求的数据■检测和处理设备出现的错误2 字符设备驱动程序框架①Linux各种设备驱动程序都是以模块的形式存在的,驱动程序同样遵循模块编程的各项原则②字符设备是最基本、最常⽤的设备,其本质就是将千差万别的各种硬件设备采⽤⼀个统⼀的接⼝封装起来,屏蔽了不同设备之间使⽤上的差异性,简化了应⽤层对硬件的操作③字符设备将各底层硬件设备封装成统⼀的结构体,并采⽤相同的函数操作,如下等:open/close/read/write/ioctl④添加⼀个字符设备驱动程序,实际上是给上述操作添加对应的代码⑤Linux对所有的硬件操作统⼀做以下抽象抽象file_operations结构体规定了驱动程序向应⽤程序提供的操作接⼝struct file_operations ext2_file_operations ={.llseek = generic_file_llseek,.read = generic_file_read,.write = generic_file_write,.aio_read = generic_file_aio_read,.aio_write = generic_file_aio_write,.ioctl = ext2_ioctl,.mmap = generic_file_mmap,.open = generic_file_open,.release = ext2_release_file,.fsync = ext2_sync_file,.readv = generic_file_readv,.writev = generic_file_writev,.sendfile = generic_file_sendfile,};⑥⽤户态与内核态数据的交互⽤户应⽤程序与驱动程序分属于不同的进程空间,因此⼆者之间的数据应当采⽤以下函数进⾏交换long copy_to_user(kernel_buffer, user_buffer,n)//从内核空间拷贝n字节数据到⽤户空间copy_from_user(kernel_buffer, user_buffer,n)//从⽤户空间拷贝n字节数据到内核空间put_user(kernel_value, user_buffer)//从内核空间拷贝⼀数据变量到⽤户空间get_user(kernel_value, user_buffer)//从⽤户空间拷贝⼀数据变量到内核空间(内核空间数据可是任意类型)2.1 file_operations结构体⑴write函数■从应⽤程序接收数据送到硬件ssize_t (*write)(struct file*, const char __user *, size_t, loff_t*);⑵read函数■从硬件读取数据并交给应⽤程序ssize_t (*read)(struct file *, char __user *, size_t, loff_t*); /// 从设备中同步读取数据⑶ioctl函数■为应⽤程序提供对硬件⾏为的相关配置int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);⑷open函数■当应⽤程序打开设备时对设备进⾏初始化■使⽤MOD_INC_USE_COUNT增加驱动程序的使⽤次数,当模块使⽤次数不为0时,禁⽌卸载模块Int (*open)(struct inode *, struct file*);⑸release函数■当应⽤程序关闭设备时处理设备的关闭操作■使⽤MOD_DEC_USE_COUNT减少驱动程序的使⽤次数,配合open使⽤,来对模块使⽤次数进⾏计数int (*release)(struct inode *, struct file*);⑹⑻⑻⑼⑽2.2 驱动程序初始化和退出①驱动程序初始化函数■Linux在加载内核模块时会调⽤初始化函数■在初始化函数中⾸先进⾏资源申请等⼯作■使⽤register_chrdev向内核注册驱动程序②驱动程序退出函数■Linux在卸载内核模块时会调⽤退出函数■释放驱动程序使⽤的资源■使⽤unregister_chrdev从内核中卸载驱动程序2.3 将驱动程序模块注册到内核内核需要知道模块的初始化函数和退出函数,才能将模块放⼊⾃⼰的管理队列中①module_init()向内核声明当前模块的初始化函数②module_exit()向内核声明当前模块的退出函数2.4 应⽤字符设备驱动程序㈠加载驱动程序■insmod 内核模块⽂件名■cat /proc/devices 查看当前系统中所有设备驱动程序及其主设备号㈡⼿动建⽴设备⽂件■设备⽂件⼀般建⽴/dev⽬录下■mknod ⽂件路径c [主设备号] [从设备号]㈢应⽤程序接⼝函数■编写应⽤层测试程序■可以使⽤标准C的⽂件操作函数来完成①int open(const char *path, int oflag,…);★打开名为path的⽂件或设备★成功打开后返回⽂件句柄★常⽤oflag:O_RDONLY, O_WRONLY, O_RDWR②int close(int fd);★关闭之前被打开的⽂件或设备★成功关闭返回0,否则返回错误代号③ssize_t read(int fd, void *buffer, size_t count)★从已经打开的⽂件或设备中读取数据★buffer表⽰应⽤程序缓冲区★count表⽰应⽤程序希望读取的数据长度★成功读取后返回读取的字节数,否则返回-1④ssize_t write(int fd, void *buffer, size_t count);★向已经打开的⽂件或设备中写⼊数据★buffer表⽰应⽤程序缓冲区★count表⽰应⽤程序希望写⼊的数据长度★成功写⼊后返回写⼊的字节数,否则返回-1④int ioctl(int fd, unsigned int cmd, unsigned long arg);★向驱动程序发送控制命令★cmd:⽤来定义⽤户向驱动分配的命令例如G PF驱动中:设置指定管脚的⾼低电平、输⼊输出特性等为了规范化及错误检查常⽤_IO宏合成该命令:_IO(MAGIC, num) ★arg:配置命令参数配合cmd命令完成指定功能3 globalmem虚拟设备实例描述3.1 头⽂件、宏及设备结构体在globalmem字符设备驱动中,应包含它要使⽤的头⽂件,并定义globalmem设备结构体及相关宏。