第10章-嵌入式Linux设备驱动开发_第1页
第10章-嵌入式Linux设备驱动开发_第2页
第10章-嵌入式Linux设备驱动开发_第3页
第10章-嵌入式Linux设备驱动开发_第4页
第10章-嵌入式Linux设备驱动开发_第5页
已阅读5页,还剩47页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2024/1/5Page1第10章嵌入式Linux设备驱动开发设备驱动程序介于操作系统和硬件之间,屏蔽了硬件设备的物理细节,并提供了访问各种硬件设备的统一接口。驱动程序设计是嵌入式Linux开发中重要的一局部,也是比较困难的一局部,在进行这局部的开发时需要熟悉Linux的内核机制、驱动程序与用户级应用程序的接口,需要考虑系统对设备的并发操作,需要非常熟悉所开发硬件的工作原理。2024/1/5Page2第10章嵌入式Linux设备驱动开发Linux设备驱动开发概述10.1设备驱动模块化编程10.2Linux字符设备驱动10.3块设备驱动10.42024/1/5Page3第10章嵌入式Linux设备驱动开发

本章小结10.6思考与练习10.7网络设备驱动10.52024/1/5Page410.1Linux设备驱动开发概述

操作系统的目的之一就是掩盖掉各种硬件的特殊性,使得系统中的硬件设备对于用户而言是透明的。用来管理硬件控制器的软件通常叫做设备驱动程序。Linux驱动程序功能是对设备初始化和释放、把数据从内核传送到硬件和从硬件读取数据、读取应用程序传送给设备文件的数据和回送应用程序请求的数据、检测和处理设备出现的错误。Linux核心的设备驱动程序根本上是一些共享库(SharedLibrary),在库中含有一些用来处理底层硬件的例程。Linux的设备驱动程序用来处理各种硬件的多样性。操作系统的根本功能之一是对设备处理的抽象化。所有的物理设备被当做正规的文件来处理,可以被“翻开〞、“关闭〞、“读〞和“写〞,就像用系统调用处理文件一样。系统中每一个设备都对应一个设备特殊文件,例如,系统中的第一个IDE磁盘的设备文件名是/dev/hda。对于块设备(如磁盘)和字符设备,它们的的设备特殊文件通常是通过mknod命令用主设备号和次设备号来描述和创立的。2024/1/5Page510.1Linux设备驱动开发概述Linux设备驱动开发调试有两种方法:一种是直接编译到内核,随同Linux启动时加载,启动内核时就会驱动此硬件设备。这种方法称为静态链接。另一种是编译为可加载模块〔Loadablekernelmodules〕的形式,编译生成一个.o文件,当应用程序需要时再动态加载进内核空间运行,这种方法称为动态链接。Linux提供了一批管理内核模块的命令,主要有1smod、insmod和rmmod等。1smod命令用于查看当前内核加载的模块信息;insmod命令将编译的模块直接插入内核,如果出现故障,可以使用rmmod命令从内核卸载模块,而不需要重新启动内核。2024/1/5Page610.1Linux设备驱动开发概述2024/1/5Page710.1Linux设备驱动开发概述设备驱动和文件系统的关系设备驱动程序的任务包括自动配置和初始化子程序,负责检测所要驱动的硬件设备是否存在和是否能正常工作。如果该设备正常,那么对这个设备及其相关的设备驱动程序需要的软件状态进行初始化。这局部驱动程序仅在初始化的时候被调用一次。10.1Linux设备驱动开发概述效劳于I/O请求的子程序,又称为驱动程序的上半局部。调用这局部是系统调用的结果。在执行这局部程序的时候,系统仍认为和进行调用的进程属于同一个进程,只是由用户态转换为核心态,并具有进行此系统调用的用户程序的运行环境,所以可以在其中调用sleep()等与进程运行环境有关的函数。在系统内部,I/O设备的存取通过一组固定的入口点来进行,这组入口点是由每个设备的设备驱动程序提供的。设备驱动和文件系统的关系2024/1/5Page810.1Linux设备驱动开发概述10.1.2设备类型和设备号

1.字符设备2.块设备3.网络设备2024/1/5Page910.1Linux设备驱动开发概述1.字符设备一个字符设备是一种字节流设备,对设备的存取只能按顺序按字节的存取而不能随机访问,字符设备没有请求缓冲区,所有的访问请求都是按顺序执行的。Linux下的大多设备都是字符设备。应用程序是通过字符设备节点来访问字符设备的。设备节点一般都由mknod命令创立在/dev目录下,字符设备文件的第一个标志是前面的“c〞标志。如“crw-rw〞、“crw-r〞“crw-rw〞所代表的文件都是字符设备文件。2024/1/5Page1010.1Linux设备驱动开发概述1.字符设备Linux使用管理文件的方法来管理字符设备,所以每个字符设备在/dev/目录下都有一个对应的设备文件,即设备节点,它们包含了设备的类型、主/次设备号以及设备的访问权限控制等。每个字符设备文件都有自己的与普通文件相同的文件操作函数组结构〔structfile_operations〕。字符设备驱动通常至少需要实现文件操作函数组中的open()、release()、read()和write()四种操作方法。常见的字符设备有鼠标、键盘等。2024/1/5Page112.块设备存储设备一般属于块设备,块设备有请求缓冲区,并且支持随机访问而不必按照顺序去存取数据。Linux下的磁盘设备都是块设备,尽管在Linux下有块设备节点,但应用程序一般是通过文件系统及其高速缓存来访问块设备的,而不是直接通过设备节点来读写块设备上的数据。块设备文件的第一个标志是前面的“b〞标志,如“brw-rw〞所代表的就是块设备。10.1Linux设备驱动开发概述2024/1/5Page122.块设备块设备既可以作为普通的设备用来存放任意数据,也可以将块设备按某种文件系统类型的格式进行格式化,然后按照该文件系统类型的格式来读取块设备上的数据,但不管哪种方式,最后访问设备上的数据都必须通过调用设备本身的操作方法实现,区别在于前者直接调用块设备的操作方法,而后者那么间接调用块设备的操作方法。常见的块设备有各种硬盘、flash磁盘、RAM磁盘等。10.1Linux设备驱动开发概述2024/1/5Page133.网络设备网络设备不同于字符设备和块设备,它是面向报文的而不是面向流的,它不支持随机访问,也没有请求缓冲区。在Linux里一个网络设备也可以叫做一个网络接口,应用程序是通过Socket而不是设备节点来访问网络设备,在系统里根本就不存在网络设备节点。内核使用一套与数据包传输相关的函数来与网络设备驱动程序通信,它们不同于字符设备和块设备的read()和write()方法。10.1Linux设备驱动开发概述2024/1/5Page143.网络设备Linux系统通过设备号来区分不同设备。设备号由两局部组成:主设备号和次设备号。主设备号指明对应哪些设备驱动,这种对应关系是固定不变的并作为内核资源的一局部存在。需要注意的是,同一个主设备号可以对应两个不同的设备驱动,一个可以是字符设备另一个可以是块设备。10.1Linux设备驱动开发概述2024/1/5Page153.网络设备次设备号区分被一个设备驱动控制下的某个独立的设备。比方,同一个类型的USB设备可以在系统中存在几个,它们通过次设备号加以区分,而设备驱动可以只对应一个。Linux支持的各种设备的主设备号定义在include/linux/major.h文件中,而已经在官方注册的主设备号和次设备号在Documentation/devices.txt文件中可以找到。10.1Linux设备驱动开发概述2024/1/5Page16模块是程序设计中一个十分重要的概念,它是一个具有独立逻辑功能的代码块,每个模块都可以被重复使用,模块化编程可以有效提高程序的编写效率,同时也能增强程序的可读性和易修改性。10.2设备驱动模块化编程2024/1/5Page172024/1/5Page1810.2设备驱动模块化编程10.2.1设备驱动程序原理Linux设备驱动程序主要由驱动程序的注册与注销、设备的翻开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理等局部组成,每个局部都有与之对应的接口。10.2设备驱动模块化编程10.2.1设备驱动程序原理

Linux内核在结构体file_operations中统一定义了设备文件的各个访问接口,file_operations结构定义在/include/linux/fs.h中。设备驱动程序加载时,首先会在内核中注册对应的设备号和结构体file_operations。应用程序通过系统调用访问设备时,内核会根据设备号查找到相应的结构体file_operations,然后再根据结构体file_operations中的接口调用具体放入设备访问函数。2024/1/5Page192024/1/5Page2010.2设备驱动模块化编程10.2.2设备访问方式及实现设备访问方式有多种,选择哪种访问方式不仅与设备本身有关,还与它的使用情况有关。Linux对查询方式、中断方式和DMA三种方式都提供了很好的支持。10.2.2设备访问方式及实现1、查询方式在数据传送之前,对目标设备的状态进行查询,确知外设已经做好了传送数据的准备时再进行数据传送,否那么,CPU等待并持续不断的查询,一旦外设准备好,那么立即进行读或写操作,这种方式称为查询方式。10.2设备驱动模块化编程2024/1/5Page2110.2设备驱动模块化编程1、查询方式对于查询方式来说,一个数据传送过程由三个环节组成:(1)从接口中读取状态字。(2)CPU检测状态字的对应位是否满足就绪条件,如果不满足,那么回到前一步,继续读取状态字。(3)如果状态字说明外设己处于就绪状态,那么传送数据。这种方式看上去似乎很浪费资源,效率低,但在某些情况下仍有使用的价值。例如当一个设备从翻开计算机到关闭之前一直需要使用,而它又没有向系统发中断的功能,这种场合下查询方式还是很有用的。在Linux驱动程序中实现这种方式还是比较简单的。只要在驱动程序的read()程序中使用while语句一直判断外设的状态,状态一旦满足要求,那么跳出循环,读取外设数据并返回read()函数就可以了。2024/1/5Page222、中断方式在中断传送方式下,外设具有申请CPU效劳的主动权,当输入设备已将数据准备好或者输出设备可以接收数据时,便可以向CPU发出中断请求,使CPU暂时停下目前的工作而和外设进行一次数据传输,等输入操作完成后,CPU继续进行原来的工作。可见中断传送方式就是外部设备中断CPU的工作,使CPU停止执行当前程序,而去执行一个为外部设备的数据输入输出效劳程序,该效劳程序称为中断处理子程序或中断效劳子程序。中断子程序执行完后,CPU又转回来执行原来的程序。被外界中断时,程序中下一条指令所在处称为断点,从中断效劳程序返回时,从断点处继续执行被中断的程序。10.2设备驱动模块化编程2024/1/5Page232、中断方式采用这方式访问外设的前提是该外设能发出操作系统能识别的中断。在Linux驱动程序中采用中断方式访问外设需做到以下几点:(1)向系统申请中断。Linux提供了函数request_irq()来实现申请中断。申请成功与否的关键在于此函数的一个参数irq,它是希望申请的中断号。如果外设可以发中断,那该参数就填2。不过还有一点需要考虑的是如果想申请的中断号己被别的设备占用,那就需要想方法协调这两个设备了。可以使用命令cat/proc/interrupts来查看操作系统中现有中断号的使用情况。当然是否申请成功,也可以通过这条命令查看。(2)释放中断。函数free_irq()可以实现这个功能。(3)实现中断函数。申请完中断号以后,希望系统响应该中断后做些什么全依赖此函数。其实该函数也没有特别的地方,无非就是做些读写设备的动作,采集需要的数据。不过需要说明的是该函数应该尽量的短,因为此时在独占CPU资源,系统很难响应别的事件。该函数的末尾应该调用函数wake_up_interruptible(),来唤醒该设备队列中其它进程。10.2设备驱动模块化编程2024/1/5Page243、DMA方式在DMA方式下,外部设备利用专门的接口电路直接和存储器进行数据传送,并不经过CPU。这样,进行传输就不必进行保护现场之类的一系列额外操作,数据传输的速度根本上决定于外设和存储器的速度。DMA方式传送数据那么由DMA控制器来提供源和目的地址、修改地址、控制结束及发出控制信号。显然,采用DMA方式,数据传送的速度显著提高,而且CPU的负担也明显减轻了。同时也缩短了传送的响应时间。10.2.2设备访问方式及实现2024/1/5Page253、DMA方式DMA传送的根本过程如下:(1)外部设备(或CPU利用指令)向DMA控制器发出DMA传送请求。(2)DMAC向CPU发总线请求,要求CPU交出总线的管理和使用权,并在接到CPU的总线响应信号后接管系统总线的管理和使用权,从而变为系统的主设备。(3)DMAC将被访问存储单元地址送到地址总线上。(4)向存储器和进行DMA传送的外设发出读写命令,那么存储器和外设通过数据总线进行数据传送。(5)假设DMA传送己完成,那么DMAC撤销对CPU的总线请求,交回系统总线的管理和使用权,回到从设备状态。10.2.2设备访问方式及实现2024/1/5Page262024/1/5Page2710.2设备驱动模块化编程10.2.3内核与驱动程序的关系操作系统中分为单体内核〔Monolithickernel〕和微内核〔Microkernel〕两种,单体内核是一个相对较大的程序,而微内核是一个较小的程序,操作系统的大局部功能运行在用户空间。Linux是一个单体内核,分成5个子系统,整个内核在一个地址空间。这样增加一个设备就比较麻烦,由于设备需要在内核空间运行,因此需要重新编译内核。Linux通过使用内核可以根据需要将各局部放入内核。10.2.3内核与驱动程序的关系应用程序、库、内核、驱动程序的关系框图如图10-2所示:2024/1/5Page282024/1/5Page2910.2设备驱动模块化编程10.2.4中断处理中断处理是设备驱动程序的根本功能之一。Linux内核对所有的中断进行了统一编号,并把中断的相关信息存储在结构体数组irq_desc中。该数组定义在/include/linux/irq.h中。每个数组元素对应一个中断,其中包含中断名、中断状态和硬件访问函数以及中断处理函数的入口地址等信息。2024/1/5Page3010.3Linux字符设备驱动ARM处理器的应用领域Linux字符设备驱动框架如下: 定义一个结构体staticstructfile_operations变量,其内定义一些设备的翻开、关闭、读、写、控制函数; 在结构体外分别实现结构体中定义的这些函数; 向内核中注册或删除驱动模块。字符设备驱动程序的框架的核心是数据结构structfile_operation,它是一系列指针的集合,每个被翻开的文件都对应于一系列的操作,需要在驱动程序中加以实现。其定义如下〔在/include/linux/fs.h中定义〕:structfile_operations{int(*seek)(structinode*,structfile*,off_t,int);int(*read)(structinode*,structfile*,char,int);int(*write)(structinode*,structfile*,off_t,int);int(*readdir)(structinode*,structfile*,structdirent*,int);int(*select)(structinode*,structfile*,int,select_table*);int(*ioctl)(structinode*,structfile*,unsinedint,unsignedlong);int(*mmap)(structinode*,structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structinode*,structfile*);int(*fasync)(structinode*,structfile*,int);int(*check_media_change)(structinode*,structfile*);int(*revalidate)(dev_tdev);}10.3Linux字符设备驱动2024/1/5Page31块设备接口主要是针对硬盘、软盘、CD-ROM等慢速设备设计的,以免消耗过多的CPU等待时间。它仅仅支持面向块的I/O操作,所有I/O操作都通过在内核地址空间中的I/O缓冲区进行。当多个请求同时提交给设备时,块设备访问的性能很大程度上取决于请求的顺序。由于块设备有可移动的单元,如果所有的请求是朝着同一个方向的,性能最正确。10.4块设备驱动2024/1/5Page32块设备驱动程序的特点如下:1、块设备接口相对复杂,不如字符设备明晰易用。2、块设备驱动程序对整个系统的性能影响较大,速度和效率是需要考虑的重要方面。3、系统中使用缓冲区与访问请求的优化管理来提高系统的性能。10.4块设备驱动2024/1/5Page3410.4块设备驱动10.4.1块设备驱动简介块设备〔blockdevice〕是文件系统的物质根底,它也支持像文件一样被访问。这种为翻开的块特殊文件提供正确的文件操作组的机制和字符设备的十分相似。Linux用blkdevs向量表维护已经登记的块设备文件。它像chrdevs向量表一样,使用设备的主设备号作为索引。它的条目也是device_struct数据结构。与字符设备不同,块设备进行分类,SCSI是其中一类,而IDE是另一类。2024/1/5Page3510.4块设备驱动10.4.2块设备相关结构体1.structblock_device_operations结构体2.structgendisk结构体3.structrequest_queue结构体4.structhd_struct结构体5.structbio结构体6.structblock_device结构体

10.4块设备驱动structblock_device_operations结构体块设备通过structblock_device_operations结构使它们的操作对系统可用,定义在/include/linux/fs.h中。structblock_device_operations{int(*open)(structinode*,structfile*);/*翻开设备*/int(*release)(structinode*,structfile*);/*释放*/int(*ioctl)(structinode*,structfile*,unsigned,unsignedlong);/*ioct1系统调用的实现*/long(*unlocked_ioctl)(structfile*,unsigned,unsignedlong);long(*compat_ioctl)(structfile*,unsigned,unsignedlong);int(*direct_access)(structblock_device*,sector_t,unsignedlong*);int(*media_changed)(structgendisk*);/*用于检查驱动器介质是否改变*/int(*revalidate_disk)(structgendisk*);/*使介质有效*/int(*getgeo)(structblock_device*,structhd_geometry*);/*填充驱动器信息*/structmodule*owner;/*模块拥有者*/};2024/1/5Page362.structgendisk结构体Linux内核中,使用structgendisk结构体来表示一个独立的磁盘设备或者分区。structgendisk{intmajor;/*主设备号*/intfirst_minor;/*第1个次设备号*/intminors;/*最大次设备号数量,如果设备不能分区,该值为1*/chardisk_name[32];/*主设备名*/structhd_struct**part;/*分区信息*/intpart_uevent_suppress;structblock_device_operations*fops;/*设备操作结构体*/structrequest_queue*queue;/*设备请求队列*/void*private_data;/*私有数据*/sector_tcapacity;/*扇区数*/intflags;/*设置驱动器状态的标志*/chardevfs_name[64];intnumber;structdevice*driverfs_dev;structkobjectkobj;10.4块设备驱动2024/1/5Page373.structrequest_queue结构体structrequest_queue结构体表征等待进行的请求队列。structrequest_queue{structlist_headqueue_head;structrequest*last_merge;

elevator_televator;

request_fn_proc*request_fn;merge_request_fn*back_merge_fn;merge_request_fn*front_merge_fn;merge_requests_fn*merge_requests_fn;make_request_fn*make_request_fn;prep_rq_fn*prep_rq_fn;unplug_fn*unplug_fn;merge_bvec_fn*merge_bvec_fn;activity_fn*activity_fn;structtimer_listunplug_timer;intunplug_thresh;unsignedlongunplug_delay;

structwork_structunplug_work;structbacking_dev_infobacking_dev_info;}10.4块设备驱动2024/1/5Page384.structhd_struct结构体该结构体存储了磁盘上的分区信息。structhd_struct{sector_tstart_sect;sector_tnr_sects;structkobjectkobj;unsignedreads,read_sectors,writes,write_sectors;intpolicy,partno;}

10.4块设备驱动2024/1/5Page395.structbio结构体该结构用来描述内核以文件系统、虚拟内存子系统或系统调用的形式对块I/O设备进行的输入、输出数据的操作。structbio{sector_tbi_sector;structbio*bi_next;/*请求队列链表*/structblock_device*bi_bdev;unsignedlongbi_flags;/*状态、命令等*/unsignedlongbi_rw;unsignedshortbi_vcnt;/*bio_vec的个数*/unsignedshortbi_idx;/*bvl_vec的当前索引*/unsignedshortbi_phys_segments;unsignedshortbi_hw_segments;unsignedintbi_size;/*剩余I/O数量*/unsignedintbi_hw_front_size;unsignedintbi_hw_back_size;unsignedintbi_max_vecs;/*最多可持有的bvl_vecs数量*/structbio_vec*bi_io_vec;/*实际的矢量表*/bio_end_io_t*bi_end_io;atomic_tbi_cnt;void*bi_private;bio_destructor_t*bi_destructor;/*销毁器*/};10.4块设备驱动2024/1/5Page406.structblock_device结构体这个结构代表了内核中的一个设备,它可以表示整个磁盘或者一个特定的分区。当这个结构代表一个分区时,bd_contains指向包含这个分区的设备,bd_part指向设备的分区结构。当这个结构代表一个块设备时,bd_disk指向设备的gendisk结构。structblock_device{dev_tbd_dev;structinode*bd_inode;/*分区节点*/intbd_openers;structsemaphorebd_sem;/*翻开/关闭锁*/structsemaphorebd_mount_sem;/*加载互斥锁*/structlist_headbd_inodes;void*bd_holders;intbd_holders;}10.4块设备驱动2024/1/5Page41Linux系统具有比较完善的网络功能,Linux网络设备驱动是Linux网络应用的重要组成局部。在Linux网络系统中,其网络子系统主要是基于BSDunix的socket机制。在网络子系统和驱动程序之间定义了专门的数据结构sk_buff进行数据传递。Linux系统提供支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。网络接口在内核层处理包的发送和接收,并不存在于进程中的Linux文件系统中。网络接口在文件系统中的角色就像被挂载的块设备,在内核中用device数据结构来表示,在做数据包的发送和接收时直接通过接口访问,不需要进行文件的操作。网络驱动程序和内核之间的交互一次处理一个网络包,这允许协议可以对驱动程序隐藏,而物理传输可以对协议隐藏。10.5网络设备驱动2024/1/5Page42Linux网络设备驱动层次结构为:1、网络协议接口层2、网络设备接口层3、设备驱动功能层4、网络设备与媒介层10.5网络设备驱动2024/1/5Page432024/1/5Page4410.5网络设备驱动10.5.1网络设备概述

常用的网络设备有中继器、网桥、路由器和网关。中继器是局域网互连的最简单设备,它工作在OSI体系结构的物理层,接收并识别网络信号,然后再生信号

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论