1:在linux2.6板本内核开发人员开始创建驱动框架,以led驱动为例:html
没有驱动框架的时候咱们须要作一下事情:java
module_init: node
1:alloc_chrdev_region 注册字符驱动linux
2:cdev_alloc、cdev_init、cdev_add来向内核中添加驱动;c++
3:class_create 建立类编程
4:device_create建立驱动设备cdevapi
5:ioremap/使用静态的虚拟地址数组
module_exit:框架
1:iounmap编程语言
2:release_mem_region
3:device_destroy
4:class_destroy
5:cdev_del
6:unregister_chrdev_region
咱们在不使用驱动框架的状况下须要本身来作以上事情,比较繁琐;下面来分析一下led的驱动框架:
内核工程师创建linux驱动框架的两个文件在/drivers/leds目录下文件名为:led-core.c、led-class.c
咱们首先分下一下led-class.c文件,看一下内核工程师关于led驱动都作了哪些事情,
subsys_initcall(leds_init);
module_exit(leds_exit);
subsys_initcall 在/include/linux/init.h头文件中:
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define __define_initcall(level,fn,id) static initcall_t __initcall_##fn##id __used __attribute__((__section__(".initcall" level ".init"))) = fn
展开之后:
static initcall_t __initcall_leds_init4 __used __attribute__((__section__(".initcall"4."init"))) = leds_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; }
首先是内核工程师创建的模块初始化:
leds_init 这个函数中调用的是class_create函数,建立了leds这个类,因此在/sys/class目录下会有leds这个类目录;
而且对这个类初始化
struct class { const char *name; struct module *owner; struct class_attribute *class_attrs; struct device_attribute *dev_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 class_private *p; };
static struct device_attribute led_class_attrs[] = { __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), #endif __ATTR_NULL, };
/************************************
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
*************************************************************/
这里是一个结构体数组,里面每一个__ATTR宏表达式都是一个结构体,这个宏的结构以下所示,这个宏就是对device_attribute里的元素赋值;实际上device_attribute
结构体最终会经过device_create函数建立成具备必定权限的文件,文件名为 name 权限 mode_t 文件读的方式:.show 写的方式.store
这个文件直接表明了硬件的一个特性,以 __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),为例会在leds目录下建立一个brightness的文件,这个
文件使用来控制硬件led的brightness属性的,这个文件的执行权限位0644 即110100100 文件全部者权限可读可写。。。咱们最终操做硬件就是经过读写这个文件来实现的,
这个文件的读写方法就是.show 和 .store 方法,
struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };
struct attribute { const char *name; struct module *owner; mode_t mode; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key *key; struct lock_class_key skey; #endif };
下面咱们看一下linux内核为咱们构建的一个led设备结构:
struct led_classdev { const char *name; int brightness; int max_brightness; int flags; /* Lower 16 bits reflect status */
#define LED_SUSPENDED (1 << 0)
/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME (1 << 16)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness); /* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); /* Activate hardware accelerated blink, delays are in * miliseconds and if none is provided then a sensible default * should be chosen. The call can adjust the timings if it can't * match the values specified exactly. */
int (*blink_set)(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off); struct device *dev; struct list_head node; /* LED Device list */
const char *default_trigger; /* Trigger to use */ #ifdef CONFIG_LEDS_TRIGGERS /* Protects the trigger data below */
struct rw_semaphore trigger_lock; struct led_trigger *trigger; struct list_head trig_list; void *trigger_data; #endif };
上面这个结构体就是linux内核为咱们建立的通用的led设备模型,一个结构体对应一个led设备,这种思想就是面向对象的思想,其实c++以及java这种面向对象的编程语言,底层的实现
就是基于这方方式的,把一个对象的全部名称、属性,以及操做方法封装在一个结构体中,咱们对这个对象执行的操做就至关于对这个结构体执行操做就能够了;
下面对这个结构体中的元素分析一下
*name:设备命
brightness 亮度
max_brightness:最大亮度
brightness_set函数指针,这个设置指向真正的设置亮度的函数
(
led_brightness_store
led_set_brightness
led_cdev->brightness_set(led_cdev, value);
//能够看出linux内核中调用是这么调用的应用层对brightness文件的写操做执行的是.store对应的led_brightness_store 而后调用led_set_brightness而后在调用结构体中的brightness_set函数真正的执行led的写操做这里一共有4层调用;一样show也是这个原理;
)
。。。。。。。。。。。。。。。。。。。
在来看一下驱动工程师应该作的事情,
这个函数中作的事情:
1:
struct s3c24xx_gpio_led *led;
struct s3c24xx_gpio_led { struct led_classdev cdev; struct s3c24xx_led_platdata *pdata; };
建立led驱动设备的结构体指针, struct led_classdev cdev; 这个结构体就是咱们上面分析的led设备结构体
led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
用kzalloc函数对这个指针分配内存;
led->cdev.brightness_set = s3c24xx_led_set; led->cdev.default_trigger = pdata->def_trigger; led->cdev.name = pdata->name; led->cdev.flags |= LED_CORE_SUSPENDRESUME; led->pdata = pdata; /* no point in having a pull-up if we are always driving */ if (pdata->flags & S3C24XX_LEDF_TRISTATE) { s3c2410_gpio_setpin(pdata->gpio, 0); s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT); } else { s3c2410_gpio_pullup(pdata->gpio, 0); s3c2410_gpio_setpin(pdata->gpio, 0); s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT); }
对这个结构体赋值,关键的最经常使用的几个值:
name
brightness_set
ret = led_classdev_register(&dev->dev, &led->cdev);
接下来就是注册led设备了;
led_classdev_register
device_create
在调用device_create函数建立相应的文件;
------------------------------------------------------------------------------------------------------------------------------------------
最后在分析一下本身写的led驱动与使用驱动框架的本质区别在哪里:
本身写的使用通用的设备模型cdev这个结构体,对设备操做时使用内核提供的file_opetations操做函数结构体,而应用层执行使用api调用/dev/目录下的设备文件节点直接操做便可;
而使用框架的话,就是内核专门为led提供了一个led的设备驱动模型,把全部的关于led的操做属性,都放到了这个驱动模型中,而后驱动工程师在对这个驱动模型进行初始化便可;
这就是使用和不使用驱动模型的本质区别;
参考文章:http://www.2cto.com/kf/201609/545540.html
-----------------------------------------------------------------------------------------------------------------------------------------------------
补充:对九鼎写的x210驱动进行分析(未完待续)