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