下载了scull的代码,看着学习。
昨天看了LDD3的第三章:字符设备驱动程序。了解了有关设备编号,三个重要的结构,字符设备的注册等。今天主要看看scull代码中字符设备的初始函数。
查看的代码位置D:\Code\scull\examples\scull,源码粘贴:
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");
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. */
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;
}
首先分配主次设备号:
如果scull_major事先已知则使用register_chrdev_region函数分配设备编号,反之使用alloc_chrdev_region函数。对返回值检测,看是否分配成功。
接下来看到一句不太明白的代码:
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
kmalloc函数大致看了下应该是内核使用的内存分配函数。先看一下scull_devices这个结构体,里面有很多信息,有数据,还有对数据的描述内容。我想上面的代码主要是为scull这个假设的设备分配内存,内存分配结束后对该内存清零。
/*
* Representation of scull quantum sets.
*/
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 sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
然后代码就是一个for循环:对分配内存的每个设备进行初始化,其中有一函数如下。以及该函数的定义。
scull_setup_cdev(&scull_devices[i], i);
/*
* 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_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
该函数完成字符设备的注册。
下面的代码看了一下,暂时不太明白,先告一段落吧。
对照着书上的内容从头到尾看一下代码。分配主次设备号->分配设备内存->初始化内存及向内核注册字符设备。
但书上说到struct cdev结构时讲到“如果读者打算在运行时获得一个独立的cdev结构,则应该如下编写代码:struct cdev *my_cdev = cdev_alloc();my_cdev->ops = &my_fops;”
但是代码中似乎没有这么一段。
百度了一下看了别人写的, 才发现自己理解错误了。上面引用的原文是动态初始化,而scull源码中使用的是静态初始化。
参考转载内容:Linux内核中的cdev_alloc和cdev_add