《LED驱动》4--驱动分析2

1.注册字符设备驱动新接口1

        1.1、新接口与老接口 

        (1)老接口:register_chrdev
        (2)新接口:register_chrdev_region/alloc_chrdev_region + cdev
        (3)为什么需要新接口。因为以前是没有次设备号的需求的,随着之后的发展,又有了此设备号,因为主设备号只能有255个,这样能注册的设备数量就很少,但是设备的驱动有很多,比如一块开发板上可就就有好几个LED驱动,但是他们之前的区别可能就是寄存器不同而已。所以内核又引入了此设备号,将注册设备号分为了两部。第一步,申请设备号。第二部注册设备号。同时,还兼容了老的接口(register_chrdev)。

        兼容:register_chrdev

static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)
{
	return __register_chrdev(major, 0, 256, name, fops);
}
int __register_chrdev(unsigned int major, unsigned int baseminor,
		      unsigned int count, const char *name,
		      const struct file_operations *fops)
{
	struct char_device_struct *cd;
	struct cdev *cdev;
	int err = -ENOMEM;

	cd = __register_chrdev_region(major, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);

	cdev = cdev_alloc();
	if (!cdev)
		goto out2;

	cdev->owner = fops->owner;
	cdev->ops = fops;
	kobject_set_name(&cdev->kobj, "%s", name);

	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
	if (err)
		goto out;

	cd->cdev = cdev;

	return major ? 0 : cd->major;
out:
	kobject_put(&cdev->kobj);
out2:
	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
	return err;
}

        查看register_chrdev在内核里的定义,有以下几个关键点:

        (1)inline。

        (2)参数列表:major,name,fops。

        (3)return __register_chrdev(major, 0, 256, name, fops)。

        首先,关键字inline。inline代表函数是一个内联函数,其作用是对于那些比较精简的函数,减少开销,函数调用是用到栈的开销的,内敛函数即可以有宏函数的减少开销的优点,还可以检查参数匹配。

        第二,参数列表。major是主设备,那么是驱动的名字,fops是驱动的file_operations。

        第三,return __register_chrdev(major, 0, 256, name, fops)。可以看到,register_chrdev 的实质还是调用的__register_chrdev(major, 0, 256, name, fops);其中的0是设备号。可见,使用register_chrdev注册设备号是不可以不可以设置次设备号的,都是采用的0设备号。__register_chrdev会调用__register_chrdev_region,cdev_alloc,cdev_init,cdev_add。

        1.2、新接口

        新接口:register_chrdev_region/alloc_chrdev_region + cdev。新接口将注册设备驱动分为了两部。第一步,申请设备号。第二部注册设备号。

        申请设备号有两种方式,register_chrdev_region/alloc_chrdev_region。

       (1) register_chrdev_region。

int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
	struct char_device_struct *cd;
	dev_t to = from + count;
	dev_t n, next;

	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		if (next > to)
			next = to;
		cd = __register_chrdev_region(MAJOR(n), MINOR(n),
			       next - n, name);
		if (IS_ERR(cd))
			goto fail;
	}
	return 0;
fail:
	to = n;
	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
	}
	return PTR_ERR(cd);
}

        使用register_chrdev_region可以申请一段连续的设备号,传入参数有dev_t from, unsigned count, const char *name。from是给的起始的设备号,dev包括主设备和次设备号。count是要申请的驱动设备数,name是值驱动的名字。同样新的接口其实也是调用__register_chrdev_region。

        申请设备号后还需要两步操作,cdev_init(),和cdev_add(),如:
         cdev_init(&adxl34x_cdev, &adxl34x_fops);        
         ret = cdev_add(&adxl34x_cdev, adxl34x_devid, 1);

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);  //计数器
	cdev->ops = fops;
}

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

	p->dev = dev;
	p->count = count;

	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;

	kobject_get(p->kobj.parent);

	return 0;
}

        cdev结构体里面有个重要的元素,ops和dev。cdev_init是将设置cdev中的ops,cdev_add是设置cdev中的dev。这样ops和dev就绑定在一起了,完成了注册设备驱动。

        再卸载驱动时:

       unregister_chrdev_region(dev_t from, unsigned count);//注销设备,解除绑定,归还设备号
        cdev_del(&hello_cdev);            //删除一个字符设备。

        (2) alloc_chrdev_region。

        与register_chrdev_region的区别是,register_chrdev_region知道主设备号,alloc_chrdev_region不知道,需要系统分配。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)
{
	struct char_device_struct *cd;
	cd = __register_chrdev_region(0, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);
	*dev = MKDEV(cd->major, cd->baseminor);
	return 0;
}

        可以看出利用alloc_chrdev_region分配设备号时,会调用__register_chrdev_region自动分配(第一个参数是0),第二个参数是此设备号的开始,第三个参数是要分配的数量。这一个步同样是在申请设备号。后面注册,注销都是和register_chrdev_region一样。cdev_init,cdev_add,unregister_chrdev_region,cdev_del。

2.自动创建设备文件

        1.相关函数

        class_create、device_create和device_destroy,class_destroy。

adxl34x_class = class_create(THIS_MODULE, "adxl345"); 
device_create(adxl34x_class, NULL, MKDEV(adxl34x_major, 0), NULL, "adxl345"); 

device_destroy(adxl34x_class,  MKDEV(adxl34x_major, 0));
class_destroy(adxl34x_class);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值