当前位置: 首页 > Linux

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

时间:2023-04-06 19:31:38 Linux

写在前面内核版本:2.6.35文中贴出的源码会省略一些不利于分析的琐碎细节。该结构包含其他结构。称为包含关系,类似于面向对象中的继承;该结构包含指向其他结构的指针,我称之为绑定关系。软件层面的框架就是把一个任务的整体逻辑、通用逻辑、固定逻辑提炼出来。通过对外暴露一定的接口,获取运行所必需的数据和具体的运行逻辑(函数)。led框架由内核开发者提供,位于/drivers/leds/led-class.c。两个重要的函数是leds_init和led_classdev_register1。leds_init函数分析staticstructclass*leds_class;staticint__initleds_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数组return0;}该函数的主要工作是创建并填充一个名为leds的类实体,更详细的分析如下1.1aboutclass//include/linux/device.hstructclass{constchar*姓名;//类名structclass_attribute*class_attrs;//class_attribute数组structdevice_attribute*dev_attrs;//dev_attribute数组,用于描述属于该类的共享设备属性//----一些函数指针----int(*suspend)(structdevice*dev,pm_message_tstate);诠释(*简历)(结构设备*dev);};类结构在语义上用于描述一类设备的公共属性和操作,类实体用于描述一类设备,因此在leds_init函数中创建了一个名为leds的类实体用于描述公共的LED设备的属性和操作逻辑。类结构不同于面向对象语言中类的概念。这里只是一个普通的结构。要对类有更完整的了解,还需要进一步了解属性结构。class结构绑定了一个class_attribute类型和device_attribute类型的数组。前者用于描述类的属性,后者用于描述属于该类的设备的公共属性;两个结构体里面都包含属性结构体,可以认为这两个结构体是属性的子类。//include/linux/device.hstructclass_attribute{structattributeattr;//-----类属性的读写方法--------ssize_t(*show)(structclass*class,structclass_attribute*attr,char*buf);ssize_t(*store)(structclass*class,structclass_attribute*attr,constchar*buf,size_tcount);};structdevice_attribute{结构属性属性;//设备属性的读写方法ssize_t(*show)(structdevice*dev,structdevice_attribute*attr,char*buf);ssize_t(*store)(structdevice*dev,structdevice_attribute*attr,constchar*buf,size_tcount);};//include/linux/sysfs.hstruct属性{constchar*name;//属性名,在sysfs中表示为文件名mode_tmode;//文件权限};从attribute的声明位置可以看出,attribute是设计用来在sysfs中使用的,也就是提供用户和内核交互的接口。属性实体在sysfs中会以文件的形式出现,用户对其的读写操作会追溯到内核中具体的show和store方法。因此,如果想通过读写属性文件来控制硬件设备,需要在驱动程序中相应的属性中填写函数指针。函数指针填充在leds_init函数中使用的led_class_attrs数组中。代码如下:staticstructdevice_attributeled_class_attrs[]={__ATTR(brightness,0644,led_brightness_show,led_brightness_store),//亮度属性,填充读写方法__ATTR(max_brightness,0444,led_max_brightness_show,NULL),//max_brightness属性,填写read方法,相应的文件权限也设置为只读...};1.2class_create函数解析class_create内部调用调用__class_create,所以我们直接分析后者;//driver/base/class.cstructclass*__class_create(structmodule*owner,constchar*name,structlock_class_key*key){//---分配堆空间---structclass*cls;cls=kzalloc(sizeof(*cls),GFP_KERNEL);//---结构填充---cls->name=name;cls->owner=所有者;cls->class_release=class_create_release;//填充类的释放函数,刚出生就死了__class_register(cls,key);//将新创建的类实体注册到内核returncls;}标准先分配空间然后填充进程,最后调用__class_register将创建的类注册到内核。至于怎么注册,这里就不分析了,也没进去看,无非是把创建的类的信息填充到内核维护的某个数组中。至此,对leds_init函数的分析就完成了,下面我们来整体描述一下它的执行过程:内核在启动过程的某个阶段会调用leds_int函数,首先使用class_create创建并注册leds类;然后填写暂停和恢复功能;然后使用文件中定义的led_class_attrs数组填充leds类中的device_attribute数组,实现属性文件和读写功能的绑定。2.led_classdev_register函数分析/**parent:要创建的设备的父设备*led_cdev:需要框架用户提供的结构体*/intled_classdev_register(structdevice*parent,structled_classdev*led_cdev){//创建设备实体,填充到传入的led_classdev实体led_cdev->dev=device_create(leds_class,parent,0,led_cdev,"%s",led_cdev->name);return0;}这个函数主要是创建一个device实体,并绑定到leds_class,当然device_create函数里面必须要注册device实体。//include/linux/leds.hstructled_classdev{constchar*name;内部亮度;//亮度intmax_brightness;//最大亮度intflags;结构设备*开发;//绑定dev结构体,也应该理解为parentClassvoid(*brightness_set)(structled_classdev*led_cdev,enumled_brightnessbrightness);枚举led_brightness(*brightness_get)(structled_classdev*led_cdev);};led_classdev在语义上可以理解为dev的子类,是对led设备的描述。框架的用户应该创建led_classdev实体并将其传递给led_classdev_register函数。综上,led框架的源码大致分析完毕。框架暴露给开发者的接口是led_classdev_register。开发者准备led_classdev实体,它提供框架需要的数据(如设备名称、亮度)和操作逻辑(设置亮度等)。当然还有led_classdev_unregister接口,比较简单,这里就不分析了。