scull字符驱动学习笔记
目录
1 scull驱动简介
2 主次编号
3 scull源码分析
-------------------------
| 1 scull驱动简介 |
-------------------------
scull是一个字符驱动,我们操作一块内存区好像它是一个设备。scull是硬件设备关联不大, 所以它是通用可移植的。
-----------------------
| 2 主次编号 |
----------------------
2.1
随便提提主次编号吧!
字符设备通过文件系统中的名字来存取,也就是常说的设备文件。我们查看/dev目录下的文件,会查看到系统下所有设备文件。
主编号标识设备相连的驱动。次编号用来决定引用哪个设备,依据你的驱动是如何编写的, 你可以从内核得到一个你的设备的直接指针。内核允许多个驱动共享主编号。
2.2
一些主设备编号是静态分给最普通的设备的,对于新驱动,大多数情况下使用动态分配来获取你的主设备号。
动态分配主设备号函数
alloc_chrdev_region
动态分配的缺点是你无法提前创建设备节点, 因为分配给你的模块的主编号会变化. 对于驱动的正常使用, 这不是问题, 因为一旦编号分配了, 你可从 /proc/devices 中读取它。为使用动态主编号来加载一个驱动, 因此, 可使用一个简单的脚本来代替调用 insmod, 在调用insmod 后, 读取 /proc/devices 来创建特殊文件.
在scull源码中获取主编号的代码
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d/n", scull_major);
return result;
}
-------------------
| 3 scull源码分析 |
-------------------
scull 设备结构体
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 sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
/*
* 内存中数据存储结构体。
*/
struct scull_qset {
void **data; //二维指针,data保存的是一个已分配的数据存储区的地址。
struct scull_qset *next;
};
一个scull设备的布局。
这里我们引入两个新的概念,量子和量子集。
scull_qset 组成一数据结构链表。
每个scull_qset使用一个1000个指针的数组(量子集),同时每一个指针指向4000字节的内存区域(量子)。
下面的代码展示如何将回收整个内存数据区的内存。
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;
int qset = dev->qset; /* "dev" is not-null */
int i;
for (dptr = dev->data; dptr; dptr = next)
{ /* all the list items */
if (dptr->data) {
for (i = 0; i < qset; i++)
kfree(dptr->data[i]);
kfree(dptr->data);
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr);
}
dev->size = 0;
dev->quantum = scull_quantum;
dev->qset = scull_qset;
dev->data = NULL;
return 0;
}
初始化驱动模块注册函数。
int scull_init_module(void)
{
int result, i;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
/*动态获取主设备号 */
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
/*MAJOR 宏定义获得设备的主设备号*/
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d/n", scull_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
/*初台化已分配的内存空间*/
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
/* Initialize each device. */
/***************************************************************
scull_devices[i]对应的结构体
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 sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
***************************************************************/
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem); //??暂时还不理解
scull_setup_cdev(&scull_devices[i], i); //创建字符设备
}
/* At this point call the init function for any friend device */
dev = MKDEV(scull_major, scull_minor + scull_nr_devs);
dev += scull_p_init(dev);
dev += scull_access_init(dev);
#ifdef SCULL_DEBUG /* only when debugging */
scull_create_proc();
#endif
return 0; /* succeed */
fail:
scull_cleanup_module();
return result;
}