Linux下有中断端点的USB设备驱动的实现

Linux下有中断端点的USB设备驱动的实现
Linux下有中断端点的USB设备驱动的实现

0引言

Linux 以其内核高效稳定、源码开放、文件管理机制完善等优点,得到了广泛的应用。而USB 接口以其低成本、速度高、通用性强、支持即插即用等优点,越来越多的被采用。由于Linux 在硬件配置上尚不能全部支持USB 设备,因此Linux 下如何驱动有USB 接口的设备一直是该领域工程实现的热点和难点。USB 设备传输数据时所用端点主要有批量端点和中断端点两大类,如打印机、大容量存储设备属于批量端点,其特点是传输批量的没有周期和传输速率要求的数据;而手写板等设备属于中断端点,其特点是主机通过对设备的查询来确定设备是否有数据需要传输[1]

。批量端点的设备驱动程序设计比较简单,内核源码中的skeleton 示例即为批量端点的;而针对中断端点,内核源码主要提供鼠标和键盘设备的驱动程序示例,它们都是采用输入子系统方式实现的,不够灵活。如果结合批量端点的示例程序和中断端点的传输特点,实现有中断端点的USB 设备驱动,可以简化程序,提高效率,并且有很强的实用价值和创新价值。但是在批量端点的驱动

程序中,读函数一次即可完成数据传输,而中断端点的传输方式是中断的,要多次读取数据,所以需要解决线程同步问题,相对比较复杂。本文将以手写板设备为例,参考Linux 内核源码中批量端点的USB 设备驱动的示例[2],对Linux 下如何设计和实现有中断端点的USB 设备驱动进行研究。实验结果显示,设计出的驱动可以成功读写设备。

1USB 系统简介

USB 意为通用串行总线,以单一类型的总线连接各种不同类型的设备,是一种被动的总线类型,不能主动向主机发送任何消息。

USB 驱动程序在内核中的位置如图1所示。USB 核心通过定义一系列的数据结构、函数和宏来抽象设备的硬件细节,提供专门的接口来支持USB 设备驱动和主控制器。

Linux 操作系统将设备分成4种类型:字符设备,块设备,网络设备和其它设备[3]。鼠标、键盘、串口属于字符设备,它们可以很好地用“流”来描述。本文主要讨论字符设备。

USB 设备包括配置、接口和端点,分别对应配置描述符、

收稿日期:2009-06-22;修订日期:2009-08-26。

开发与应用

接口描述符和端点描述符。通过获取描述符,设备驱动程序就可以知道具体设备的相关信息,从而对设备进行相应控制。驱动程序会对USB 设备的每一个接口进行一次探测,当探测成功后,驱动程序就被绑定到这个接口上[4]。如图2所示。

USB 通信最基本的形式是通过端点(USB 端点分中断、批量、等时、控制4种)进行的[5],USB 端点可看作是单向的管道,只能往一个方向传送数据,从主机到设备或者从设备到主机。

2Linux 设备驱动原理

Linux 驱动硬件的步骤如下:

(1)首先加载驱动程序,向系统注册,得到一个主设备号。(2)当安装上相应硬件后,驱动程序会根据主设备号在/dev 目录下创建一个设备文件,并给该设备文件分配一个次设备号,就是说该硬件设备文件有主、次设备号[6]。

(3)当打开此设备文件时,Linux 操作系统就知道该设备对应的驱动程序,对其加以控制。

在应用程序与硬件设备通信过程中,涉及一个很重要的数据结构:file_operations 。这个结构主要包括open 、read 、write 、release 等成员,每个成员是一个系统调用,分别对应驱动程序中定义的open 、read 、write 、release 等函数[7]。当应用程序对某个设备文件执行open 、read 等函数时,首先设备文件会根据已分配的主、次设备号找到设备驱动程序,再根据设备驱动程序对file_operation 结构体定义的系统调用,找到对应的open 、read 等函数,并把控制权交给相应函数,完成与硬件设备的通信。如图3所示。

3Linux 下的USB 设备驱动机制的研究

在Linux 环境下设计驱动程序,思想简洁、操作简单、功能强大,但是它只能调用Linux 内核提供的函数,而且要和硬件交互,系统容易崩溃,工程实现比较困难。

一个USB 驱动程序要完成4项工作:识别驱动程序要支持的设备、注册和注销USB 驱动程序、探测和断开、传输数据[8]。

3.1

识别驱动程序要支持的设备

USB 核心使用一个列表来判断对于一个设备该使用哪一

个驱动程序。这个列表由包含制造商ID 和设备号ID 的结构体构成。当安装硬件的制造商ID 和设备号ID 与列表信息匹配时,硬件可以使用该驱动程序。

3.2注册和注销USB 驱动程序

一个USB 驱动程序必须有结构体usb_driver ,以向USB 核

心描述USB 驱动程序,一般包括name 、id_table 、probe 、discon-nect 这几个成员,分别告诉内核驱动程序名称、硬件匹配信息列表和当一个匹配的硬件连接或移除时,应调用哪些函数。在USB 驱动程序被初始化时,调用usb_register 函数把usb_driver 注册到USB 核心。在USB 驱动程序将要被卸载时,调用usb_deregister 函数把usb_driver 从内核中注销,然后,与该驱动程序绑定的所有USB 接口被断开,并调用断开函数。

3.3探测和断开

当一个设备连上总线时,usb_get_device_descriptor 函数自

动执行,取出设备的描述符树结构,用结构体usb_device 保存配置和接口的信息。如果USB 核心认为有一个接口可以由该驱动程序处理,探测函数probe 被调用。在探测函数中,USB 驱动程序调用usb_set_intfdata 函数将设备信息保存到接口,然后调用usb_register_dev 函数向USB 核心注册硬件设备。同样,当一个USB 接口从系统中移除或者驱动程序正在从USB 核心中卸载时,USB 核心将调用断开函数disconnect [9]。在断开函数里,接口数据被置空,通过调用usb_deregister_dev 函数硬件设备被注销。

3.4传输数据

当驱动程序有数据要发送到USB 设备时或者要从USB

设备接收数据时,即实现read 、write 函数,一般要分配并提交一个urb (USB 请求块)来实现与设备间的数据传输。在初始化

图1USB 驱动程序在内核中的位置

硬件

USB 设备驱动程序

USB 核心USB 主控制器

用户

字符设备块设备层网络设备TTY 设备

其它

内核

接口端点端点端点

urb时可以指定回调函数,来报告urb传输的状态信息。由于驱动程序属于内核空间,应用程序属于用户空间,而内核空间与用户空间不能直接通信,因此驱动程序要在内核空间分配一个缓冲区做中介以读写USB设备的数据,它一方面与硬件交互信息,另一方面通过copy_to_user或copy_from_user函数与用户层传递数据。

4在Linux下的手写板设备驱动的实现

上面是针对Linux2.6.22.14的内核源码里的skeleton.c 程序所做的分析,其设计框架和开发流程是很典型的[10]。该示例程序是针对批量端点进行数据传输的,在读设备时用一次读完的方法;而对于中断类型的端点,则需要多次的读,该例程无法正常工作。本文在此例程的基础上加以研究、进行改进,针对有中断端点的USB手写板设备,设计并实现了其驱动。

4.1设计与实现有中断端点的USB设备驱动

设计有中断端点的USB设备驱动步骤如下:

(1)Linux设备驱动程序作为内核的一部分,运行于内核态,需包含相应的头文件和编译预定义。

(2)定义usb_skel结构体变量dev,用来保存硬件设备基本信息,包括设备、接口、引用主键、读写缓冲区、读写端点能处理的字节数、读写端点的地址、读写端点轮询的时间间隔。

(3)根据设备制造商和设备号定义变量skel_table来创建匹配硬件列表。定义usb_driver结构体变量skel_driver来描述驱动程序,初始化name、id_tabe、probe、disconnect等成员。在usb_skel_init和usb_skel_exit函数中,分别调用usb_register和usb_deregister来实现注册和注销设备驱动。

(4)实现skel_probe和skel_disconnect函数来完成当设备连接或移除时,驱动自动调用的函数,即usb_driver的probe、dis-

connect成员对应的函数。定义delete函数来回收已分配的资源,在disconnet函数中被调用。

(5)定义file_operations结构体变量skel_fops,初始化open、release、read、write系统调用,分别实现相应的skel_open、skel_ release、skel_read、skel_write函数。其中,open函数用来打开设备文件,增加对设备的引用计数,并将设备保存为文件私有数据。release函数用来实现关闭设备文件时,减少对设备的引用计数,并调用delete函数回收资源。write和read函数用来实现写入用户空间数据到硬件和读取硬件数据到用户空间的功能,其实现将在下文重点介绍。

USB驱动的实现基本遵循固定格式,可以参考例程。由于probe函数要处理硬件信息,有特殊性,因此详细说明probe 函数的实现过程,如图4所示。

在probe函数里,找出中断读端点和中断写端点来建立通信管道,初始化dev的各成员变量,最后向内核注册硬件设备生成设备文件。其中要动态分配两块内存分别作为读和写操作时urb用的缓冲区int_in_buffer和int_out_buffer,这两块缓冲区在硬件被移除或者设备驱动程序被卸载时调用到的delete 函数中释放。

4.2信号量在read函数中的应用

由于批量端点与中断端点传输数据的不同主要体现在read、write函数上,两个函数实现方法类似,而read函数是实现的难点,所以本文重点讨论read函数,其实现过程如图5所示。

在read函数中,首先分配、初始化、提交urb。提交成功后,urb交由内核控制进行传输,这时,一方面read函数向下执行copy_to_user函数,将int_in_buffer的数据复制到用户空间;另一方面,read_callback回调函数待urb传输完毕后被调用,返回urb的完成状态信息urb_status。但是,只有urb传输成功后,int_in_buffer里才是硬件上传的数据,执行copy_to_user函数才有意义。为了保证内核线程按正确的顺序进行,本文引用信号量机制。

在程序开始,定义一个全局静态的semaphore结构体变量sem。在read函数中,提交urb给内核控制后,利用down函数将信号量sem上锁,此时,read所在线程会进入休眠状态;同时,内核线程控制urb进行数据传输,读取硬件数据到int_in_ buffer中,当urb传输完毕,read_callback回调函数被执行,它返回urb_status,并利用up函数将信号量sem解锁。这样,read 所在线程由休眠状态被唤醒,继续向下执行,根据read_call-back回调函数中返回的urb_status,决定是否将int_in_buffer里的数据拷贝到用户空间,完成了对硬件设备的读操作。

但是,如果当read所在线程处于休眠状态,而urb传输中出现异常,导致read_callback回调函数不被执行,信号量sem

图4probe函数实现过程

读取输入参数-interface接口数据指针

分配结构体变量dev

通过接口获取设备来初始化dev->udev

用接口初始化dev->interface

遍历各端点寻找中断

读端点/中断写端点

得到读端点描述符

初始化读端点地址

dev->int_in_endpointAddr

分配读数据缓冲区

dev->int_in_buffer

用读端点的其它信息来

初始化dev的其它成员变量

得到写端点描述符

初始化写端点地址

dev->int_out_endpointAddr

分配写数据缓冲区

dev->int_out_buffer

用写端点的其它信息来

初始化dev的其它成员变量

将初始化后的dev保存成

interface

的私有成员变量

用interface向内核注册

新连接的硬件设备

生成设备文件,返回正确回收已分配资源,返回错误

结束

失败

成功

无法解锁,那么read 所在线程将处于死锁状态,调用read 函数的应用程序也无法向下运行,这种情况在实际使用中是不允许发生的。因此本文引用定时器来容错,即在一个指定时间内,若read_callback 回调函数未被执行,则调用定时器处理函数timer_handle ,将urb_status 设为错误状态值,并调用up 函数将信号量sem 解锁,保证read 所在线程继续向下执行。

引入信号量的优点在于read 所在线程在等待返回的urb 传输状态时,进入休眠状态,利于系统去调用其它进程,节省了对cpu 的占用率。

用上述思路设计驱动,可以实现用户读操作。而内核示例程序,没有实现对中断类型端点读取数据的功能,而且,示例程序引用信号量是用来实现互斥、保护功能,而不是实现等待、控制顺序的功能,因此,本文设计的驱动有一定的创新价值。

通过结合内核源码示例,作者成功地完成了一个有中断端点的USB 手写板设备驱动的编写,并进行了测试。

4.3测试实验及实验结果

Linux 下对于一个硬件设备的驱动可以直接编译到内核

代码中,也可以用模块方式加载。由于模块方式灵活,易调试,本文选用它进行测试。在驱动程序所在目录下,编写Make-file 文件,然后执行make 命令,就会生成驱动模块,使用insmod 命令来加载模块。在开发过程中,可以运行dmesg 命令查看

内核输出信息来进行调试,运行lsmod 命令查看驱动模块是否被加载。连接硬件设备,在/dev 目录下可以看到skel0文件,此为手写板的设备文件。应用程序通过对skel0设备文件进

行open 、read 、write 、close 等操作,即可实现与硬件设备通信。

编写一个读应用程序,实现死循环读的功能,在每次循环中判断此次读是否成功,若成功,将读取结果显示到终端上;编写另一个写应用程序,根据手写板协议,实现多次写入指定命令到设备的功能,判断每次写是否成功,若成功,将所写命令显示到终端。打开两个终端,分别运行读和写应用程序,观察结果。

查看终端显示结果发现,当写终端显示成功写入命令时,读终端会显示相应的设备回应数据。当激活命令成功写入后,用手指在手写板上滑动,读终端会不断上传坐标数据包,将得到数据保存到文件中进行分析,发现上传数据与手写板协议描述情况相符,而且没有丢包现象,查看此时驱动的CPU 占用率也很小,说明本文在Linux 下针对有中断端点的USB 手写板设备实现的驱动,不但实时准确,而且高效。

5结束语

针对有中断端点的手写板设备,本文分析了Linux 下开发

USB 设备驱动程序的基本原理和实现过程,提出了在驱动程序中引用信号量机制来控制内核线程同步,并详细介绍了信号量机制在read 函数中的应用过程,为使用Linux 的用户驱动有中断端点的USB 设备提供了一种新的方法,有很强的实用价值;本文结合内核源码中的批量端点程序示例和中断端点

的特点,比普遍采用的输入子系统方式简化许多,并且在设备驱动中通过引用信号量机制完成读取数据功能,提高了设备驱动的效率,有较强的创新性。

参考文献:

[1]梁正平,毋国庆,肖敬.Linux 中USB 设备驱动程序研究[J ].计算机应用研究,2004,21(6):70-72.

[2]Linux kernel source code [Z ].Linux-2.6.22.14.

[3]肖刚,李纪扣,畅卫功.嵌入式Linux 下USB 驱动的实现[J ].微计算机信息,2007,26:86-88.

[4]梁国军.Linux 环境下USB 的原理、驱动和配置[EB/OL ].https://www.360docs.net/doc/426050853.html,,2007.

[5]张波,张曦煌.基于嵌入式Linux 的U 盘驱动的分析与改进[J ].计算机工程与设计,2008,29(15):3917-3918.

[6]朱园.Linux 设备驱动程序的研究与开发[J ].仪表技术,2008(2):32-34.

[7]曾水平,徐峰.Linux 系统下USB 设备驱动的实现[J ].国外电子测量技术,2006,25(10):30-32.

[8]赵明.Linux 下的硬件驱动——USB 设备[EB/OL ].https://www.360docs.net/doc/426050853.html,/developerworks/cn/linux,2003.

[9]

刘飞,张曦煌.基于嵌入式平台的USB 摄像头驱动程序的实现[J ].计算机工程与设计,2008,29(8):1994-1996.

[10]科波特.LINUX 设备驱动程序[M ].魏永明,译.北京:中国电力

出版社,2006.

图5read 函数实现过程

Read 函数里,分配、初始化、提交urb

信号量上锁,read 线程休眠

定时器开始计时

内核控制Urb 传输硬件数据给int_in_buffer

规定时间内,read_callback

函数是否被调用

Read_callback 函数返回Urb_status

判断urb_status

是否为0

urb 传输成功,信号量解锁Read 函数被唤醒向下执行,将int_in_buffer 数据拷贝到用户层返回成功传送的

字节数

规定时间内,未返回

Urb_status 值

调用定时器处理函数

urb 传输失败,信号量解锁urb 传输未知,信号量解锁

Read 函数被唤醒

向下执行,记录错误信息

返回错误

结束

其它值

为0

被调用

未被调用

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

(整理)嵌入式系统的以太网接口设计及linux内核网络设备驱动.

嵌入式系统的以太网接口设计及linux驱动 1 以太网概述 以太网(Ethernet)是当今局域网采用的最通用的通信协议标准。在以太网中,所有计算机被连接在一条电缆上,采用带冲突检测的载波侦听多路访问(CSMA/CD)方法,采用竞争机制和总线拓扑结构。基本上,以太网由共享传输媒体,如双绞线电缆或同轴电缆、多端口集线器、网桥或交换机构成。 按照OSI(Open System Interconnection Reference Model,开放式系统互联参考模型)7层参考模型,以太网定义的是物理层(PHY)和数据链路层(对应以太网的MAC层)的标准。 2 嵌入式处理器上扩展以太网接口 以太网接口控制器主要包括MAC乘PHY两部分,如图1所示为嵌入式处理器集成MAC层控制器。 MAC层控制器和PHY的连接是通过MII、RMII等接口实现的。在IEEE802的标准系列中,数据链路层包括LLC和MAC两个子层。其中MAC负责完成数据帧的封装、解封、发送和接受功能。PHY层的结构随着传输速率的不同而有一定的差异。对于1OBaseT等网络,从以太网PHY芯片输出的就是传输所需的差分信号。但是还需要一个网络隔离变压器组成图2的结构。网络隔离变压器可起到抑制共模干扰、隔离线路以及阻抗匹配等作用。 本文介绍一种新款网络接口芯片DM9000A,它可以很方便的实现与嵌入式CPU的接口,实现扩展以太网口的功能。DM9000A是中国台湾DAVICOM公司推出的一款高速以太网接口芯片,其基本特征是:集成10/100M物理层接口;内部带有16K字节SRAM用作接收发送的FIFO缓存;支持8/16bit两种主机工作模式:

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

Linux网络设备驱动开发实验

实验三:Linux网络设备驱动开发实验 一、实验目的 读懂linux网络设备驱动程序例子,并且实际加载驱动程序,加载进操作系统以后,会随着上层应用程序的触发而执行相应动作,具体执行的动作可以通过代码进行改变。 ●读懂源码及makefile ●编译驱动程序 ●加载 ●多种形式触发动作 二、预备知识 熟悉linux驱动基本原理,能读懂简单的makefile。 三、实验预计时间 80-120分钟左右 四、驱动程序部分具体步骤 要求读懂一个最简单的驱动程序,在驱动程序的诸如“xxx_open”、“xxx_read”等标准接口里面加入打印语句。可参考多模式教学网上的驱动样例。 五、用于触发驱动动作的应用程序及命令 驱动程序就是以静态的标准接口库函数形式存在,网络设备驱动会受到两大类情况的触发,一种是linux里面的控制台里面的命令,另一种是套接口应用程序,首先要搞清都有哪些具体的命令和应用程序流程,应用程序参考多模式教学网的例子。 六、运行测试 提示:需要将驱动程序以dll加载进系统中,并且触发应用程序调用各种文件操作的接口函数,使得驱动有所动作,打印出相关信息。 1.编译驱动: cd /某某目录/vnetdev/ make clean make 2.加载驱动与打开网卡: insmod netdrv.ko

ifconfig vnet0 up 3.运行应用程序 ../raw 4.通过命令“修改网卡MTU”触发驱动执行动作: ifconfig vnet0 mtu 1222 5.显示内核打印: cat /var/log/messages 6.卸载: ifconfig vnet0 down rmmod netdrv.ko 7.修改代码中的某些函数中的打印信息,重新试验上述流程。 至此大家都应该真正理解和掌握了驱动程序-操作系统-应用程序的三者联动机制。 七、实验结果 由图可知能正常加载网卡驱动,并且能够打印调试信息。

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

Linux设备驱动模型之platform总线深入浅出

Linux设备驱动模型之platform总线深入浅出 在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中 platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver //include\linux\platform_device.h struct platform_driver { int (*probe)(struct platform_device *); //探测函数,在注册平台设备时被调用int (*remove)(struct platform_device *); //删除函数,在注销平台设备时被调用void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); //挂起函数,在关机被调用int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢复函数,在开机时被调用struct device_driver driver;//设备驱动结构}; 1 2 3 4 5 6 7 8

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

Linux网络设备驱动

嵌入式培训专家
Linux网络设备驱动
主讲:宋宝华
https://www.360docs.net/doc/426050853.html,

华清远见
今天的内容
vLinux网络设备驱动架构 vLinux网络设备驱动数据流程
? NON-NAPI模式数据接收流程 ? NAPI模式数据接收流程 ? 数据发送流程
vLinux网络协议栈的实现
? TCP/UDP/IP/MAC各层数据传递 ? 网络系统调用与socket

华清远见
Linux网络设备驱动架构

华清远见
net_device
struct net_device_ops { int (*ndo_open)(struct net_device *dev); int (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); void (*ndo_tx_timeout) (struct net_device *dev); ... }
struct net_device { struct net_device_stats stats; const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; ... }
struct ethtool_ops { int (*get_settings)(struct net_device *, struct ethtool_cmd *); int (*set_settings)(struct net_device *, struct ethtool_cmd *); void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); ... }

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

linux驱动基础试题

L i n u x驱动基础试题(时间:1个小时) 一、选择题(每题4分,共40分,包括单选和多选,多选、少选均不得分) 1.Linux系统中将设备进行分类管理,下列设备中(ACD)属于字符设备,(BC)属于块设备 [A]键盘[B]硬盘[C]闪存设备[D]帧缓存设备[E]网卡 2.Linux系统中,内核以(D)区分设备 [A]设备节点名[B]设备节点号[C]设备名称[D]设备号 3.Linux系统中设备节点可以创建在(A) [A]/dev目录下[B]根目录下[C]/tmp目录下[E]以上都可以 4.Linux驱动程序运行在(A) [A]内核空间[B]用户空间[C]用户空间和内核空间 5.Linux系统中设备驱动程序是以模块形式组织的,编译驱动时可以用哪种方式编译(AB) [A]静态编译进内核[B]动态编译 6.内核中,设备的主设备号用(B)位来表示,次设备号用(D)位来表示 [A]8[B]12[C]16[D]20[E]24[F]32 7.Linux系统中哪些种类的设备有设备节点(BD) [A]定时器[B]字符设备[C]块设备[D]网络设备 8.通常情况下,kmalloc函数能分配的最大内存是(C) [A]4K[B]64K[C]128K[D]4M 9.能保证物理空间上连续的内存分配函数是(AB) [A]__get_free_pages[B]kmalloc[C]vmalloc[D]malloc 10.Linux系统中通过add_timer添加的timer是(A) [A]一次的[B]循环的[C]以上两种都可以 二、简答题(每题6分,共60分) 系统中以模块方式组织设备驱动程序,请列举在一个模块程序中必不可少的组成部分。(可以写个Helloworld模块的程序) 2.请从定义、性质、操作方式等方面对比说明字符设备和块设备。 3.请列举Linux设备驱动程序中,程序延缓执行的机制。 4.简述Linux设备驱动中使用中断的步骤。 5.简述信号量和自旋锁的异同和使用时的注意事项。 6.简述命令mknod/dev/zeroc15的做用和命令各个部分的含义,并写出创建一个块设备节点的命令。 7.简述命令insmod,rmmod,lsmod的功能。 8.驱动程序中采用动态申请设备号的,我们如何得到对应设备的设备号? 9.简述设备驱动程序和普通应用程序的异同点。 10.简述mmap机制的作用和使用mmap的好处。

如何实现Linux设备驱动模型

文库资料?2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd. 如何实现Linux 设备驱动模型 设备驱动模型,对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了device 设备、driver 驱动、bus 总线和class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。总线、设备、驱动以及类之间的关系错综复杂,在Linux 内核中通过kobject 、kset 和subsys 来进行管理,驱动编写可以忽略这些管理机制的具体实现。 设备驱动模型的内部结构还在不停的发生改变,如device 、driver 、bus 等数据结构在不同版本都有差异,但是基于设备驱动模型编程的结构基本还是统一的。 Linux 设备驱动模型是Linux 驱动编程的高级内容,这一节只对device 、driver 等这些基本概念作介绍,便于阅读和理解内核中的代码。实际上,具体驱动也不会孤立的使用这些概念,这些概念都融合在更高层的驱动子系统中。对于大多数读者可以忽略这一节内容。 1.1.1 设备 在Linux 设备驱动模型中,底层用device 结构来描述所管理的设备。device 结构在文件中定义,如程序清单错误!文档中没有指定样式的文字。.1所示。 程序清单错误!文档中没有指定样式的文字。.1 device 数据结构定义 struct device { struct device *parent; /* 父设备 */ struct device_private *p; /* 设备的私有数据 */ struct kobject kobj; /* 设备的kobject 对象 */ const char *init_name; /*设备的初始名字 */ struct device_type *type; /* 设备类型 */ struct mutex mutex; /*同步驱动的互斥信号量 */ struct bus_type *bus; /*设备所在的总线类型 */ struct device_driver *driver; /*管理该设备的驱动程序 */ void *platform_data; /*平台相关的数据 */ struct dev_pm_info power; /* 电源管理 */ #ifdef CONFIG_NUMA int numa_node; /*设备接近的非一致性存储结构 */ #endif u64 *dma_mask; /* DMA 掩码 */ u64 coherent_dma_mask; /*设备一致性的DMA 掩码 */ struct device_dma_parameters *dma_parms; /* DMA 参数 */ struct list_head dma_pools; /* DMA 缓冲池 */ struct dma_coherent_mem *dma_mem; /* DMA 一致性内存 */ /*体系结构相关的附加项*/ struct dev_archdata archdata; /* 体系结构相关的数据 */ #ifdef CONFIG_OF

Linux 系统下4G 终端模块驱动的实现

龙源期刊网 https://www.360docs.net/doc/426050853.html, Linux 系统下4G 终端模块驱动的实现 作者:邹龙王德志刘忠诚周治坤 来源:《电脑知识与技术》2015年第28期 摘要:文章分析了Linux系统的设备驱动原理,USB接口设备的驱动程序编写与内核编译原理,结合实例完成了4G模块的驱动程序与内核编译,并对编译后的Linux系统进行了验证,验证了系统内核能够正确识别4G模块并分配内存,成功实现了Linux系统的4G模块驱动。 关键词:Linux;设备驱动;4G;USB 中图分类号:TP391 文献标识码:A 文章编号:1009-3044(2015)27-0206-04 Abstract: The device driver of Linux system is analyzed, and the USB interface device driver is compiled with the kernel principle. The 4G module is compiled with an example. The Linux system is verified by the 4G system. The system kernel can correctly identify the 4G module and allocate memory.. Key words: Linux; device driver; 4G; USB Linux系统以其良好的可剪裁性、强稳定性以及易操作等特点,已在物联网,程序控制,电子消费,智能家居等领域得到广泛的使用。4G网络的推广和应用也在各领域展开。因此,将Linux设备与4G网络有机地结合起来,为新一代物联网构造一个更加高速,更加安全,更加稳定的网络通信环境,将会成为一个应用热点。 本文介绍了一种Linux系统驱动4G模块的方法,Linux系统通过USB接口驱动4G终端 模块,实现4G网络的接入。首先,文章介绍了整体的软硬件应用环境,然后分析了Linux系统下的设备驱动以及USB接口设备驱动的编写原理,完成了4G终端模块在Linux系统中的驱动程序编写和内核编译,并且最后对驱动的内核烧入进行了验证性测试。 1 Linux系统设备驱动原理 当一个新的硬件设备接入Linux系统时[1],我们需要加载与其对应的驱动程序,之后驱动程序会根据自己的类型向Linux系统注册,注册成功后系统会为驱动程序配置与其类型相应的软件接口以及反馈一个主设备号给驱动程序,然后驱动程序会根据这个主设备号在/dev目录下创建一个设备文件,这样,我们就可以通过这个设备文件来对接入的硬件设备进行控制了。 1.1 Linux系统设备驱动类型

Linux设备驱动程序学习(10)-时间、延迟及延缓操作

Linux设备驱动程序学习(10)-时间、延迟及延缓操作 Linux设备驱动程序学习(10) -时间、延迟及延缓操作 度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在中定义或该文件包含的某个子平台相关文件中。作为通用的规则,即便如果知道HZ 的值,在编程时应当不依赖这个特定值,而始终使用HZ。对于当前版本,我们应完全信任内核开发者,他们已经选择了最适合的HZ值,最好保持HZ 的默认值。 对用户空间,内核HZ几乎完全隐藏,用户HZ 始终扩展为100。当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。对用户来说确切的HZ 值只能通过/proc/interrupts 获得:/proc/interrup ts 的计数值除以/proc/uptime 中报告的系统运行时间。 对于ARM体系结构:在文件中的定义如下: 也就是说:HZ 由__KERNEL__和CONFIG_HZ决定。若未定义__KERNEL__,H Z为100;否则为CONFIG_H Z。而CONFIG_HZ是在内核的根目录

的.config文件中定义,并没有在make menuconfig的配置选项中出现。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为: 所以正常情况下s3c24x0的HZ为200。这一数值在后面的实验中可以证实。 每次发生一个时钟中断,内核内部计数器的值就加一。这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。 使用jiffies 计数器 这个计数器和用来读取它的工具函数包含在,通常只需包含,它会自动放入jiffi es.h 。 jiffies 和jiffies_64 必须被当作只读变量。当需要记录当前jiffies 值(被声明为volatile 避免编译器优化内存读)时,可以简单地访问这个unsigned long 变量,如: 以下是一些简单的工具宏及其定义:

linux设备驱动

Linux设备驱动 操作系统的目的之一就是将系统硬件设备细节从用户视线中隐藏起来。例如虚拟文件系统对各种类型已安装的文件系统提供了统一的视图而屏蔽了具体底层细节。本章将描叙Linux核心对系统中物理设备的管理。 CPU并不是系统中唯一的智能设备,每个物理设备都拥有自己的控制器。键盘、鼠标和串行口由一个高级I/O芯片统一管理,IDE控制器控制IDE硬盘而SCSI控制器控制SCSI硬盘等等。每个硬件控制器都有各自的控制和状态寄存器(CSR)并且各不相同。例如Adaptec 2940 SCSI控制器的CSR与NCR 810 SCSI控制器完全不一样。这些CSR被用来启动和停止,初始化设备及对设备进行诊断。在Linux中管理硬件设备控制器的代码并没有放置在每个应用程序中而是由内核统一管理。这些处理和管理硬件控制器的软件就是设备驱动。Linux 核心设备驱动是一组运行在特权级上的内存驻留底层硬件处理共享库。正是它们负责管理各个设备。 设备驱动的一个基本特征是设备处理的抽象概念。所有硬件设备都被看成普通文件;可以通过和操纵普通文件相同的标准系统调用来打开、关闭、读取和写入设备。系统中每个设备都用一种特殊的设备相关文件来表示(device special file),例如系统中第一个IDE硬盘被表示成/dev/hda。块(磁盘)设备和字符设备的设备相关文件可以通过mknod命令来创建,并使用主从设备号来描叙此设备。网络设备也用设备相关文件来表示,但Linux寻找和初始化网络设备时才建立这种文件。由同一个设备驱动控制的所有设备具有相同的主设备号。从设备号则被用来区分具有相同主设备号且由相同设备驱动控制的不同设备。例如主IDE硬盘的每个分区的从设备号都不相同。如/dev/hda2表示主IDE 硬盘的主设备号为3而从设备号为2。Linux通过使用主从设备号将包含在系统调用中的(如将一个文件系统mount到一个块设备)设备相关文件映射到设备的设备驱动以及大量系统表格中,如字符设备表,chrdevs。 Linux支持三类硬件设备:字符、块及网络设备。字符设备指那些无需缓冲直接读写的设备,如系统的串口设备/dev/cua0和/dev/cua1。块设备则仅能以块为单位读写,典型的块大小为512或1024字节。块设备的存取是通过

Linux设备驱动程序说明介绍

Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__

《LINUX设备驱动程序》阅读笔记全十八章

《LINUX设备驱动程序》阅读笔记 目录 第1章:设备驱动程序简介 (1) 第2章:构造和运行模块 (1) 第3章:字符设备驱动程序 (1) 第4章:调试技术 (2) 第5章:并发和竞态 (2) 第6章:高级字符驱动程序操作 (3) 第7章:时间、延迟及延缓操作 (3) 第8章:分配内存 (3) 第9章:与硬件通信 (4) 第10章:中断处理 (4) 第11章:内核的数据类型 (4) 第12章:PCI 驱动程序 (5) 第13章:USB 驱动程序 (5) 第14章:Linux 设备模型 (5) 第15章:内存映射和 DMA (5) 第16章:块设备驱动程序 (6) 第17章:网络驱动程序 (6) 第18章:TTY 驱动程序 (6) 第1章:设备驱动程序简介 1、“通常,设备驱动程序就是这个进入Linux内核世界的大门”,“设备驱动程序在Linux 内核中扮演着特殊的角色,它们是一个个独立的黑盒子,使某个特定硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序”。 2、Linux系统将设备分成三种基本类型:字符设备、块设备和网络设备。 第2章:构造和运行模块 1、“内核黑客通常拥有一个‘牺牲用的’系统,用于测试新的代码”。 2、模块在被使用之前需要注册,而退出时要仔细撤销初始化函数所做的一切。驱动模块只能调用由内核导出的那些函数。 3、公共内核符号表中包含了所有的全局内核项(即函数和变量)的地址。当模块被装入内核后,它所导出的任何符号都会变成内核符号表的一部分。 第3章:字符设备驱动程序 第一节-主设备号和次设备号。对字符设备的访问都是通过文件系统内的设备名称进行的。通常而言,主设备号标识设备对应的驱动程序,而次设备号用于正确确定设备文件所指的设备。对应的数据结构为dev_t 类型。分配设备号使用函数alloc_chrdev_region() ,释放就使用unregister_chrdev_region() 函数。 第二节-一些重要的数据结构。大部分基本的驱动程序的操作都要涉及到三个重要的内

相关文档
最新文档