当前位置:文档之家› Linux设备驱动程序简介

Linux设备驱动程序简介

Linux设备驱动程序简介
Linux设备驱动程序简介

第一章Linux设备驱动程序简介

Linux Kernel 系统架构图

一、驱动程序的特点

?是应用和硬件设备之间的一个软件层。

?这个软件层一般在内核中实现

?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略

o机制:驱动程序能实现什么功能。

o策略:用户如何使用这些功能。

二、设备驱动分类和内核模块

?设备驱动类型。Linux 系统将设备驱动分成三种类型

o字符设备

o块设备

o网络设备

?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。

?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。

三、字符设备

?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用

所对应的对该硬件进行操作的功能函数。

?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部

事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进

行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功

能组件的代码,做好内核内部的善后事务,最后返回应用程序。

?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢?

?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也

存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

问指针。例如framebuffer设备就是这样一个设备,应用程序可以用

mmap 或 lseek 访问图象的各个区域。

四、块设备

?块设备通常是按照块为单位来访问数据,比如一块为512字节。

?块设备也是通过 /dev 目录下的文件系统节点来访问。块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核和驱动程序的

接口不同。

?块设备除了给内核提供和字符设备一样的接口外,还提供了专门面向块设备的接口,块设备的接口必须支持挂装文件系统,通过此接口,块设

备能够容纳文件系统,因此应用程序一般通过文件系统来访问块设备上

的内容。

?文件系统可能是除驱动程序外 Linux 系统中最重要的模块类型,与块设备驱动程序联系紧密。

五、网络设备驱动和网络接口

?网络设备驱动不同于字符设备和块设备,不在/dev下以文件节点为代表,而是通过单独的网络接口(eth0、eth1>来代表。

?任何网络事务都要经过一个网络接口,即一个能够和其它主机交换数据的设备。通常接口代表一个硬件设备(如网卡>,但也可能是个纯软件设

备。

?内核和网络驱动程序间的通讯完全不同于内核和字符设备以及块设备驱动程序之间的通信,内核调用一套和数据包传输相关的函数。

六、设备文件和设备驱动

?设备文件是文件系统上的一个节点,是一种特殊的文件,叫做设备文件。每个设备文件在用户空间代表了一个设备。

?设备文件一般存在/dev目录下,用mknod命令创建。设备文件有主、次设备号与其关联。

?设备文件是用户应用程序和设备驱动的接口。应用程序一般只能通过设备文件来使用设备驱动的功能。

?字符和块设备驱动必须有相应的设备文件来对应。

很明显,操作系统内部不可能用设备文件名来与物理设备及其驱动进行绑定。其实,操作系统内部是用设备号来与物理设备及其驱动进行绑定的。习惯上,用主设备号与驱动进行关联,用次设备号与具有相同驱动的不同物理设备关联<例如:2个硬盘)。

dennis@dennis-desktop:~$ ls -l /dev/sd[a-c]

brw-rw---- 1 root disk 8, 0 2018-04-13 13:38 /dev/sda

brw-rw---- 1 root disk 8, 16 2018-04-13 13:38 /dev/sdb

brw-rw---- 1 root disk 8, 32 2018-04-13 13:38 /dev/sdc

当用户程序运行open("/dev/ttyS0",…>时,由于设备文件/dev/ttyS0有一个设备号与其关联,因此操作系统可以获知应用程序想操控的设备的设备号,而操作系统内部又将设备号与物理设备及其驱动进行了绑定,因此操作系统就可以知道应该调用哪一个驱动去控制哪一个设备。当然这一切的前提是,操作系统内部要将设备号与物理设备及其驱动进行绑定,那么操作系统内部是用什么手段完成这种绑定关系的呢?实际上,在操作系统内部存在一个结构体链表<就是上图中的Char device list,以后称它为设备链表),链表的每个节点代表一个绑定关系<也就是说:节点至少含有2个字段,1个用于记录设备号,另1个用于记录寻找驱动的信息,通常是一个指向驱动函数结构体的指针)。那么是谁生成节点并将它链入链表的呢?当然是驱动程序!

七、构造和运行模块

1、Kernel Module的特点

?模块只是先注册自己以便服务于将来的某个请求,然后就立即结束。

?模块可以是实现驱动程序,文件系统,或者其他功能。

?加载模块后,模块运行在内核空间,和内核链接为一体。

2、模块与内核的接口函数<除掉read、write等功能函数)

生成节点并将它链入设备链表这个操作由驱动中的函数实现,这些函数什么时机运行呢?当然最合适的时机是内核加载模块

?函数 init_module:内核加载模块的时候调用。主要功能是:为以后使用模块里的函数和变量预先做准备

?函数cleanup_module:模块的第二个入口点,内核在模块即将卸载之前调用它。

3、操作模块相关的命令

?insmod: 加载模块。后面参数是模块文件名。

# insmod /lib/modules/hello.ko

Hello, world

?rmmod:卸载模块。后面参数是模块名称。

# rmmod hello

Goodbye, cruel world

?lsmod:列出当前内核使用的模块。或者查看/proc/modules文件。

?depmod:扫描/lib/modules//目录下的所有内核模块,从而给内核模块生成依赖文件。

o生成/lib/modules//modules.dep文件,其中是当前运行内核的版本号

?modprobe:根据modules.dep文件探测并加载内核模块。只需要给出模块名称,自动寻找适合的模块文件,并进行加载。注意和insmod的不同之处。

o可以自动寻找模块文件并加载。

o自动寻找并加载依赖的模块。

#cat /lib/modules/2.6.22.6/modules.dep

/lib/modules/s3c24xx_buttons.ko: /lib/modules/leds.ko

/lib/modules/leds.ko:

# lsmod

Module Size Used by Not tainted

# modprobe s3c24xx_buttons

leds initialized

buttons initialized

# lsmod

Module Size Used by Not tainted

s3c24xx_buttons 5944 0

leds 3592 1 s3c24xx_buttons # rmmod leds

rmmod: leds: Resource temporarily unavailable

# rmmod s3c24xx_buttons

buttons driver unloaded

# lsmod

Module Size Used by Not tainted

leds 3592 0

# rmmod leds

leds driver unloaded

# lsmod

Module Size Used by Not tainted

# insmod s3c24xx_buttons

s3c24xx_buttons: Unknown symbol ledoff

s3c24xx_buttons: Unknown symbol ledon

insmod: cannot insert '/lib/modules/s3c24xx_buttons.ko': Unknown symbol in module (-1>: No such file or directory

?modinfo:查看模块文件的基本信息

dennis@dennis-desktop:/work/studydriver/buttons$ modinfo

s3c24xx_buttons.ko

filename: s3c24xx_buttons.ko

license: GPL

description: S3C2410/S3C2440 BUTTON Driver

author: YangZhu E-mail: scyz@https://www.doczj.com/doc/502568516.html,

depends:

vermagic: 2.6.22.6 mod_unload ARMv4

4、内核模块的编译方法

内核源码树:指的是内核源代码tar包解压缩后形成的目录<包含其下级所有目录和文件)

已编译内核源码树:指的是已经成功生成过内核的内核源码树<即:已经成功执行过make uImage的内核源码树)

驱动大多都编译为模块,2.6内核中要想编译模块,必须先存在已经成功编译了的内核源码树<即:已编译内核源码树),且该源码树编译出来的内核就是该模块即将运行在其上的内核。

编译方法1:

?编写Makefile:obj-m := hello.o

?编译命令:make –C 内核源码树目录 M=`pwd` modules。例如:

dennis@dennis-desktop:/work/studydriver/examples/misc-modules$ make -C /work/system/linux-2.6.22.6/ M=`pwd` modules

对该make命令的解释:

要想编译内核模块,只需要在内核源码树的顶层目录下输入make modules来编译Makefile中的modules目标即可,剩下的事情,由内核构造系统全权替我们处理。但由于目前不处于内核源码树的顶层目录,并且当前目录下的Makefile 也没有modules目标,因此使用-C参数来告知make程序需要在执行之前切换到/work/system/linux-2.6.22.6/目录。此外,由于模块的源代码在当前目录中,不在内核源码树中,因此需要使用M变量<该变量是内核构造系统的变量)告知内核构造系统,编译模块所需的源代码以及Makefile在当前目录

编译方法2:

?编写Makefile如下:

ifeq ($(KERNELRELEASE>,>

KERNELDIR ?= /work/system/linux-2.6.22.6

PWD := $(shell pwd>

modules:

$(MAKE> -C $(KERNELDIR> M=$(PWD> modules

modules_install:

$(MAKE> -C $(KERNELDIR> M=$(PWD> modules_install clean:

rm -rf *.o *~ core .depend .*.cmd *.ko

*.mod.c .tmp_versions

.PHONY: modules modules_install clean

else

obj-m := hello.o

endif

?编译命令:make

对该Makefile的解释:

当make时,由于变量KERNELRELEASE尚未赋值,因此ifeq ($(KERNELRELEASE>,>为真,于是变量KERNELDIR被赋值为内核源码树目录

/work/system/linux-2.6.22.6,变量PWD被赋值为当前目录

/work/studydriver/examples/misc-modules,然后执行找到的第1个目标modules,从而执行命令

make -C /work/system/linux-2.6.22.6

M=/work/studydriver/examples/misc-modules modules

,而当该命令执行以调用内核构造系统的时候,内核构造系统会为变量KERNELRELEASE赋值,从而它不再为空,从而当前目录下的Makefile就变成了只有一行:obj-m := hello.o。此时情况与编译方法1的情况完全相同,因此2种编译方法得到了相同的结果。

最后将得到编译好的模块hello.ko

5、简单的内核模块例子

?初始化函数:hello_init,使用宏module_init来声明。

?销毁函数:hello_exit,使用宏module_exit来声明。

?模块LICENSE信息,使用宏MODULE_LICENSE来说明

1 #include

2 #include

3 MODULE_LICENSE("Dual BSD/GPL">。

4

5 static int hello_init(void>

6 {

7 printk(KERN_ALERT "Hello, world\n">。

8 return 0。

9 }

10

11 static void hello_exit(void>

12 {

13 printk(KERN_ALERT "Goodbye, cruel world\n">。

14 }

15

16 module_init(hello_init>。

17 module_exit(hello_exit>。

模块执行结果:

# insmod hello.ko

Hello, world

# rmmod hello

Goodbye, cruel world

6、带参数的内核模块例子

?可以给模块在加载的时候传递参数

?使用宏MODULE_PARM(变量名,变量类型,权限>来声明参数。变量有如下类型:short、ushort、int、uint、long、ulong、charp、bool ?使用方法:insmod he llp.ko howmany=3 whom=“YangZhu"

1 #include

2 #include

3 #include

4 static char *whom = "world"。

5 static int howmany = 1。

6 module_param(howmany, int, S_IRUGO | S_IWUSR>。

7 module_param(whom, charp, S_IRUGO>。

8

9 static int hello_init(void>

10 {

11 int i。

12 for (i = 0。 i < howmany。 i++>

13 printk(KERN_ALERT "(%d>

Hello, %s\n", i, whom>。

14 return 0。

15 }

16

17 static void hello_exit(void>

18 {

19 printk(KERN_ALERT "howmany is %d, whom is %s\n", howmany, whom>。

20 printk(KERN_ALERT "Goodbye, cruel world\n">。

21 }

22

23 module_init(hello_init>。

24 module_exit(hello_exit>。

模块执行结果:

# insmod hellop.ko

hellop: module license 'unspecified' taints kernel.

(0> Hello, world

# insmod hellop.ko howmany=3 whom="YangZhu"

(0> Hello, YangZhu

(1> Hello, YangZhu

(2> Hello, YangZhu

# rmmod hellop

howmany is 3, whom is YangZhu

Goodbye, cruel world

模块运行期间,参数变量会以文件的形式出现在/sys目录,MODULE_PARM宏中的权限指定了该文件的权限。可以通过改变/sys目录下文件的内容来改变参数变量的值

# insmod hellop.ko howmany=3 whom="YangZhu"

(0> Hello, YangZhu

(1> Hello, YangZhu

(2> Hello, YangZhu

# ls /sys/module/hellop/parameters/ -l

-rw-r--r-- 1 root root 4096 May 3 22:09 howmany

-r--r--r-- 1 root root 4096 May 3 22:09 whom

# cat /sys/module/hellop/parameters/howmany

3

# cat /sys/module/hellop/parameters/whom

YangZhu

# echo 10 >/sys/module/hellop/parameters/howmany

# echo YangYong >/sys/module/hellop/parameters/whom

-sh: cannot create /sys/module/hellop/parameters/whom: Permission denied

# rmmod hellop

howmany is 10, whom is YangZhu

Goodbye, cruel world

7、编程注意事项

?License问题。Linux内核源码以GPL许可发布,模块如果不声明自己使用的license,加载的时候警告。可以使用MODULE_LICENSE(“GPL”>来

避免。

?避免“名字空间污染”:因为模块动态链接到内核里,最好不要输出内核中已有的全局函数或全局变量。否则会后者会影响前者。可以通过查

看 /proc/kallsyms 来查看内核符号列表。解决方法:

o EXPORT_NO_SYMBOLS。使用此宏,这个模块不输出任何符号,除了使用下面的宏定义的符号。

o EXPORT_SYMBOL (name>。使用此宏定义的符号,强制输出。需要在EXPORT_NO_SYMBOLS使用之前使用才能输出。可以输出static

的符号。

?模块之间的依赖问题:有的模块依赖于其他模块的函数或者变量,在加载前需要先加载所依赖的所有模块后,才能成功加载。卸载模块时要先

卸载被依赖的所有模块后,才能成功卸载。

8、集成模块到内核步骤

?使用module_init和module_exit宏来定义内核模块接口函数,并确保模块工作正常

?把模块文件hello.c拷贝到内核的选定目录(例如:drivers/char目录> ?修改选定目录<例如:drivers/char目录)下的Kconfig文件和Makefile文件

o修改Kconfig文件,增加如下:

config HELLO

tristate 'New Hello'

o修改Makefile文件,增加如下:

obj-$(CONFIG_HELLO> += hello.o

?重新配置内核,选中要将该功能编译进内核,而不是编译为模块。

?重新编译内核,如果成功,则得到新内核。

?测试此内核,确保内核模块已集成。

八、查看系统支持的设备

?/proc/devices:系统支持的字符设备驱动和块设备驱动,及其对应的主设备号。

o# cat /proc/devices

Character devices:

1 mem

10 misc

29 fb

400 leds

232 buttons

Block devices:

1 ramdisk

31 mtdblock

254 sbull

?dmesg:查看系统的启动信息。可以看到系统支持的驱动的一些打印信息。

o# dmesg

leds initialized

snull: snull initialized

buttons initialized

snull: enter snull_open

?/proc/ioports(/proc/iomem>:查看设备的IO内存物理地址。

o# cat /proc/iomem

19000300-19000310 : cs8900

19000300-19000310 : cs8900

30000000-33ffffff : System RAM

30024000-30293fff : Kernel text

30294000-302f1f97 : Kernel data

49000000-490fffff : s3c2410-ohci

49000000-490fffff : ohci_hcd

4d000000-4d0fffff : s3c2410-lcd

4e000000-4e0fffff : s3c2440-nand

4e000000-4e0fffff : s3c2440-nand

50000000-50003fff : s3c2440-uart.0

50000000-500000ff : s3c2440-uart

50004000-50007fff : s3c2440-uart.1

50004000-500040ff : s3c2440-uart

50008000-5000bfff : s3c2440-uart.2

50008000-500080ff : s3c2440-uart

52000000-520fffff : s3c2440-usbgadget

53000000-530fffff : s3c2410-wdt

53000000-530fffff : s3c2410-wdt

54000000-540fffff : s3c2440-i2c

54000000-540fffff : s3c2440-i2c

55000000-550fffff : s3c2410-iis

55000000-550fffff : s3c2410-iis

56000010-5600001b : qq2440_leds

56000054-56000057 : qq2440_button34

56000064-56000067 : qq2440_button12

57000000-570000ff : s3c2410-rtc

57000000-570000ff : s3c2410-rtc

f0300000-f03fffff : s3c2410-lcd

?/proc/interrupts:查看正在使用的中断号

# cat /proc/interrupts

CPU0

16: 4 s3c-ext0 KEY4

18: 0 s3c-ext0 KEY3

30: 461932 s3c S3C2410 Timer Tick 32: 0 s3c s3c2410-lcd

34: 0 s3c I2SSDI

35: 0 s3c I2SSDO

42: 0 s3c ohci_hcd:usb1 43: 0 s3c s3c2440-i2c

53: 11257 s3c-ext eth0

55: 0 s3c-ext KEY2

63: 0 s3c-ext KEY1

70: 990 s3c-uart0 s3c2440-uart

71: 2206 s3c-uart0 s3c2440-uart

83: 0 - s3c2410-wdt 九、Linux驱动相关代码放在drivers/目录下,常见驱动目录介绍

?block/:常见块设备驱动。

?char/,serial/:虚拟终端,串口。

?net/:网络设备驱动。

?video:VGA和framebuffer设备驱动。

?Ide/,scsi/:IDE和SCSI设备驱动。

顶层目录的子目录sound/:声卡驱动。

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字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用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驱动程序的数据结构 (1) 二、设备节点如何产生? (2) 三、应用程序是如何访问设备驱动程序的? (2) 四、为什么要有设备文件系统? (3) 五、设备文件系统如何实现? (4) 六、如何使用设备文件系统? (4) 七、具体设备驱动程序分析 (5) 1、驱动程序初始化时,要注册设备节点,创建子设备文件 (5) 2、驱动程序卸载时要注销设备节点,删除设备文件 (7) 参考书目 (8) 一、linux驱动程序的数据结构 设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。 各种设备由于功能不同,驱动程序提供的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。 所以,一种设备的驱动程序主要内容就是提供这样的一组file_operations 接口函数。 那么,linux是如何管理种类繁多的设备驱动程序呢? linux下设备大体分为块设备和字符设备两类。 内核中用2个全局数组存放这2类驱动程序。 #define MAX_CHRDEV 255 #define MAX_BLKDEV 255 struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; //此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。

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 *);

PCI驱动编程基本框架

Linux将所有外部设备看成是一类特殊文件,称之为“设备文件”,如果说系统调用是Linux 内核和应用程序之间的接口,那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样来操作外部设备。 1. 字符设备和块设备 Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第一个IDE硬盘使用/dev/hda表示。每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。 在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来,PCI卡通常都属于字符设备。 2. 设备驱动程序接口 Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的数据结构file_operations来完成的: struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

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 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

Windows驱动程序开发环境配置

Windows驱动程序开发笔记 一、WDK与DDK环境 最新版的WDK 微软已经不提供下载了这里:https://https://www.doczj.com/doc/502568516.html,/ 可以下并且这里有好多好东东! 不要走进一个误区:下最新版的就好,虽然最新版是Windows Driver Kit (WDK) 7_0_0,支持windows7,vista 2003 xp等但是它的意思是指在windows7操作系统下安装能编写针对windows xp vista的驱动程序, 但是不能在xp 2003环境下安装Windows Driver Kit (WDK) 7_0_0这个高版本,否则你在build的时候会有好多好多的问题. 上文build指:首先安装好WDK/DDK,然后进入"开始"->"所有程序"->"Windows Driver Kits"->"WDK XXXX.XXXX.X" ->"Windows XP"->"x86 Checked Build Environment"在弹出来的命令行窗口中输入"Build",让它自动生成所需要的库 如果你是要给xp下的开发环境还是老老实实的找针对xp的老版DDK吧,并且xp无WDK 版只有DDK版build自己的demo 有个常见问题: 'jvc' 不是内部或外部命令,也不是可运行的程序。 解决办法:去掉build路径中的空格。 二、下载 WDK 开发包的步骤 1、访问Microsoft Connect Web site站点 2、使用微软 Passport 账户登录站点 3、登录进入之后,点击站点目录链接 4、在左侧的类别列表中选择开发人员工具,在右侧打开的类别:开发人员工具目录中找到Windows Driver Kit (WDK) and Windows Driver Framework (WDF)并添加到您的控制面板中 5、添加该项完毕后,选择您的控制面板,就可以看到新添加进来的项了。 6、点击Windows Driver Kit (WDK) and Windows Driver Framework (WDF),看到下面有下载链接,OK,下载开始。下载后的文件名为: 6.1.6001.18002.081017-1400_wdksp-WDK18002SP_EN_DVD.iso将近600M大小。

Linux设备驱动程序简介

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

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

如何实现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驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

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字节。块设备的存取是通过

Windows驱动程序框架理解_经典入门

标题: 【原创】Windows驱动程序框架 windows驱动程序入门比较坑爹一点,本文旨在降低入门的门槛。注:下面的主要以NT式驱动为例,部分涉及到WDM驱动的差别会有特别说明。 首先,肯定是配置好对应的开发环境啦,不懂的就百度下吧,这里不再次描述了。 在Console控制台下,我们的有一个入口函数main;在Windows图形界面平台下,有另外一个入口函数Winmain。我们只要在这入口函数里面调用其他相关的函数,程序就会按照我们的意愿跑起来了。在我们用IDE开发的时候,也许你不会发现这些细微之处是如何配置出来的,一般来说我们也不用理会,因为在新建工程的时候,IDE已经帮我们把编译器(Compiler)以及连接器(Linker)的相关参数设置好,在正式编程的时候,我们只要按照规定的框架编程就行了。 同样,在驱动程序也有一个入口函数DriverEntry,这并不是一定的,但这是微软默认的、推荐使用的。在我们配置开发环境的时候我们有机会指定入口函数,这是链接器的参数/entry:"DriverEntry"。 入口函数的声明 代码: DriverEntry主要是对驱动程序进行初始化工作,它由系统进程(System)创建,系统启动的时候System系统进程就被创建了。 驱动加载的时候,系统进程将会创建新的线程,然后调用执行体组件中的对象管理器,创建一个驱动对象(DRIVER_OBJECT)。另外,系统进程还得调用执行体组件中的配置管理程序,查询此驱动程序在注册表中对应项。系统进程在调用驱动程序的Driv erEntry的时候就会将这两个值传到pDriverObject和pRegistryPath。 接下来,我们介绍下上面出现的几个数据结构: typedef LONG NTSTATUS 在驱动开发中,我们应习惯于用NTSTATUS返回信息,NTSTATUS各个位有不同的含义,我们可以也应该用宏NT_SUCCESS来判断是否返回成功。 代码: NTSTAUS的编码意义: 其中 Ser是Serviity的缩写,代表严重程度。 00:成功01:信息10:警告11:错误 C是Customer的缩写,代表自定义的位。

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__

相关主题
文本预览
相关文档 最新文档