自动创建设备文件

原文地址:http://blog.chinaunix.net/uid-25381054-id-3270860.html

刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod [OPTION]... NAME TYPE [MAJOR MINOR]命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev(mdev)

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。

struct class和class_create(…) 定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。

在2.6.38(最新版本3.14.4)内核版本中,struct class定义在头文件include/linux/device.h中:

struct class {
	const char		*name;
	struct module		*owner;

	struct class_attribute		*class_attrs;
	struct device_attribute		*dev_attrs;
	struct bin_attribute		*dev_bin_attrs;
	struct kobject			*dev_kobj;

	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, mode_t *mode);

	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct kobj_ns_type_operations *ns_type;
	const void *(*namespace)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct subsys_private *p;
};
/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})
struct class *__class_create(struct module *owner, const char *name,
			     struct lock_class_key *key)
{
	struct class *cls;
	int retval;

	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}

	cls->name = name;
	cls->owner = owner;
	cls->class_release = class_create_release;

	retval = __class_register(cls, key);
	if (retval)
		goto error;

	return cls;

error:
	kfree(cls);
	return ERR_PTR(retval);
}
第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 在class.c中,还定义了class_destroy(…)函数,用于在模块卸载时删除类。

device_create(…)定义在core.c中
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;

	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。  
下面通过改写LDD3中scull源码来实现设备文件scull【0~3】的自动创建。

首先应该加上头文件 #include <linux/device.h> 
其次在scull_init_module中

  1. 626 int scull_init_module(void)
  2. 627 { 
  3. 628 +-- 7 lines: int result, i;
  4. ------------------------------------------------------------------------------
  5. 635 if (scull_major) { 
  6. 636 dev = MKDEV(scull_major, scull_minor);
  7. 637 result = register_chrdev_region(dev, scull_nr_devs, "scull");
  8. 638 +-- 8 lines: } else {
  9. ------------------------------------------------------------------------------
  10. 646 }
  11. 647 
  12. 648 +-- 4 lines: allocate the devices -- we can't have them static, as the number
  13. ------------------------------------------------------------------------------
  14. 652 scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
  15. 653 +-- 6 lines: if (!scull_devices) {
  16. ------------------------------------------------------------------------------
  17. 659 //create the device class
  18. 660 my_class = class_create(THIS_MODULE,"scull"); //类名为 scull
  19. 661 if(IS_ERR(my_class)) 
  20. 662 {
  21. 663 printk("Err: failed in creating class.\n");
  22. 664 return -1; 
  23. 665 }
然后在scull_setup_cdev函数中

  1. 605 static void scull_setup_cdev(struct scull_dev *dev, int index)
  2. 606 {
  3. 607 int err, devno = MKDEV(scull_major, scull_minor + index);
  4. 608 
  5. 609 cdev_init(&dev->cdev, &scull_fops);
  6. 610 dev->cdev.owner = THIS_MODULE;
  7. 611 dev->cdev.ops = &scull_fops;
  8. 612 err = cdev_add (&dev->cdev, devno, 1);
  9. 613 /* Fail gracefully if need be */
  10. 614 if (err)
  11. 615 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
  12. 616 //create the device file
  13. 617 int result = device_create(my_class,NULL,devno,NULL,"scull%d",index); //设备名为scull[0~3]
  14. 618 if (result<0) 
  15. 619 {
  16. 620 printk (KERN_WARNING "hello: can't get major number %d\n", scull_major);
  17. 621 return result;
  18. 622 }
  19. 623 }
这样在加载该驱动时,就会自动生成相应的设备文件。

在移除模块时,要相应的删除自动创建的设备和类

  1. 591 void scull_cleanup_module(void)
  2. 592 {
  3. 593 int i;
  4. 594 dev_t devno = MKDEV(scull_major, scull_minor);
  5. 595 
  6. 596 /* Get rid of our char dev entries */
  7. 597 if (scull_devices) {
  8. 598 for (= 0; i < scull_nr_devs; i++) {
  9. 599 scull_trim(scull_devices + i);
  10. 600 cdev_del(&scull_devices[i].cdev);
  11. 601 device_destroy(my_class,MKDEV(scull_major, scull_minor+i));
  12. 602 }
  13. 603 kfree(scull_devices);
  14. 604 }
  15. 605 
  16. 606 #ifdef SCULL_DEBUG /* use proc only if debugging */
  17. 607 scull_remove_proc();
  18. 608 #endif
  19. 609 
  20. 610 /* cleanup_module is never called if registering failed */
  21. 611 unregister_chrdev_region(devno, scull_nr_devs);
  22. 612 class_destroy(my_class);
  23. 613 
  24. 614 /* and call the cleanup functions for friend devices */
  25. 615 scull_p_cleanup();
  26. 616 scull_access_cleanup();
  27. 617 
  28. 618 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值