led驱动框架:led-class.c源码分析

文章详细分析了Linux内核2.6.35版本中的LED框架,包括leds_init函数创建并填充leds类,以及led_classdev_register函数用于创建device实体。class结构体描述设备类的通用属性和操作,device_attribute和class_attribute用于在sysfs中提供用户接口。开发者通过led_classdev_register接口注册LED设备,提供亮度等相关信息和操作逻辑。

写在前面

1. 内核版本:2.6.35

2. 文章中贴出的源码会省略一些琐碎的、对分析无益的细节

3. 结构体中包含其他结构体,我称之为包含关系,类似面向对象中的继承;结构体中包含其他结构体的指针,我称之为绑定关系

4. 软件层面的框架就是提取出一个任务中整体的、通用的、固定的逻辑,通过对外暴露一定的接口来获取运行所必需的数据和具体操作逻辑(函数)

5. led框架由内核开发者提供,位于/drivers/leds/led-class.c,其中比较重要的两个函数是leds_initled_classdev_register

1. leds_init函数分析

static struct class *leds_class;
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds"); //创建leds类设备
leds_class->suspend = led_suspend;  //填充函数指针
leds_class->resume = led_resume; //填充函数指针
leds_class->dev_attrs = led_class_attrs; //填充device_attribute数组
return 0;
}

该函数主要工作就是创建并填充一个名为ledsclass实体,更加细致的分析如下

1.1 关于class

//  include/linux/device.h
struct class {
const char *name; //类的名字
struct class_attribute *class_attrs; //class_attribute数组
struct device_attribute *dev_attrs; //dev_attribute数组,用来描述属于该类设备所共有的属性
//----一些函数指针----
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
};

class结构在语义上用来描述一类设备所具有的通用的属性和操作,一个class实体用来描述一类设备,所以leds_init函数中创建了一个名为ledsclass实体来描述`led`设备的共有属性和操作逻辑。

class结构不同于面向对象语言中的类的概念,在这里它只是一个普通的结构。

想要更完整地了解class,还需要进一步地理解attribute结构。

class结构中绑定class_attribute类型和device_attribute类型的数组,前者用来描述该类属性,后者用来描述属于该类的设备的共有属性;这两个结构体内部都包含了attribute结构,可以认为这两个结构是attribute子类

//  include/linux/device.h
struct class_attribute {
struct attribute attr;
//-----对类属性的读写方法-------
ssize_t (*show)(struct class *class, struct class_attribute *attr,
char *buf);
ssize_t (*store)(struct class *class, struct class_attribute *attr,
const char *buf, size_t count);
};
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);
};
//  include/linux/sysfs.h
struct attribute {
const char*name; //属性名,在sysfs中表现为文件名
mode_tmode; // 文件权限
};

attribute的声明位置可以看出,attribute设计为用来在sysfs中使用,即用来提供用户和内核的交互接口,attribute实体会表现为sysfs中的一个文件,用户对其的读写操作会追踪到内核中具体的showstore方法。

所以,如果希望通过读写attribute文件来控制硬件设备,驱动中就需要填充对应attribute中的函数指针,leds_init函数中使用的led_class_attrs数组中就实现了函数指针的填充,代码如下:

static struct device_attribute led_class_attrs[] = {
__ATTR(brightness, 0644, led_brightness_show, led_brightness_store), //brightness属性,填充读写方法
__ATTR(max_brightness, 0444, led_max_brightness_show, NULL), //max_brightness属性,填充读方法,相应的文件权限也设置为了只读
...
};

1.2 class_create函数分析

class_create内部调用__class_create,所以我们直接分析后者;

// driver/base/class.c
struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)
{
//---分配堆空间---
struct class *cls; 
cls = kzalloc(sizeof(*cls), GFP_KERNEL); 
//---结构体填充---
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release; //填充类的释放函数,刚出生就准备好怎么死了
__class_register(cls, key); //向内核注册方才创建的class实体
return cls;
}

标准的先分配空间后填充流程,最后调用__class_register向内核注册创建好的类,至于具体如何注册这里就不分析了,我也没进去看,但无非是将创建的类的信息填充到内核维护的某个数组中。

至此,leds_init函数的分析就完毕了,我们再整体描述一遍其执行过程:

内核在启动过程中的某个阶段会调用leds_int函数,该函数首先使用class_create创建并注册leds类;之后填充suspendresume函数;再使用文件内定义的led_class_attrs数组填充leds类中的device_attribute数组,实现attribute文件和读写函数的绑定。

2. led_classdev_register函数分析

/*
* parent: 要创建设备的父设备
* led_cdev: 需要框架使用者提供的结构体
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
//创建device实体,填充到传入的led_classdev实体中
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, "%s", led_cdev->name);
return 0;
}

该函数主要工作为创建device实体,将其绑定到leds_class,当然device_create函数内部一定对device实体进行了注册。

// include/linux/leds.h
struct led_classdev {
const char *name;
int brightness; //亮度
int max_brightness; //最大亮度
int flags;
struct device *dev; //绑定dev结构,也应该可以理解为父类
void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness);
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
};

led_classdev语义上可以理解为dev的子类,是对led设备的描述。框架的使用者应当创建led_classdev`实体,将其传入led_classdev_register函数。

综上,led框架的源码就算大致分析完毕了。该框架暴露给开发者的接口是led_classdev_register,开发者准备好led_classdev实体,该实体就提供了框架需要的数据(比如设备名、亮度)和操作逻辑(设置亮度等)。当然还有led_classdev_unregister接口,比较简单,这里就不分析了。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值