嵌入式linux系统设计与应用 课件 第八章 嵌入式Linux驱动程序_第1页
嵌入式linux系统设计与应用 课件 第八章 嵌入式Linux驱动程序_第2页
嵌入式linux系统设计与应用 课件 第八章 嵌入式Linux驱动程序_第3页
嵌入式linux系统设计与应用 课件 第八章 嵌入式Linux驱动程序_第4页
嵌入式linux系统设计与应用 课件 第八章 嵌入式Linux驱动程序_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

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

文档简介

第八章嵌入式Linux驱动程序第八章嵌入式Linux驱动程序(1)设备驱动开发概述驱动程序处理过程/设备驱动程序框架

(2)内核设备模型功能/Sysfs和实现机制/Platform总线/设备树(3)字符设备驱动程序关键数据结构/驱动框架/实例(4)网络设备驱动程序驱动程序框架/关键数据结构/设计方法/实例ARM-Linux驱动程序的4种经典方法1.mmap映射型设计方法(单片机工程师的最爱)2.传统file_operatiopns设计方法(盛久不衰,沿用至今)3.platform总线型设计方法(2.6版本主流)4.设备树(内核3.x版本后流行)

设备驱动程序开发概述PartOne8.1Linux设备驱动程序概述

作为Linux内核的重要组成部分,设备驱动程序主要完成以下的功能:(1)对设备初始化和释放。(2)把数据从内核传送到硬件和从硬件读取数据。(3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据。(4)检测错误和处理中断。Linux设备驱动程序可以分为两个主要组成部分:(1)对子程序进行自动配置和初始化,检测驱动的硬件设备是否正常,能否正常工作。(2)设备服务子程序和中断服务子程序,这两者分别是驱动程序的上下两部分。驱动上部分即设备服务子程序的执行是系统调用的结果,并且伴随着用户态向核心态的演变,在此过程中还可以调用与进程运行环境有关的函数,比如sleep()函数。驱动程序的下半部分即中断服务子程序。Linux设备驱动程序分类

1.字符设备字符设备是传输数据以字符为单位进行的设备,字符设备驱动程序通常实现open、close、read和write等系统调用函数,常见的字符设备有键盘、串口、控制台等。通过文件系统节点可以访问字符设备,例如/dev/tty1和/dev/lp1。2.块设备所谓块设备是指对其信息的存取以“块”为单位。块设备和字符设备一样可以通过文件系统节点来访问。在大多数linux系统中,只能将块设备看作多个块进行访问,一个块设备通常是1024B数据。3.网络设备网络设备驱动通常是通过套接字(Socket)等接口来实现操作。任何网络事务处理都可以通过接口来完成和其他宿主机数据的交换。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核的通信是完全不同的。8.1.1Linux设备驱动程序分类1.字符设备字符设备是传输数据以字符为单位进行的设备,字符设备驱动程序通常实现open、close、read和write等系统调用函数,常见的字符设备有键盘、串口、控制台等。通过文件系统节点可以访问字符设备,例如/dev/tty1和/dev/lp1。字符设备和普通文件系统之间唯一的区别是普通文件允许往复读写,而大多数字符设备驱动仅是数据通道,只能顺序读写。此外,字符设备驱动程序不需要缓冲且不以固定大小进行操作,它与用户进程之间直接相互传输数据。2.块设备所谓块设备是指对其信息的存取以“块”为单位。如常见的光盘、硬磁盘、软磁盘、磁带等,块长大小通常取512B、1024B或4096B等。块设备和字符设备一样可以通过文件系统节点来访问。在大多数linux系统中,只能将块设备看作多个块进行访问,一个块设备通常是1024B数据。块设备的特点是对设备的读写是以块为单位的,并且对设备的访问是随机的。块设备和字符设备的区别主要在于内核内部的管理上,其中应用程序对于字符设备的每个I/O操作都会直接传递给系统内核对应的驱动程序;而应用程序对于块设备的操作要经过系统的缓冲区管理间接地传递给驱动程序处理。

3.网络设备网络设备驱动通常是通过套接字(Socket)等接口来实现操作。任何网络事务处理都可以通过接口来完成和其他宿主机数据的交换。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核的通信是完全不同的。1.内存与I/O端口编写驱动程序大多数情况下其本质都是对内存和I/O端口的操作。(1)内存Linux通常有以下几种地址类型:用户虚拟地址物理地址总线地址内核逻辑地址内核虚拟地址(2)I/O端口有两个重要的内核调用可以保证驱动程序使用正确的端口,它们定义在include/linux/ioport.h中。int__check_region(structresource*,resource_size_t,resource_size_t);该函数的作用是查看系统I/O表,看是否有别的驱动程序占用某一段I/O口。structresource*__request_region(structresource*, resource_size_tstart, resource_size_tn, constchar*name,intflags);根据CPU系统结构的不同,CPU对I/O端口的编址方式通常有两种:第一种是I/O映射方式,如x86处理器为外设专门实现了一个单独的地址空间,称为I/O地址空间,CPU通过专门的I/O指令来访问这一空间的地址单元;第二种是内存映射方式,RSIC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为了内存的一部分,此时CPU访问I/O端口就像访问一个内存单元,不需要单独的I/O指令。这两种方式在硬件实现上的差异对软件来说是完全可见的。2.并发控制

在驱动程序中经常会出现多个进程同时访问相同的资源时可能会出现竞态(racecondition),即竞争资源状态,因此必须对共享资料进行并发控制。Linux内核中解决并发控制最常用的方法是自旋锁(spinlocks)和信号量(semaphores)。(1)自旋锁自旋锁是一个互斥现象的设备,它只能是两个值:locked(锁定)或unlocked(解锁)。它通常作为一个整型值的单位来实现。在任何时刻,自旋锁只能有一个保持者,也就是说在同一时刻只能有一个进程获得锁。(2)信号量信号量是一个结合一对函数的整型值,这对函数通常称为P操作和V操作。自旋锁和信号量有很多相似之处但又有些本质的不同。其相同之处主要有:首先它们对互斥来说都是非常有用的工具;其次在任何时刻最多只能有一个线程获得自旋锁或信号量。不同之处主要有:首先自旋锁可在不能睡眠的代码中使用,如在中断服务程序(ISR)中使用,而信号量不可以;其次自旋锁和信号量的实现机制不一样;最后通常自旋锁被用在多处理器系统。总体而言,自旋锁通常适合保持时间非常短的情况,它可以在任何上下文中使用,而信号量用于保持时间较长的情况,只能在进程上下文中使用。3.阻塞与非阻塞在驱动程序的处理过程中我们提到了阻塞的概念,这里进行以下说明。阻塞(blocking)和非阻塞(nonblocking)是设备访问的两种不同模式,前者在I/O操作暂时不可进行时会让进程睡眠,而后者在I/O操作暂时不可进行时并不挂起进程,它或者放弃,或者不停地查询,直到可以进行操作为止。(1)阻塞与非阻塞操作阻塞操作是指在执行设备操作时,若不能获得资源则进程挂起,直到满足可操作的条件再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列中移走,直到等待条件被满足。非阻塞操作是在不能进行设备操作时并不挂起,它会立即返回,使得应用程序可以快速查询状态。(2)异步通知异步通知是指一旦设备准备就绪,则该设备会主动通知应用程序,这样应用程序就不需要不断地查询设备状态,通常把异步通知称为信号驱动的异步I/O(SIGIO),这有点类似于硬件上的中断。中断处理Linux将中断分为两个部分:上半部分(tophalf)和下半部分(bottomhalf)。上半部分的功能是注册中断,下半部完成了中断处理程序的大部分工作中断的Bottom-half机制,包括了softirq(软中断)、tasklet(任务队列)、workqueue(工作队列)等,任务队列(tasklet)下面是tasklet的定义:structtasklet_struct{ structtasklet_struct*next;//指向下一个tasklet unsignedlongstate;//tasklet的状态 atomic_tcount;//计数,1表示禁止 void(*func)(unsignedlong);//处理函数指针 unsignedlongdata;//处理函数参数};在interrupt.h中可以看到tasklet的数据结构,其状态定义了两个位的含义:enum{TASKLET_STATE_SCHED,/*正在运行*/ TASKLET_STATE_RUN /*已被调度,准备运行*/};工作队列(workqueue)工作队列的queue核心代码如下所示。staticvoidinsert_work(structcpu_workqueue_struct*cwq,

structwork_struct*work,structlist_head*head,

unsignedintextra_flags){

structglobal_cwq*gcwq=cwq->gcwq;

set_work_cwq(work,cwq,extra_flags);

list_add_tail(&work->entry,head);

//head参数表示的是每cpu一个的全局队列

if(__need_more_worker(gcwq))//如果是诸如高优先级之类的工作或者当前已经没有//空闲的工作者了,那么唤醒一个工作者,系统起码要保持一个空闲的工作者进程以备用。

wake_up_worker(gcwq);}设备号在linux2.6内核中,主从设备被定义为一个dev_t类型的32位数,其中前12位表示主设备号,后20位表示从设备号。另外,在include/linux/kdev.h中定义了如下的几个宏来操作主从设备号。#defineMAJOR(dev) ((unsignedint)((dev)>>MINORBITS))#defineMINOR(dev) ((unsignedint)((dev)&MINORMASK))#defineMKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))上述宏分别实现从32位dev_t类型数据中获得主设备号、从设备号及将主设备号和从设备号转换为dev_t类型数据的功能。设备驱动程序框架Linux的设备驱动程序可以分为以下部分。(1)驱动程序与内核的接口,这是通过关键数据结构file_operations来完成的。(2)驱动程序与系统引导的接口,这部分利用驱动程序对设备进行初始化。(3)驱动程序与设备的接口,描述了驱动程序如何与设备进行交互,这与具体设备密切相关。根据功能划分,设备驱动程序代码通常可分为以下几个部分:(1)驱动程序的注册与注销(2)设备的打开与释放:引用计数(3)设备的读写操作(4)设备的控制操作(5)设备的轮询和中断处理(2)设备的打开与释放打开设备是由调用定义在incliude/linux/fs.h中的file_operations结构体中的open()函数完成的。open()函数主要完成的主要工作:增加设备的使用计数。检测设备是否异常,及时发现设备相关错误,防止设备有未知硬件问题。若是首次打开,首先完成设备初始化。读取设备次设备号。其函数原型如下:int(*open)(structinode*,structfile*);驱动程序的加载

通常linux驱动程序可通过两种方式进行加载:一种是将驱动程序编译成模块形式进行动态加载,常用命令有insmod(加载)、rmmod(卸载)等;另一种是静态编译,即将驱动程序直接编辑放进内核。动态加载模块设计使Linux内核功能更容易扩展。而静态编译方法对于在要求硬件只是完成比较特定、专一的功能的一些嵌入式系统中,具有更高的效率。以网卡DM9000为例说明驱动程序的加载过程

内核设备模型PartTwo8.2内核设备模型

在Linux2.6内核及后续版本中,设备模型为设备驱动程序管理、描述设备抽象数据结构之间关系等提供了一个有效的手段,其主要功能包括

:(1)电源管理和系统关机(2)与用户空间通信(3)热插拔(hotplug)设备管理(4)设备类型管理(5)对象生命周期处理linux内核各目录代码量对比linux内核设备模型带来的好处十分之多。首先linux设备模型是一个具有清晰结构的组织所有设备和驱动的树状结构,用户就可以通过这棵树去遍历所有的设备,建立设备和驱动程序之间的联系;其次,Linux驱动模型把很多设备共有的一些操作抽象出来,大大减少重复开发的可能;再次,Linux设备模型提供了一些辅助的机制,比如引用计数,让开发者可以安全高效的开发驱0动程序。同时,Linux设备模型还提供了一个非常有用的虚拟的基于内存的文件系统sysfs。Sysfs解释了内核数据结构的输出、属性以及它们之间及用户空间的连接关系。sysfs

sysfs给用户提供了一个从用户空间去访问内核设备的方法,它在Linux里的路径是/sys。这个目录并不是存储在硬盘上的真实的文件系统,只有在系统启动之后才会建起来。可以使用tree/sys这个命令显示sysfs的结构。由于信息量较大,这里只列出第一层目录结构:/sys|--block|--bus|--class|--dev|--devices|--firmware|--fs|--kernel|--module`--power如果只是单纯要访问设备,一般很少会直接操作sysfs,因为sysfs非常繁琐和底层化现象严重,大部分情况下可以使用更加方便的DeviceKit或者libudev。Block目录从块设备的角度来组织设备,其下的每个子目录分别对应系统中的一个块设备;值得注意的是sys/block目录从内核2.6.26已经正式转移到sys/class/block中。Sys/block目录虽然为了向后兼容保持存在,但是其中的内容已经变为指向它们在sys/devices/中真实设备的符号链接文件。Bus目录从系统总线这个角度来组织设备,内核设备按照总线类型分层放置的目录结构,它是构成linux统一设备模型的一部分。Class目录把看以类别的角度看待设备,比如PCI设备或者USB设备等,该目录是按照设备功能分类的设备模型,是linux统一设备模型的一部分。Dev目录下维护一个按照字符设备或者块设备的设备号链接到硬件设备的符号链接,在内核2.6.26首次引入。Devices目录是所有设备的大本营,系统中的任一设备在设备模型中都由一个device对象描述,是sysfs下最重要的目录。该目录结构就是系统中实际的设备拓扑结构。Firmware目录包含了一些比较低阶的子系统,比如ACPI、EFI等,是系统加载固件机制的对用户空间的接口。Fs目录里列出的是系统支持的所有文件系统,但是目前只有fuse、gfs2等少数文件系统支持sysfs接口。Kernel目录下包含的是一些内核的配置选项,如slab分配器等。Modules目录下包含的是所有内核模块的信息,内核模块实际上和设备之间存在对应联系,通过这个目录可以找到设备。Power目录存放的是系统电源管理的数据,用户可以通过它来查询目前的电源状态,甚至可以直接“命令”系统进入休眠等省电模式。sysfs的实现机制kobject

“内核对象”(kernel

object)的设备管理机制,该机制是基于一种底层数据结构,通过这个数据结构,可以使所有设备在底层都具有一个公共接口,便于设备或驱动程序的管理和组织。从面向对象的角度来说,kobject可以看作是所有设备对象的基类。Kobject结构struct

kobject{

const

char

*name;//指向设备名称的指针struct

list_head

entry;//挂接到所在kset中去的单元struct

kobject

*parent;//指向父对象的指针struct

kset

*kset;//所属kset的指针struct

kobj_type

*ktype;//指向其对象类型描述符的指针struct

sysfs_dirent*sd;//指示在sysfs中的目录项struct

kref

kref;//对象引用计数unsignedint

state_initialized:1;//标记:初始化unsignedint

state_in_sysfs:1;//标记在sysfs中;

。。。。。。}kref的定义非常简单,其结构体里只有一个原子变量。struct

kref{

atomic_trefcount;};Ktype域是一个指向kobj-type结构的指针,表示该对象的类型。

struct

kobj_type{

void

(*release)(struct

kobject*kobj);

const

struct

sysfs_ops*sysfs_ops;

struct

attribute**default_attrs;};Ksetkobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合Kset的定义如下:struct

kset{

struct

list_headlist;//用于连接该kset中所有kobject的链表头

spinlock_tlist_lock;//迭代时用的锁

struct

kobjectkobj;//指向代表该集合基类的对象

const

struct

kset_uevent_ops*uevent_ops;//指向一个用于处理集合中kobject对象的热插拔结构操作的结构体};设备模型的组织-platform总线

Platform总线就是从2.6

内核开始引入的一种虚拟总线,主要用来管理CPU的片上资源,具有更好的移植性。目前,大部分的驱动都是用Platform总线编写的,除了极少数情况之外如构建内核最小系统之内的而且能够采用CPU存储器总线直接寻址的设备。Platform总线模型主要包括platform_device、platform_bus、platform_driver三个部分。Platform总线模型的platform_driver机制将设备的本身资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过标准接口进行申请和使用,具有很高的安全性和可靠性。而模型中的platform_device是一个具有自我管理功能的子系统。当platform模型中总线上有设备,又有驱动的时候,就会进行设备与驱动匹配的过程,总线起到了沟通设备和驱动的桥梁作用。设备树设备树的主要作用:一是平台标识,所谓平台标识就是板级识别,让内核知道当前使用的是哪个开发板,这里识别的方式是根据root节点下的compatible字段来匹配。二是运行时配置,比如在内核启动的时候ramdisk的配置,比如bootargs的配置,ramdisk的起始和结束地址。三是设备信息集合,这也是最重要的信息,集合了各种设备控制器设备树设备树用树状结构描述设备信息,它有以下特性:每个设备树文件都有一个根节点,每个设备都是一个节点。节点间可以嵌套,形成父子关系,这样就可以方便地描述设备间的关系。每个设备的属性都用一组key-value对(键值对)来描述。每个属性的描述用“。”表示结束。/{//根节点node1{//node1是节点名,是板子的子节点key=value;//node1的属性...node2{

//node2是node1的子节点key=value;//node2的属性...}}字符设备驱动设计框架PartThree8.38.3.1字符设备的重要数据结构字符设备驱动程序编写通常都要涉及到三个重要的内核数据结构,分别是file_operations结构体、file结构体和inode结构体。File_operations为用户态应用程序提供接口,是系统调用和驱动程序关联的重要数据结构。File结构体在内核代码include/linux/fs.h中定义,表示一个抽象的打开的文件,file_operations结构体就是file结构的一个成员。Inode结构表示一个文件,而file结构表示一个打开的文件。这正是二者间最重要的关系。每个进程为每个打开的文件分配一个文件描述符,每个文件描述符对应一个file结构,同一个文件被不同的进程打开后,在不同的进程中会有不同的file文件结构,其中包括了文件的操作方式(只读\只写\读写),偏移量,以及指向inode的指针等等。这样,不同的file结构指向了同一个inode节点。这里介绍一下字符设备的分配和初始化,它有两种不同的方式。cdev_alloc()函数用于动态分配一个新的cdev结构体并初始化。一般如果建立新的cdev结构体可以使用该方式,这里给出一个参考代码:structcdev*my_cdev=cdev_alloc();my_cdev->owner=THIS_MODULE;my_cdev->ops=&fops;如果需要把cdev结构体嵌入到指定设备结构中,可以采用静态分配方式。cdev_init()函数可以初始化一个静态分配的cdev结构体,并建立cdev和file_operation之间的连接。与cdev_alloc()唯一不同的是,cdev_init()函数用于初始化已经存在的cdev结构体。这里给出一段参考代码:structcdevmy_cdev;cdev_init(&my_cdev,&fops);my_cdev.owner=THIS_MODULE;字符设备驱动程序的重要数据结构

structfile_operations{ structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);

long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);……file结构体使用open打开文件时,传入的flags、mode等参数会被记录在内核中对应的structfile结构体里(f_flags、f_mode)打开字符设备节点时,内核中也有对应的structfile注意这个结构体中的结构体:structfile_operations*f_op,这是由驱动程序提供的。8.3.2字符设备驱动框架字符设备驱动程序的初始化流程一般可以用如下的过程来表示:(1)定义相关的设备文件结构体(如file_operation()中的相关成员函数的定义)。(2)向内核申请主设备号(建议采用动态方式)。(3)申请成功后,通过调用MAJOR()函数获取主设备号。(4)初始化cdev的结构体,可以通过调用cdev_init()函数实现。(5)通过调用cdev_add()函数注册cdev到内核。(6)注册设备模块,主要使用module_init()函数和module_exit()函数。编写一个字符设备的驱动程序,首先要注册一个设备号。内核提供了三个函数来注册一组字符设备编号,这三个函数分别是:alloc_chrdev_region()、register_chrdev_region()和register_chrdev()。其中register_chrdev()在上节已经介绍过。这里首先介绍的是alloc_chrdev_region()函数,该函数用于动态申请设备号范围,通过指针参数返回实际分配的起始设备号。Register_chrdev_region()函数用于向内核申请分配已知可用的设备号(次设备号通常为0)范围。下面是该函数原型:intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)。参数from是要分配的设备号的dev_t类型数据,表示了要分配的设备编号的起始值,参数count表示了允许分配设备编号的范围。register_chrdev()是一个老版本内核的设备号分配函数,不过新内核对其还是兼容的。Register_chrdev()兼容了动态和静态两种分配方式。Register_chrdev()不仅分配了设备号,同时也注册了设备。这是register_chrdev()与前两个函数的最大区别。也就是说,如果使用alloc_chrdev_region()或register_chrdev_region()分配设备号,还需要对cdev结构体初始化。而register_chrdev()则把对cdev结构体的操作封装在了函数的内部。所以在一般的字符设备驱动程序中,不会看到对cdev的操作。与注册分配字符设备编号的方法类似,内核提供了两个注销字符设备编号范围的函数unregister_chrdev_region()和unregister_chrdev()。这两个函数实际上都调用了__unregister_chrdev_region()函数,原理是一样的。Register_chrdev()函数封装了cdev结构的操作,而alloc_chrdev_region()或register_chrdev_region()只提供了设备号的注册,并未真正的初始化一个设备,只有cdev这个表示设备的结构体初始化了,才可以说设备初始化了。字符设备驱动框架staticunsignedintxxx_open(){…}staticunsignedintxxx_ioctl(){

…}structfile_operationsfops={.owner=THIS_MODULE,.open=xxx_open,.ioctl=xxx_ioctl,//注意新式写法这里应是

.unlocked_ioctl=xxx_ioctl};staticint__initxxx_init(void){...register_chrdev(xxx_dev_no,DEV_NAME,&fops);}staticvoid__exitxxx_exit(void){unregister_chrdev(xxx_dev_no,DEV_NAME);...}module_init(xxx_init);module_exit(xxx_exit);字符设备驱动程序设计

试验程序编写应用程序调用open函数打开hello_drv这个设备,打开以后可以使用write函数向hello_drv的写缓冲区writebuf中写入数据(不超过100个字节),也可以使用read函数读取读缓冲区readbuf中的数据操作,操作完成以后应用程序使用close函数关闭chrdevbase设备。Makefile测试程序编写驱动编写好以后是需要测试的,一般编写一个简单的测试程序,测试程序运行在用户空间。测试程序很简单通过输入相应的指令来对hello_drv设备执行读或者写操作。I2C总线驱动图8-7Linux内核I2C总线驱动框架Linux内核提供了一个通用的方法来实现I2C设备驱动。这个通用的设备驱动由I2C_dev.c文件实现。在/drivers/I2C目录下

----Algos/

一些I2C总线适配器通信的算法

----Busses/

I2C总线驱动的方法

----Chips/

I2C设备驱动

----I2C-boardinfo.c

----I2C-core.c

I2C核心文件,用于联系设备驱动和总线驱动,重要函数为I2C_add_addapter、I2C_add_driver和I2C_transfer函数

----I2C-dev.c

通用的I2C设备驱动

----Kconfig

----MakefileLinux内核的I2C总线驱动程序框架如图8-7所示。I2C总线驱动程序主要由3个部分组成:I2Ccore(I2C核心),adapter(适配器),client(设备驱动)

。(1)I2Ccore(I2C核心)是I2C总线驱动程序体系结构的核心,它为总线设备驱动提供统一的接口,通过这些接口来访问在特定I2C设备驱动程序中实现的功能,并实现从I2C

总线驱动体系结构中添加和删除总线驱动的方法等。(2)adapter部分代表I2C适配器驱动,adapter是各个适配器驱动所构成的集合,主要实现各相应适配器数据结构I2C_adapter的具体的通信传输算法(I2C_algorithm),此算法管理I2C控制器及实现总线数据的发送接收等操作。(3)Client部分则代表挂载在I2C总线上的设备驱动,Client部分是各个I2C设备构成的集合,主要实现各描述I2C设备的数据结构I2C_client及其私有部分,并通过I2Ccore提供的接口实现设备的注册,提供设备可使用的地址范围及地址检测成功后的回调函数。I2C总线驱动处于控制中心的I2Ccore实现了控制策略,具体I2C总线的适配器和设备的驱动实现了具体设备可用的机制,控制策略和底层机制通过中间的函数接口相联系。正是中间的函数接口使得控制策略与底层机制无关,从而使得控制策略具有良好的可移植性和重用性。在实际设计中,I2C核心提供的接口不需要修改,只需针对目标总线适配器驱动和设备驱动进行必要修改即可。块设备驱动程序块设备驱动整体框架嵌入式网络设备驱动设计PartFour8.4嵌入式网络设备驱动

嵌入式Linux的网络系统主要采用socket机制,操作系统和驱动程序之间定义专门的数据结构sk_buff用来进行数据包的发送与接收。Sk_buff数据结构Linux内核对sk_buff的操作有分配、释放、变更等。(1)分配操作structsk_buff*alloc_skb(unsignedintlen,gfp_tpriority);structsk_buff*dev_alloc_skb(unsignedintlen);(2)释放操作voidkfree_skb(structsk_buff*skb);voiddev_kfree_skb(structsk_buff*skb);voiddev_kfree_skb_irq(structsk_buff*skb);voiddev_kfree_skb_any(structsk_buff*skb);

温馨提示

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

最新文档

评论

0/150

提交评论