字符设备驱动程序

上传人:wm****3 文档编号:41834478 上传时间:2018-05-31 格式:DOC 页数:12 大小:121KB
返回 下载 相关 举报
字符设备驱动程序_第1页
第1页 / 共12页
字符设备驱动程序_第2页
第2页 / 共12页
字符设备驱动程序_第3页
第3页 / 共12页
字符设备驱动程序_第4页
第4页 / 共12页
字符设备驱动程序_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《字符设备驱动程序》由会员分享,可在线阅读,更多相关《字符设备驱动程序(12页珍藏版)》请在金锄头文库上搜索。

1、Linux 设备驱动程序学习(设备驱动程序学习(1)-字符设备驱动程序字符设备驱动程序Linux 设备驱动程序学习(设备驱动程序学习(1)-字符设备驱动程序字符设备驱动程序今天进入Linux 设备驱动程序(第设备驱动程序(第 3 版)版)第三章字符设备驱动程序的学习。这一章主要通过介绍字符设备 scull(Simple Character Utility for Loading Localities,区域装载的简单字符工具)的驱动程序编写,来学习 Linux 设备驱动的基本知识。scull 可以为真正的设备驱动程序提供样板。一、主设备号和次设备号一、主设备号和次设备号主设备号表示设备对应的驱动

2、程序;次设备号由内核使用,用于正确确定设备 文件所指的设备。内核用 dev_t 类型()来保存设备编号,dev_t 是一个 32 位的数,12 位表示主设备号,20 为表示次设备号。在实际使用中,是通过中定义的宏来转换格式。(dev_t)主设备号、次设备号MAJOR(dev_t dev)MINOR(dev_t dev)主设备号、次设备号(dev_t) MKDEV(int major,int minor) 建立一个字符设备之前,驱动程序首先要做的事情就是获得设备编号。其这主要函数在中声明:int register_chrdev_region(dev_t first, unsigned int c

3、ount,char *name); /指定设备编号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); /动态生成设备编号void unregister_chrdev_region(dev_t first, unsigned int count); /释放设备编号分配主设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。以下是在 scull.c 中用来获取主设备号的代码:if (scull_major) dev = MKDEV(sc

4、ull_major, scull_minor);result = register_chrdev_region(dev, scull_nr_devs, “scull“); else result = alloc_chrdev_region(scull_major = MAJOR(dev);if (result 。三、字符设备的注册内核内部使用 struct cdev 结构来表示字符设备。在内核调用设备的操作之前, 必须分配并注册一个或多个 struct cdev。代码应包含,它 定义了 struct cdev 以及与其相关的一些辅助函数。注册一个独立的注册一个独立的 cdev 设备的基本过程如

5、下:设备的基本过程如下:1、为 struct cdev 分配空间(如果已经将 struct cdev 嵌入到自己的设备的特 定结构体中,并分配了空间,这步略过!)struct cdev *my_cdev = cdev_alloc();2、初始化 struct cdev void cdev_init(struct cdev *cdev, const struct file_operations *fops) 3、初始化 cdev.ownercdev.owner = THIS_MODULE;4、cdev 设置完成,通知内核 struct cdev 的信息(在执行这步之前必须确定 你对struct

6、cdev的以上设置已经完成!)int cdev_add(struct cdev *p, dev_t dev, unsigned count)从系统中移除一个字符设备:void cdev_del(struct cdev *p)以下是 scull 中的初始化代码(之前已经为struct scull_dev 分配了空间):/* Set up the char_dev structure for this device.*/static void scull_setup_cdev(struct scull_dev *dev, int index)int err, devno = MKDEV(scull

7、_major, scull_minor + index);cdev_init(dev-cdev.owner = THIS_MODULE;dev-cdev.ops = /这句可以省略,在 cdev_init 中已经做过err = cdev_add (/* Fail gracefully if need be 这步值得注意*/if (err)printk(KERN_NOTICE “Error %d adding scull%d“, err, index);四、scull 模型的内存使用 以下是 scull 模型的结构体:/* Representation of scull quantum sets

8、.*/struct scull_qset void *data;struct scull_qset *next;struct scull_dev struct scull_qset *data; /* Pointer to first quantum set */int quantum; /* the current quantum size */int qset; /* the current array size */unsigned long size; /* amount of data stored here */unsigned int access_key; /* used by

9、 sculluid and scullpriv */struct semaphore sem; /* mutual exclusion semaphore */struct cdev cdev; /* Char device structure */;scull 驱动程序引入了两个 Linux 内核中用于内存管理的核心函数,它们的定 义都在: void *kmalloc(size_t size, int flags);void kfree(void *ptr);以下是 scull 模块中的一个释放整个数据区的函数(类似清零),将在 scull 以 写方式打开和 scull_cleanup_mo

10、dule 中被调用:int scull_trim(struct scull_dev *dev)struct scull_qset *next, *dptr;int qset = dev-qset; /* 量子集中量子的个数*/int i;for (dptr = dev-data; dptr; dptr = next) /* 循环scull_set 个数次,直到 dptr 为 NULL 为止。*/if (dptr-data) for (i = 0; i datai);/* 释放其中一个量子的空间*/kfree(dptr-data);/* 释放当前的 scull_set 的量子集的空间*/dptr

11、-data = NULL;/* 释放一个 scull_set 中的 void *data 指针*/next = dptr-next; /* 准备下个 scull_set 的指针*/kfree(dptr);/* 释放当前的 scull_set*/dev-size = 0; /* 当前的 scull_device 所存的数据为 0 字节*/dev-quantum = scull_quantum;/* 初始化一个量子的大小*/dev-qset = scull_qset;/* 初始化一个量子集中量子的个数*/dev-data = NULL;/* 释放当前的 scull_device 的 struct

12、scull_qset *data 指针*/return 0;以下是 scull 模块中的一个沿链表前行得到正确 scull_set 指针的函数,将在 read 和 write 方法中被调用:/*Follow the list*/struct scull_qset *scull_follow(struct scull_dev *dev, int n)struct scull_qset *qs = dev-data;/* Allocate first qset explicitly if need be */if (! qs) qs = dev-data = kmalloc(sizeof(stru

13、ct scull_qset), GFP_KERNEL);if (qs = NULL)return NULL; /* Never mind */memset(qs, 0, sizeof(struct scull_qset);/* Then follow the list */while (n-) if (!qs-next) qs-next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);if (qs-next = NULL)return NULL; /* Never mind */memset(qs-next, 0, sizeof(struct

14、scull_qset);qs = qs-next;continue;return qs;其实这个函数的实质是:如果已经存在这个 scull_set,就返回这个 scull_set 的指针。如果不存在这个 scull_set,一边沿链表为 scull_set 分配空间一边 沿链表前行,直到所需要的 scull_set 被分配到空间并初始化为止,就返回这 个 scull_set 的指针。 五、open 和 release open 方法提供给驱动程序以初始化的能力,为以后的操作作准备。应完成的工作如下:(1)检查设备特定的错误(如设备未就绪或硬件问题);(2)如果设备是首次打开,则对其进行初始化;

15、(3)如有必要,更新 f_op 指针;(4)分配并填写置于 filp-private_data 里的数据结构。而根据 scull 的实际情况,他的 open 函数只要完成第四步(将初始化过的 struct scull_dev dev 的指针传递到 filp-private_data 里,以备后用)就好了,所以 open 函数很简单。但是其中用到了定义在中的 container_of 宏,源码如下:#define container_of(ptr, type, member) ( const typeof( (type *)0)-member ) *_mptr = (ptr); (type *)

16、( (char *)_mptr - offsetof(type,member) );)其实从源码可以看出,其作用就是:通过指针 ptr,获得包含 ptr 所指向数据(是 member 结构体)的 type 结构体的指针。即是用指针得到另外一个指针。release 方法提供释放内存,关闭设备的功能。应完成的工作如下:(1)释放由 open 分配的、保存在 file-private_data 中的所有内容;(2)在最后一次关闭操作时关闭设备。由于前面定义了 scull 是一个全局且持久的内存区,所以他的 release 什么都不做。六、read 和 write read 和 write 方法的主要作用就是实现内核与用户空间之间的数据拷贝。因为 Linux 的内核空间和用户空间隔离的,所以要实现数据拷贝就必须使用在中定义的:unsigned long copy_to_u

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号