Chinaunix首页 | 论坛 | 博客
  • 博客访问: 211651
  • 博文数量: 10
  • 博客积分: 346
  • 博客等级: 一等列兵
  • 技术积分: 322
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-24 11:04
文章分类
文章存档

2012年(10)

我的朋友

分类: LINUX

2012-06-01 17:44:42

字符设备驱动基础知识

 

设备号

字符设备通过字符设备文件来存取。字符设备文件由使用ls –l的输出的第一列“c”标识。如果使用ls –l 命令,会看到在设备文件项中有2个数(由一个逗号分隔)这些数字就是设备文件的主次设备编号。

前面一个数字4是主设备号,它是用来标识与设备文件相连的驱动程序。后面的929367等数字是次设备号,它是被驱动程序用来辨别操作的是同类型的哪个设备。

设备号在内核中是用dev_t来描述的:12位为主设备号,低20位是次设备号

提取主设备号:MAJOR(dev_t dev)

提取次设备号:MINOR(dev_t dev)

 

LINUX下设备号的分配:静态申请,动态申请两种方法

静态申请:查看Documentation/devices.txt,确定一个没有使用过的主设备号,使用register_chrdev_region函数注册设备号(缺点:如果没有分析好,或者驱动程序使用广泛的时候,可能会发生设备号冲突,使驱动程序无法注册)

Int register_chrdev_regio(dev_t from,unsigned count,const char *name):注册从from开始的count个设备号(主设备号不变,次设备号增加,如果次设备号溢出,主设备号加1

参数:from:要注册的第一个设备号   count:要注册的设备号个数   name:设备名(体现在/proc/devices

 

动态申请:使用alloc_chrdev_region分配设备号(缺点:无法在安装驱动前创建设备文件。解决办法:安装驱动后,从/proc/devices中查询设备号)

Int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name):动态申请count个设备号,第1个设备号的次设备号为baseminor

参数:dev:分配到的设备号(不需要传值,用来获取主设备号)baseminor:起始次设备号    count:要注册的设备号个数    name:设备名(体现在/prroc/devices

在不再使用设备号时,应释放这些设备号:void unregister_chrdev_region(dev_t from, unsigned count)释放从from开始的count个设备号。

 

创建设备文件:mknod命令手工创建,自动创建

Mknod手工创建:mknod filename type major minor

Filename:设备文件名  type:设备文件类型 major:主设备号 minor:次设备号

自动创建:安装成功驱动后,自动创建文件。

利用udevmdev)来实现设备文件的自动创建很简单,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备.

例子:

Struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);

Device_create(myclass, NULL, MKDEV(major_num,0), NULL, “my_device”);

当驱动被加载时,udev(mdev)就会自动在/dev下创建my_device设备文件。

 

重要数据结构:

struct file:代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,在文件关闭后释放。重要成员:loff_t f_pos/*文件读写位置*/ struct file_operations *f_op

struct inode:用来记录文件的物理上的信息。因此,它和代表打开文件的file结构不同。一个文件可以对应多个file结构,但只有一个inode结构。重要成员:dev_t i_rdev:设备号。

 

:一个函数指针的集合,定义能在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL

设备注册:字符设备使用struct cdev来描述,注册分为三步:分配cdev,初始化cdev,添加cdev

分配cdev:  struct cdev * cdev_alloc(void);

初始化cdevvoid cdev_init(struct cdev *cdev,const struct file_operations *fops)参数cdev:待初始化的cdev结构;fops:设备对应的操作函数集

添加(注册)cdevini cdev_add(struct cdev *p,dev_t dev,unsigned count)参数:p待添加到内核的字符设备结构;dev:设备号;count:添加的设备个数

 

设备操作:

Int (*open)(struct inode *,struct file *)

在设备文件上的第一个操作,并不要求驱动程序一定要实现这个方法。如果该项为NULL,设备的打开操作永远成功。

Void (*release)(struct inode *,struct file *)

当设备文件被关闭时调用这个操作。与open相仿,release也可以没有。

Ssize_t (*read)(struct file *filp,char __user *buff,size_t count,loff_t *offp)

从设备中读取数据

Ssize_t (*write)(struct file *,const char __user *,size_t,loff_t *)

向设备发送数据

Unsigned int (*poll)(struct file *,struct poll_table_struct *)

对应select系统调用

Int (*ioctl)(struct inode *,struct file *,unsigned int,unsigned long)

控制设备

Int (*mmap)(struct file *,struct vm-area_struct *)

将设备映射到进程虚拟地址空间中

Off_t (*llseek) (struct file *,loff_t,int)

修改文件的当前读写位置,并将新位置作为返回值。

 

Open方法:open方法是驱动程序用来为以后的操作完成初始化准备工作的。在大部分驱动程序中,open完成初始化设备和标明次设备号的工作。

Release方法:与open相反,这个方法有时也称为close,它应该完成关闭设备的作用。

读和写:

//filp是文件指针,count是请求传输的数据量,buff参数指向数据缓存,offp指出文件当前的访问位置。Buff参数是用户空间指针,它在内核空间时可能根本是无效的——没有那个地址的映射,它不能被内核代码直接引用。

内核提供了专门的函数用于访问用户空间的指针,如:

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

Int copy_to_user(void __user *to,const void *rom,int n)

 

设备注销:

Int cdev_del(struct cdev *p)参数P:要注销的字符设备结构

 

 

阅读(2396) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~