字符设备驱动3:字符类设备驱动框架分析

前面的博文循规蹈矩按照无驱动框架的步骤分析了一个简单的字符设备驱动,但是现如今更多是使用内核开发者提供的驱动框架来完成驱动的注册,这样的做法即可减少代码的错误率,也能避免错误例如内存申请忘记释放的问题,更能简化驱动的开发难度,这里就以一个简答的led类设备驱动架构为例,分析字符设备驱动框架:

驱动框架实际也是一个驱动代码,他把很多的,一类设备都会进行的错误放在一个较早的时间,在内核模块加载的初期,就将其作为一个模块加载进去了,并未驱动开发者提供了很多便利的注册接口函数。

1.驱动框架自身完成的部分:

subsys_initcall(leds_init);

subsys_initcall()的等级高于module_init()函数,所以在真正的驱动加载之前他已经作为一个模块加载进了内核之中。

static int __init leds_init(void)
{
	leds_class = class_create(THIS_MODULE, "leds");
	if (IS_ERR(leds_class))
		return PTR_ERR(leds_class);
	leds_class->suspend = led_suspend;
	leds_class->resume = led_resume;
	leds_class->dev_attrs = led_class_attrs;
	return 0;
}

(1)led设备类的创建:
 

leds_class = class_create(THIS_MODULE, "leds");

此后在/sys/class目录下就会有一个leds目录,此后使用此框架注册的led设备都可以在leds目录下找到;

(2)将创建的类less进行一些固定内容的填充:

leds_class->suspend = led_suspend;
leds_class->resume = led_resume;
leds_class->dev_attrs = led_class_attrs;

 

2.框架提供的led类设备注册函数:
 

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
				      "%s", led_cdev->name);
	if (IS_ERR(led_cdev->dev))
		return PTR_ERR(led_cdev->dev);

#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
	/* add to the list of leds */
	down_write(&leds_list_lock);
	list_add_tail(&led_cdev->node, &leds_list);
	up_write(&leds_list_lock);

	if (!led_cdev->max_brightness)
		led_cdev->max_brightness = LED_FULL;

	led_update_brightness(led_cdev);

#ifdef CONFIG_LEDS_TRIGGERS
	led_trigger_set_default(led_cdev);
#endif

	printk(KERN_DEBUG "Registered led device: %s\n",
			led_cdev->name);

	return 0;
}

EXPORT_SYMBOL_GPL(led_classdev_register);

(1)进行设备的注册:

led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,"%s", led_cdev->name);

这里规定了注册的设备属于leds类设备,系统动态的为其分配设备号,

(2)将注册的设备放进leds类设备维护的链表内:
 

down_write(&leds_list_lock);
list_add_tail(&led_cdev->node, &leds_list);
up_write(&leds_list_lock);

if (!led_cdev->max_brightness)
		led_cdev->max_brightness = LED_FULL;

led_update_brightness(led_cdev);

(3)最后导出这个接口函数给驱动开发者使用:

EXPORT_SYMBOL_GPL(led_classdev_register);

 

3.注销驱动函数:

void led_classdev_unregister(struct led_classdev *led_cdev)
{
#ifdef CONFIG_LEDS_TRIGGERS
	down_write(&led_cdev->trigger_lock);
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
	up_write(&led_cdev->trigger_lock);
#endif

	device_unregister(led_cdev->dev);

	down_write(&leds_list_lock);
	list_del(&led_cdev->node);
	up_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

(1)调用最原始的接口进行注销:

device_unregister(led_cdev->dev);

(2)将注册的驱动相关的结构体从管理的链表中删除:

down_write(&leds_list_lock);
list_del(&led_cdev->node);
up_write(&leds_list_lock);

可见驱动框架帮我们完成了很多的基础工作,而将与设备直接相关的一些特异性的东西留给开发者自己去填充,这样驱动架构和驱动一起工作,完成完整的驱动工作。

End。。。。。。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值