Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)文章的例子和实验使用《LDD3》所配的lddbus模块(稍作修改)。提示:在学习这部分内容是一定要分析所有介绍的源代码,知道他们与上一部分内容(kobject、kset、attribute等等)的关系,最好要分析一个实际的“flatformdevice”设备,不然会只学到表象,到后面会不知所云的。总线总线是处理器和一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连,甚至是内部的虚拟platform总线。总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。在Linux设备模型中,总线由bus_type结构表示,定义在linux/device.h:structbus_type{constchar*name;/*总线类型名称*/structmodule*owner;/*指向模块的指针(如果有),此模块负责操作这个总线*/structksetsubsys;/*与该总线相关的子系统*/structksetdrivers;/*总线驱动程序的kset*/structksetdevices;/*挂在该总线的所有设备的kset*/structklistklist_devices;/*与该总线相关的驱动程序链表*/structklistklist_drivers;/*挂接在该总线的设备链表*/structblocking_notifier_headbus_notifier;structbus_attribute*bus_attrs;/*总线属性*/structdevice_attribute*dev_attrs;/*设备属性,指向为每个加入总线的设备建立的默认属性链表*/structdriver_attribute*drv_attrs;/*驱动程序属性*/structbus_attributedrivers_autoprobe_attr;/*驱动自动探测属性*/structbus_attributedrivers_probe_attr;/*驱动探测属性*/int(*match)(structdevice*dev,structdevice_driver*drv);int(*uevent)(structdevice*dev,char**envp,intnum_envp,char*buffer,intbuffer_size);int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*suspend_late)(structdevice*dev,pm_message_tstate);int(*resume_early)(structdevice*dev);nt(*resume)(structdevice*dev);/*处理热插拔、电源管理、探测和移除等事件的方法*/unsignedintdrivers_autoprobe:1;};在更新的内核里,这个结构体变得更简洁了,隐藏了无需驱动编程人员知道的一些成员:/*inLinux2.6.26.5*/structbus_type{constchar*name;structbus_attribute*bus_attrs;structdevice_attribute*dev_attrs;structdriver_attribute*drv_attrs;int(*match)(structdevice*dev,structdevice_driver*drv);int(*uevent)(structdevice*dev,structkobj_uevent_env*env);int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*suspend_late)(structdevice*dev,pm_message_tstate);int(*resume_early)(structdevice*dev);int(*resume)(structdevice*dev);structbus_type_private*p;};structbus_type_private{structksetsubsys;structkset*drivers_kset;structkset*devices_kset;structklistklist_devices;structklistklist_drivers;structblocking_notifier_headbus_notifier;unsignedintdrivers_autoprobe:1;structbus_type*bus;};总线的注册和删除总线的主要注册步骤:(1)申明和初始化bus_type结构体。只有很少的bus_type成员需要初始化,大部分都由设备模型核心控制。但必须为总线指定名字及一些必要的方法。例如:structbus_typeldd_bus_type={.name=ldd,.match=ldd_match,.uevent=ldd_uevent,};(2)调用bus_register函数注册总线。intbus_register(structbus_type*bus)调用可能失败,所以必须始终检查返回值。若成功,新的总线子系统将被添加进系统,并可在sysfs的/sys/bus下看到。之后可以向总线添加设备。例如:ret=bus_register(&ldd_bus_type);if(ret)returnret;当必须从系统中删除一个总线时,调用:voidbus_unregister(structbus_type*bus);总线方法在bus_type结构中定义了许多方法,它们允许总线核心作为设备核心和单独的驱动程序之间提供服务的中介,主要介绍以下两个方法:int(*match)(structdevice*dev,structdevice_driver*drv);/*当一个新设备或者驱动被添加到这个总线时,这个方法会被调用一次或多次,若指定的驱动程序能够处理指定的设备,则返回非零值。必须在总线层使用这个函数,因为那里存在正确的逻辑,核心内核不知道如何为每个总线类型匹配设备和驱动程序*/int(*uevent)(structdevice*dev,char**envp,intnum_envp,char*buffer,intbuffer_size);/*在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量(参数和kset的uevent方法相同)*/lddbus的match和uevent方法:staticintldd_match(structdevice*dev,structdevice_driver*driver){return!strncmp(dev-bus_id,driver-name,strlen(driver-name));}/*仅简单比较驱动和设备的名字*//*当涉及实际硬件时,match函数常常对设备提供的硬件ID和驱动所支持的ID做比较*/staticintldd_uevent(structdevice*dev,char**envp,intnum_envp,char*buffer,intbuffer_size){envp[0]=buffer;if(snprintf(buffer,buffer_size,LDDBUS_VERSION=%s,Version)=buffer_size)return-ENOMEM;envp[1]=NULL;return0;}/*在环境变量中加入lddbus源码的当前版本号*/对设备和驱动的迭代若要编写总线层代码,可能不得不对所有已经注册到总线的设备或驱动进行一些操作,这可能需要仔细研究嵌入到bus_type结构中的其他数据结构,但最好使用内核提供的辅助函数:intbus_for_each_dev(structbus_type*bus,structdevice*start,void*data,int(*fn)(structdevice*,void*));intbus_for_each_drv(structbus_type*bus,structdevice_driver*start,void*data,int(*fn)(structdevice_driver*,void*));/*这两个函数迭代总线上的每个设备或驱动程序,将关联的device或device_driver传递给fn,同时传递data值。若start为NULL,则从第一个设备开始;否则从start之后的第一个设备开始。若fn返回非零值,迭代停止并且那个值从bus_for_each_dev或bus_for_each_drv返回。*/总线属性几乎Linux设备模型中的每一层都提供添加属性的函数,总线层也不例外。bus_attribute类型定义在linux/device.h如下:structbus_attribute{structattributeattr;ssize_t(*show)(structbus_type*,char*buf);ssize_t(*store)(structbus_type*,constchar*buf,size_tcount);};可以看出structbus_attribute和structattribute很相似,其实大部分在kobject级上的设备模型层都是以这种方式工作。内核提供了一个宏在编译时创建和初始化bus_attribute结构:BUS_ATTR(_name,_mode,_show,_store)/*这个宏声明一个结构,将bus_attr_作为给定_name的前缀来创建总线的真正名称*//*总线的属性必须显式调用bus_create_file来创建:*/intbus_create_file(structbus_type*bus,structbus_attribute*attr);/*删除总线的属性调用:*/voidbus_remove_file(structbus_type*bus,structbus_attribute*attr);例如创建一个包含源码版本号简单属性文件方法如下:staticssize_tshow_bus_version(structbus_type*bus,char*buf){returnsnprintf(buf,PAGE_SIZE,%s\n,Version);}staticBUS_ATTR(version,S_IRUGO,show_bus_version,NULL);/*在模块加载时创建属性文件:*/if(bus_create_file(&ldd_bus_type,&bus_attr_version))printk(KERN_NOTICEUnabletocreateversionattribute\n);/*这个调用创建一个包含lddbus代码的版本号的属性文件(/sys/bus/ldd/version)*/设备在最底层,Linux系统中的每个设备由一个structdevice代表:structdevice{structklistklist_children;structklist_nodeknode_parent;/*nodeinsiblinglist*/structklist_nodeknode_driver;

1 / 25
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功