码迷,mamicode.com
首页 > 其他好文 > 详细

字符设备驱动(程序设计)—①

时间:2015-05-11 12:52:29      阅读:294      评论:0      收藏:0      [点我收藏+]

标签:

via:http://blog.sina.com.cn/s/blog_7ec8fc2c010157lc.html

1、驱动程序设计

 

1)驱动分类

驱动这里分为 字符设备驱动、网络接口驱动、块设备驱动!这三类,其中前两者是重点。

 

①、字符设备

字符设备是一种 按自己来访问 的设备,字符驱动则负责驱动字符设备,这样的驱动通常是先 open、close、read和write 系统调用!

 

②、块设备

在大部分 Unix 系统中,块设备不能按照字节处理数据,只能一次传送一个或则会多个长度是 512 字节(或者更大的 2 次幂的数)的整块数据。

 

而对于 Linux 则允许块设备传送任意数目的字节。因此,块和字符设备的区别仅仅是驱动的与内核的接口不同。

 

③、网络接口

任何网络实物都通过一个接口来进行,一个接口通常是一个硬件设备(eth0),但是它也可以是一个纯粹的软件设备,比如回环接口(lo)。一个网络接口负责发送和接收数据报文。

 

2)驱动程序安装

 

方法一、模块方式

 

方法二、直接编译进内核

这种方法就是直接修改 Kconfig 以及 Makefie(这两个文件都要与你的程序放置的位置相对应,比如:/drivers/char 目录下你要放个 hello.c 的程序,然后修改这个目录下的 Kconfig 以及 Makefile 就哦了)

 

3)驱动程序的使用

 

直接拿国嵌的图就哦了:

 

技术分享

2、字符设备驱动程序设计

 

首先呢,介绍下知识点:

▲:设备号

▲:创建设备文件

▲:设备注册

▲:重要数据结构

▲:设备操作

 

1)主次设备号

 

字符设备通过自出设备文件来存取。字符设备文件使用 ls -l 的输出的第一列的 "c" 标识。

如果使用 ls -l 命令,会看到在设备文件项中友 2 个数(由一个逗号分隔)这些数字就是设备文件的主次设备编号。

 

①、设备号作用

主设备号用来标识与设备文件相连的驱动程序(就是说,主设备号连接了字符设备文件和字符设备驱动)。次设备号被驱动程序用来判别操作的是哪个设备。

 

也就是说:主设备号用来反映设备类型,次设备号用来区分同类型的设备。

 

②、设备号的描述

▲:内核中用 dev_t 来描述设备号(其实质就是 unsigned int 32 位整数,其中高 12 位为主设备号,低 20 位为次设备号)。

▲:MAJOR(dev_t dev)

从 dev_t中分解出主设备号!

▲:MINOR(dev_t dev)

从 dev_t 中分解出次设备号!

 

③、分配主设备号

可以采用 静态申请、动态分配 两种方法。

 

A、静态申请

 

方法:

首先根据 Documentation/devices.txt,确定一个没有使用的主设备号,然后使用 register_chrdev_region 函数注册设备号!

优点:

简单

缺点:

一旦驱动被广泛使用,这个随机选定的主设备号可能会导致设备号冲突,而使驱动程序无法注册。

 

register_chrdev_region

 

函数功能:

申请使用从 from 开始的 count 个设备(主设备号不变,次设备号增减)。

函数原型:

int register_chrdev_region(dev_t from,unsigned count,const char *name)

参数说明:

from:希望申请使用第一个设备号

count:希望申请使用的设备号数目

name:设备名(体现在 /proc/devices)。

提示:设备号由主设备号和次设备号构成,内核提供宏 MKDEV(ma,mi) 来构成设备号,通过宏 MAJOR(dev) 可以从设备号中提取主设备号,通过宏 MINOR(dev) 可以从设备号中提出从设备号。

返回值:

成功返回 0,失败返回值为负的错误号。

 

B、动态分配

 

方法:

使用 alloc_chrdev_region 分配设备号

优点:

简单,易于驱动推广

缺点:

无法再安装驱动前创建设备文件(因为安装前还没有分配到主设备号)。

解决方法:

安装驱动后,从 /proc/devices 中查询设备号

 

alloc_chrdev_region

 

函数功能:

请求内核动态分配 count 个设备号,且此设备号从 baseinor 开始

函数原型:

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)

参数说明:

dev:分配到的设备号

baseminor:起始此设备号

count:需要分配的次设备号数目

name:设备名(体现在 /proc/devices)

返回值:

成功返回 0,失败返回值为负的错误号。

 

④、注销设备号

 

不论使用何种方法分配设备号,都应该在不在使用它们时释放这些设备号。

 

unregister_chrdev_region

函数功能:

释放从 from 开始的 count 个设备号

函数原型:

void unregister_chrdev_region(dev_t from,unsigned count)

参数说明:

from:要删除的第一个设备号

count:要删除的设备号的个数

返回值:

 

⑤、创建设备文件

 

 方法一、

使用 mknod 命令手工创建

 

其中 mknod 用法:

mknod filename type major minor

参数含义:

filename:设备文件名

type:设备文件类型

major:主设备号

minor:次设备号

例程:

mknod serial0 c 100 0

 

方法二、自动创建

他说会后面介绍到,那咱就后面再说!

 

2)重要结构

 

在 Linux 字符设备驱动程序设计中,有三种非常重要的数据结构:

 

struct file

struct innod

struct file_operations

 

①、struct file

代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的 struct file(注意:这里是每打开一次都会产生这个文件)。它由内核在打开文件时创建,在文件关闭后释放!

 

重要成员:

loff_t f_pos    

struct file_operation *f_op

 

②、struct inode

用来记录文件的物理上的信息。因此,它和代表打开文件的 file 结构是不同的。一个文件可以对应多个 file 结构,但是只有一个 inode 结构!

 

重要成员:

dev_t i_rdev:设备号

 

③、struct file_operations

一个函数指针的集合,定义能在设备上运行的操作。

结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为 NULL。

 

例如,这个结构体可以这样写:

 

struct file_operation mem_fops = {

.owner = THIS_MODULE,

.llseek = mem_seek,

.read = mem_read,

.write = mem_write,

.ioctl = mem_ioctl,

.open = mem_open,

.release = mem_release,

};

 

3)设备注册

 

在 Linux 2.6 内核中,字符设备使用 struct cdev 来描述。

 

字符设备的注册可分为如下 3 个步骤:

▲:分配 cdev

▲:初始化 cdev

▲:添加 cdev

 

①、设备注册(分配)

分配一个 struct cdev

 

函数原型:

stuct cdev *cdev_alloc(void)

返回值:

成功返回一个指向 cdev 结构体的指针,失败返回 NULL。

 

②、设备注册(初始化)

struct cdev 的分配可以使用 cdev_alloc 函数来完成。

 

函数功能:

初始化一个 struct cdev

函数原型:

 void cdev_init(cdev *cdev,const struct file_operations *fops)

参数说明:

cdev:待初始化的 cdev 结构

fops:设备对应的操作函数集(其中 file_operations 结构的主要成员已经在上面介绍过了)!

返回值:

 

③、设备注册(添加)

struct cdev 的初始化使用 cdev_init 函数来完成。

 

函数功能:

注册一个 struct_cdev,添加系统添加该字符设备

函数原型:

int cdev_add(struct cdev *p,dev_t dev,unsigned count)

参数说明:

p:待添加到内核的字符设备结构

dev:设备号

count:添加的设备个数

返回值:

成功返回 0,失败返回值为负的错误号。

 

④、注销字符设备

 

函数原型:

int cdev_del(struct cdev *p)

参数说明:

p:要注销的字符设备结构

返回值:

成功返回 0,失败返回负的错误号

 

 

4)设备操作

 

在完成了驱动程序的注册,下一步就要实现设备所支持的操作!!!

也就是实现 file_operations 结构中对应的成员。

 

①、open

功能描述:

在设备文件进行的第一个操作,不要求驱动声明一个对应的方法。

如果这个项为 NULL,设备打开一直成功,但是你的驱动不会得到通知。

函数原型:

int (*open)(struct inode *inode,struct file *filp)

参数说明:

inode:为文件节点,这个节点只有一个,无论用户打开多少个文件,都只是对应着一个 inode 结构。

filp:只要打开一个文件,就对应着一个 file 结构体,file 结构体通常用来追踪文件在运行时的状态信息。

 

②、release

函数描述:

当最后一个打开设备的用户进程执行 close() 系统调用的时候,内核将调用驱动程序 release() 函数。函数的主要任务是清理未结束的输入输出操作,释放资源,用户自定义排它标志的复位等。

在文件结构被释放时引用这个操作,如同 open,release 可以为 NULL。

函数原型:

int (*release)(struct inode *inode,struct file *filp)

参数说明:

同 open 操作

 

③、read

功能描述:

用来从设备中获取数据。返回值代表了成功读取的字节数

函数原型:

ssize_t (*read)(struct file *filp,char __user *buffer,size_t size,loff_t *p)

函数说明:

filp:目标文件结构体指针

buffer:对应放置信息的缓冲区(既用户空间内存地址)

size:要读取的信息长度

p:读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,移动的值为要读取信息的长度值)

 

④、write

功能描述:

发送数据给设备,返回值代表成功写的字节函数(注:这个操作和上面的对文件文件进行读的操作均为阻塞操作)。

函数原型:

ssize_t (*write)(struct file *filp,contst char __user *buffer,size_t count,loff_t *ppos)

参数说明:

filp:目标文件结构体指针

buffer:要写入文件的嘻嘻缓冲区

size:要写入信息的长度

poos:当前的偏移位置,这个值通常是用来判断写文件是否越界

 

 

⑤、llseek

功能描述:

初始化一个 struct cdev

函数原型:

loft_t (*llseek)(struct file *filp,loff_t p,int orig)

参数说明:

filp:目标文件结构体指针

p:文件定位的目标偏移量

orig:对文件定位的起始地址,这个值可以为文件开头(SEEK_SET、0),当前位置(SEEK_CUR、1),文件末尾(SEEK_END、2)。

 

 5)读和写

 

 关于读和写,这里要说明一下:

filp 是文件指针,count 是请求传输的数据量。buff 参数指向数据缓存,最后,offp 指出文件当前的访问位置!

 

其中:read 和 write 方法的 buff 参数是用户空间指针。因此,它不能被内核代码直接饮用,理由:

用户孔家指针在内核空间时,可能根本无效的——没有那个地址的映射!

 

这样,内核就提供了专门的函数永固访问用户空间的指针,例如:

 

int copy_from_user(void *to,const void __user *from,int n)

int copy_to_user(void __user *to,const void *from,int n)

 

 

顺便再多介绍点吧:

能将内核空间的变量传递给用户空间的地址的函数类似的还有:

get_user(var,ptr);

put_user(var,ptr);

long stmcpy_from_user(char *dst,const char __user *src,long count);

strlen_user(str);

 

这些函数用来使用内核空间可以与用户空间进行必要的数据交互,毕竟使用系统调用的返回值来传递数据局限性太大。

 

字符设备驱动(程序设计)—①

标签:

原文地址:http://www.cnblogs.com/izhangzhne/p/4494033.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!