Linux驱动模型学习(二)

相信大家通过上节的了解,对字符设备也有了感性上的认识。接下来我们就要对字符设备驱动进行剖析了(基于Linux3.0.1版本内核)

一、字符设备结构struct cdev说明

在Linux内核中,是使用struct cdev这个数据结构来表示字符设备的。

定义在<linux/fs.h>中

struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; //操作函数集 struct list_head list; dev_t dev; //设备号 unsigned int count;// 支持的设备数,如设备中支持三个串口};

我们只对该结构中的主要成员进行说明:

a )操作函数集file_operationsops的说明——————————————–start

定义在<linux/fs.h>中

以下为file_operations结构的内容,同时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 *);//向设备中写入数据 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化设备上的异步读取操作 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化设备上的异步写入操作 int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *);//打开设备,始终是对设备文件执行的第一个操作 int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);};

file_operations 作用:是一个函数指针的集合,主要完成应用程序的系统调用对在设备驱动上的操作的映射。这个结构中的每一个成员都必须指向驱动程序中实现特定的操作函数(方法),即此结构中的函数指针指向驱动中的实现函数(方法),不同函数实现针对设备的不同操作,对不支持的操作,可设置对应函数指针为NULL。

如下;

struct file_operations memfpos = {.read = dev_read,.write = dev_write,.ioctl = dev_ioctl,.llseek = NULL,} ;

关于操作函数集file_operations的说明———————————————-end

b)设备号的说明——————————————–start

(在/dev/目录下查看)

crw-rw—-. 1 root video 10,175 Mar 19 09:36 agpgart

crw-rw—-. 1 root root 10,56Mar 19 09:37 autofs

drwxr-xr-x. 2 root root 640 Mar 19 09:36 block

drwxr-xr-x. 2 root root 80 Mar 19 09:36 bsg

红色10 : 代表主设备号。通过主设备号把设备文件和设备驱动程序一 一对应起来。

黄色0:代表次设备号。区分同类型的设备。譬如串口驱动程序通过此设备号去识别不同的串口。。。

注:第一列为“d”,代表块设备

若是普通文件则会被文件大小所代替。如下:

Mar 19 09:36 a.c

由上述结构可知,设备号的数据类型为dev_t。经追踪得:dev_t ==unsigned int ,即是32位数据

在这32位中,其中高12位是主设备号,低20位是次设备号。

操作设备号:—-存在于结构体struct inode中;成员名为i_rdev。

1>将主设备号与次设备号合成为dev_t类型

dev_t =MKDEV(主设备号, 次设备号)

2>从dev_t中分解出主设备号

主设备号 =MAJOR(dev_t dev)

3>从dev_t中分解出次设备号

次设备号 =MINOR(dev_t dev)

例:num = MINOR(inode->i_rdev);

分配设备号:—>主要是分配主设备号

1>静态分配

开发者自己选择一个数字作为主设备号,然后通过函数register_chrdev_region向内核申请使用。

缺点:如果使用的设备号已经被内核中的其他驱动使用,则申请失败。

2>动态分配

使用alloc_chrdev_region由内核分配一个可用的主设备号。

优势:不会分配到已经使用的号

缺点:由于分配的主设备号不能保证始终一致,所以无法预先创建设备节点。所以对于驱动程序来讲,一般在分配设备号,就可以在/proc/devices读到。回忆上一节。

函数原型:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

参数:

* @dev: output parameter for first assigned number —>待分配的主设备号

* @baseminor: first of the requested range of minor numbers —>第一个次设备号值

* @count: the number of minor numbers required —>次设备号的数量

* @name: the name of the associated device or driver—>关于该设备即驱动的名字

注销设备号:

不管使用哪种分配得到的设备号,都应该在驱动退出时使用unregister_chrdev_region函数释放这些设备号。

注销设备号函数原型:void unregister_chrdev_region(dev_t from, unsigned count)

参数:

* @from: the first in the range of numbers to unregister —》注销的主设备号

* @count: the number of device numbers to unregister—》注销的设备数量

关于设备号的说明————————————————-end

在了解了以上设备驱动结构 cdev 的主要成员后,我们就可以进行设备驱动程序的编写了

二、字符设备驱动程序编写:

㈠、驱动的初始化:

1.1分配设备描述结构(struct cdev)—两种分配方式:静态分配和动态分配

1>静态分配:struct cdev mdev;

2>动态分配:struct cdev *pdev = cdev_alloc();

注:注册设备号(动态)alloc_chrdev_region

1.2

函数原型:void cdev_init(struct cdev *cdev, const struct file_operations *fops);

参数:

cdev: 待初始化的cdev结构

fops:设备对应的操作函数集

注:将设备结构与操作其设备的函数集相关联。

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

参数:

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

dev:设备号

count:该类设备的设备个数

1.4硬件初始化—根据相应硬件的芯片手册,完成初始化。

在这里我们使用数组去模拟寄存器,所以不需要进行硬件的初始化。

以上步骤对应实现代码:

在时光的激流中,我们总会长大。

Linux驱动模型学习(二)

相关文章:

你感兴趣的文章:

标签云: