驱动程序设计基础教程

为什么要了解驱动设计?

开发新的产品。若有新的外设,需要开发驱动。不同的平台需要移植

Android近几年的发展,促使驱动行业的发展

开发难度高,待遇高

什么是外部设备

无论片内片外,CPU核以外的设备都可称为外部设备,因为你都需要写程序去操纵。

关于外部设备控制

嵌入式系统的几乎每种操作,最后都需要映射到实际的物理设备上。

除处理器、内存和少数其他实体外,几乎所有设备的控制操作都由设备相关的驱动程序来实现。

嵌入式系统开发平台功能成熟度的重要指标:是否具有大量、丰富的设备驱动及BSP(板级支持包)支持。

设备驱动程序的重要性

设备驱动程序往往工作于系统内核状态,因而,其运行性能、、可靠性制约着应用系统的性能和可靠性。

由于设备驱动错误,导致操作系统崩溃约占30~40%

对于嵌入式Linux

内核发行包一般支持内存、网卡、显卡、硬盘等常规设备

研究范围,是指内核发行包一般不支持的外设

移植

新设备

设备与CPU的信息交换

外部设备与CPU的通讯信息,主要包括

控制信息:告诉要进行什么处理

状态信息:告诉设备的状态

数据信息:不同的设备,传输数据类型及编码各异。

输入、输出方式控制

与外部设备的输入/输出的方式,主要包括:

直接I/O方式:通过指令直接对端口进行输入、输出控制。如X86的in、out指令。

内存映射方式:可以访问较大的地址空间,实现快速数据交换,如:ISA总线映射的空间为OxC8000~0xEFFF。

中断方式:采用中断实现数据的输入/输出。

DMA方式:采用DMA控制器,实现数据传输。

数据量不大的情况下,如字符设备,常采用中断方式;

大量数据传输的I/O设备,如磁盘、网卡等设备,常采用DMA方式,以减少CPU资源的占用。

什么是驱动程序

每个设备控制器都有一个或多个寄存器用来接收命令或设备的状态,它们通过读写命令按址存取。(通常有数据寄存器、状态、操作方式寄存器)

不同的设备寄存器的个数和命令含义都不同——设备驱动程序是操作系统中唯一知道控制器有几个寄存器及它们用途的程序。

驱动程序程序是指挥硬件工作的软件。它是应用程序与硬件之间的一个中间层,为应用程序屏蔽硬件的细节。

驱动程序的功能是什么?

驱动程序是内核的一部分

对设备的初始化和释放

把数据从内核传到硬件/从硬件读数据到内核

读取应用程序传送给设备文件的数据和回送应用程序请求的数据。这需要在用户控件,内核空间,总线以及外设之间传输数据

检测和处理设备出现的错误

驱动程序与应用程序的区别

Linux中以模块的形式加载各种驱动程序:

主动与被动的区别。应用程序有一个main函数,总是从些函数开始主动执行一个任务,而驱动程序安装以后,便停止工作,并等待被应用程序调用。

使用的库函数不同。

程序运行的区域不同。驱动程序工作在内核状态;应用程序工作在用户态。

设备分类

Linux支持三类硬件设备

字符设备:无需缓冲直接读写

块设备:通过buffer或cache进行读写

支持块的随机访问

网络设备:通过BSD套接口访问

字符设备

能够像字节流一样被访问的设备,提供数据通道,不允许来回读写,在数据传输以字符为单位进行传输。

例如:串口、鼠标、终端、打印机

字符设备设备特点:

在数据传输中,可以传输任意大小的字符数据;

可以通过文件方式(/tyCo/0)访问,但只能顺序访问数据通道;

块设备

以数据“块”为单位,对数据进行来回读写/存取的设备。1个数据块可包括512B、1KB数据。块设备可以容纳文件系统:

块设备特点:硬盘、U盘、内存、Flash、CD-ROM等。

块设备特点:

在数据传输中, 传输单位是固定大小的数据块。

块设备的存取时通过buffer、cache来进行。

可以随机访问块设备中存取的数据块。

块设备不直接与I/O系统连接,而是通过与文件系统关联,提供接口。

网络接口设备

网络设备,又称网络接口,用于网络通信。通常它们指的是硬件设备,但有时也可以是一个纯软件设备(如回环接口loopback)

与不同I/O设备不同,网络设备没有对应的设备文件,数据通信不是基于的标准的I/O系统接口,而是基于BSD套接口访问,例如:socket、bind、listen、accept、send等系统调用。

在Linux中,采用给网络接口设备分配一个唯一的名字和方法来访问该设备。如:eth0 eth1 lo

设备文件和设备号

设备文件

Linux抽象了对硬件设备的访问,可作为普通文件一样访问,使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作。

每个设备文件都有对应的两个设备号

主设备号。标识设备的种类,使用的驱动程序

2.4内核下是8位,2.6及以后内核为12位

次设备号,标识使用同一设备驱动程序的不同硬件设备

2.4内核下是8位,2.6及以后内核为20位

设备文件在Linux的什么地方?

Linux应用程序是通过设备文件(又名:设备节点)来使用驱动程序操作字符设备和块设备

/dev是存放设备文件的

字符设备用c表示;块设备用b表示;设备文件一般命名为“设备名+数字或字母”

设备文件、主设备号和驱动程序之间的关系?

应用程序如何使用设备文件?使用文件名

设备文件与驱动程序如何建立联系? 主设备号

(1)安装驱动,注册主设备号

(2)创建设备文件,指向该设备类型,及主次设备号

(3)应用程序,调用设备文件

主设备号+次设备号

主设备号用于标识设备对应的驱动程序,主设备号相同的设备使用相同的设备驱动程序。一个主设备号可能有多个设备与之对应,这些设备在驱动程序类可以通过次设备号来进一步区分。

例如,软驱的设备号是2,IDE硬盘的主设备号是3,并口的主设备号是6

代码分布

驱动源代码都放在Driver目录中

block

char

cdrom

pci

scsi

net

sound

Linux设备驱动的特点

核心代码:设备驱动程序是内核的一部分(可靠性)

核心接口:设备驱动程序必须为Linux核心或其从属子系统提供一个标准接口;

核心机制:可以使用标准的核心服务等;

动态可加载:加载请求时进行加载,不使用设备时卸载

可配置:编译内核时,可配置进内核。

设备文件接口

Linux应用程序可以通过设备文件的一组固定的入口点来访问驱动程序,这组入口点是由每个设备的驱动程序提供的。

系统试图使它对各类设备的输入、输出看起来就像普通文件一样。因此,把设备映射为一种特殊恶文件。

程序:编写应用程序实现向串口发送字符串“ATD2109992”

int main(){ int fd,n; char buf[MAX]="ATD2109992"; fd = open("/dev/ttyS0",O_RDWR); //open入口点,ttyS0是设备文件 if(fd<0){ perror("Unable opne/dev/ttyS0n"); return 1; } n = write(fd,buf,strlen(buf));//write入口点 if(n<0){ print("write() of %d bytes failed!n",strlen()buf); }else{ print("write() of %d bytes ok!n",strlen(buf)); } close(fd); //close入口点}

file_operations

struct file_operations{ //指向拥有该结构的模块的指针,一般初始化为THIS_MODULE 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 *); //初始化一个异步读取操作 ssize_t(*aio_read)(struct kiocb*,const struct iovec *,unsinged long.loff_t); //初始化一个异步写入操作 ssize_t(*aio_write)(struct kiocb*,const struct iovec *,unsinged long,loff_t); //用来读取目录,对于设备文件,该成员应该为NULL int(*readdir)(struct file*.void *,filldir_t); //轮询函数,查询对一个或多个文件描述符的读或写是否会阻塞 unsinged int(*poll)(struct file *,struct poll_table_struct *); //用来执行设备I/O操作命令 int(*ioctl)(struct inode *,struct file*,unsinged int,unsinged long); //不使用BLK文件系统,将使用此函数代替ioctl long(*unlocked_ioctl)(struct file*,unsinged int,unsinged long); //在64位系统上,使用32位的ioctl调用将使用此函数代替 long(*compat_ioctl)(struct file*,unsinged int,unsinged long); //用来将设备内存映射进程的地址空间 int(*mmap)(struct file*,struct vm_area_struct *); //用来打开设备 int(*open)(struct inode *,struct file*); //执行并等待设备的任何未完成操作 int(*flush)(struct file*,struct dentry *,int datasync); //用来关闭设备 int(*release)(struct inode *,struct file *); //fsync的异步版本 int(*aio_fsync)(struct kiocb*,int datasync); //通知设备FASYNC标志的改变 int(*fasync)(int,struct file*,int); //用来实现文件加锁,通常设备文件不需要实现次函数 int(*lock)(struct file*,int struct file_lock *);}}

开发驱动时,需要实现的接口:

目前对此结构体采用“标记化”方法进行赋值,下面是对此结构体s3c44b0_fops用“标记化”方法进行赋值的实例。

static struct file_operations s3c440_fops={ owner:THIS_MODULE; open:s3c2410_ts_open; read:s3c2410_ts_read; write:s3c2410_ts_write; release:s3c2410_ts_release;}

字符设备驱动框架

字符设备驱动程序是嵌入式Linux最基本、也是最常用的驱动程序

字符设备在Linux内核中使用struct cdev结构来表示

在struct cdev结构中包含着字符设备需要的全部信息(面向对象的思想——基类或容器)

设备号dev_t(linux/type.h)

文件操作file_operations

设备类型和设备号

对字符设备的访问是通过文件系统内的设备文件进行的,或者称为设备节点,位于/dev目录

设备类型:字符设备/块设备

主设备号:主设备号用来标识该设备的种类,也标识了该设备所使用的驱动程序

次设备号:次设备号由内核使用,标识使用同一设备驱动程序的不同硬件设备

关键数据结构

file_operations

抽象接口,驱动程序里面去实现的接口

file结构体

打开文件,在内核空间创建的文件描述符

inode

文件的索引节点

file结构体

file结构体在<Linux/fs.h>中定义

file结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个与之关联的struct file,它由内核在打开文件时创建,在文件的所有实例都关闭之后,内核将释放这个数据结构。

inode

inode是内核文件系统索引节点对象,包含内核在操作文件或目录时需要的全部信息

在内核中inode结构体用来表示文件,file是表示打开文件的结构体

重要成员

dev_t i_rdev:对于设备文件而言,此设备成员包含实际的设备号

struct cdev *i_cdev:指向:cdev结构的指针

字符设备的注册和注销

在Linux内核中,使用cdev结构体来描述一个字符设备

struct cdev{ struct kobject kobj; //内嵌的kobject对象 struct module *owner; //所属的模块 const struct file_operations *ops; //文件操作函数 struct list_head list; dev_t dev;unsinged int count;}MAJOR(dev_t dev)根据设备号获得主设备号MINOR(dev_t dev)根据设备号获得次设备号MKDEV(int major,int minor)根据次设备号与主设备号获得设备号

cdev结构体操作函数,定义在<linux/cdev.h>

void cdev_init(struct cdev *,const struct file_operations *);用于初始化cdev成员。

struct cdev *cdev_alloc(void);用于动态申请一块cdev类型内存。

void cdev_add(struct cdev *,dev_t,unsigned);用于向系统添加一个cdev,完成字符设备的注册

void cdev_del(struct cdev*);用于向系统删除一个cdev完成字符设备的注销

分配和释放设备号

在使用cdev_add()向系统注册字符设备之前应先申请设备号,采用如下函数:

已知设备号:int register_chrdev_region(dev_t from,unsigned count,const char *name);

from:设备编号的起始值,次设备号为0;count申请分配的连续设备号的个数;name:和设备关联的名称。

未知设备号:int alloc_chrdev_region(de_t *dev,unsigned baseminor,unsinged count,const char *name);

内核空间与用户空间之间复制数据的方法

在用户空间不能直接访问内核空间恶内存,因此,需要借助下面两个函数,分别用来把数据从用户空间拷贝到内核空间,以及把数据从内核空间拷贝到用户空间。

从用户态拷贝到内核态:

unsigned long copy_from_user(void* to,const void_user * from,unsigned long count);

从内核态拷贝到用户态:

unsinged long copy_to_user(void* _user* to,const void * from ,unsigned long count );

虚拟字符驱动实例

在内核空间申请一块4kb内存用于模拟一个设备,并在驱动中提供对这块设备的读、写、控制和定位函数,以供用户空间的进程能通过Linux系统调用这块内存的内容

虚拟设备驱动vs真实设备驱动

虚拟设备驱动实例

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:dandanxi6@qq.com

(0)
上一篇 2023年 3月 5日 下午12:44
下一篇 2023年 3月 5日 下午12:44

相关推荐

  • etc怎么查询收费记录通行记录明细

    etc怎么查询收费记录 目前全国没用统一的etc收费记录查询的渠道,只能进入每个省市对应的公众号、小程序等工具进行查询收费明细,以前只有各省的etc客服电话,2019年12月10日…

    数码教程 2023年 5月 6日
  • oppo手机解锁方法大全

    你的手机上锁了吗?为了守住我们手机里面的一些自己的小秘密,我们可以给手机设置手机锁:数字密码、图案、指纹识别、人脸识别。今天小版就集中给大家介绍一下OPPO手机所支持的手机解锁方式…

    2024年 1月 17日
  • 减肥笔记之工欲善其事必先利其器

    特别说明:本文中提到思路适用于BMI指数大于24,男性体脂率大于20%(女性体脂率大于25%)的朋友,对于BMI指数超过28的肥胖型朋友效果更佳。对于BMI指数和体脂率都在正常范围…

    2023年 9月 27日
  • 保险套是干什么用的

    巨无霸保险套水球往头上砸,这个丧心病狂的挑战你敢尝试么? 2015-11-26 雅思 Highing 嗨星人们 你还记得当年大明湖畔的 冰桶挑战么? 大家抠抠头皮 应该还记忆犹新吧…

    2023年 6月 22日
  • 微信可以分组么,微信联系人能分组吗

    相信不少人都有在为微信不能分组这事儿发愁,其实你们知道吗?微信是可以实现分组功能的,具体操作大家往下看。 按类别对存储资料进行分组 相信不少人都有这样的习惯,觉得有存储价值的资料,…

    2023年 5月 6日
  • 删除很久的qq好友怎么找回

    删除很久是指多少呢?其实每个人的标准是不同的,或许有些人觉得10天或20天以上就是很久了,但是又有些人会觉得没个10年或20年以上都不算很久,这个是不能一概而论的。针对找回删除的Q…

    2023年 1月 12日
  • 华为云cdn加速,让你告别网速慢的烦恼

    无论是中小企业还是个人,在网站运维上都是非常头疼的事,即使是大企业,尤其是依靠网站做用户引流的,每年在运维成本上都投入非常多的资金跟人力成本。总的来说,一个稳定、加载速度快的网站是…

    2023年 1月 28日
  • apple watch官网翻新和全新的区别

    总所周知,苹果官方出售的翻新机器是要比全新的产品优惠的,但是功能却和全新的无差别,接下来对比一下新的苹果手表与翻新的手表有什么区别。 全新与翻新:包装和内容 翻新的 Apple W…

    2023年 10月 12日
  • 新买的手机怎么判断是不是翻新机

    大家好,欢迎来到科技熊! 今天给大家分享的技巧是我们在外边买手机的时候,如何鉴别自己买的手机是否是新机,因为现在买手机的途径有很多,比如好多人会在网上购买,或者是在一些实体店购买,…

    2023年 8月 4日
  • 农民工工资强制执行新闻,法院对拖欠工人工资强制执行

    老板跑了,厂房被抵押了,公司账上一分钱没有,辛辛苦苦一年多的工钱就这样打水漂了吗?自从2014年被拖欠工资开始,来自广东中银鹏科技有限公司的56名员工始终生活在焦虑与苦闷当中。尽管…

    2023年 1月 24日