您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > Linux设备驱动程序学习-设备模型-总线`设备`驱动程序和类
文章的例子和实验使用《LDD3》所配的lddbus模块(转)(稍作修改)。总线总线是处理器和一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连,甚至是内部的虚拟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;};总线的注册和删除总线的主要注册步骤:(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;structklist_nodeknode_bus;structdevice*parent;/*设备的父设备,该设备所属的设备,通常一个父设备是某种总线或者主控制器.如果parent是NULL,则该设备是顶层设备,较少见*/structkobjectkobj;/*代表该设备并将其连接到结构体系中的kobject;注意:作为通用的规则,device-kobj-parent应等于device-parent-kobj*/charbus_id[BUS_ID_SIZE];/*在总线上唯一标识该设备的字符串;例如:PCI设备使用标准的PCIID格式,包含:域,总线,设备,和功能号.*/structdevice_type*type;unsignedis_registered:1;unsigneduevent_suppress:1;structdevice_attributeuevent_attr;structdevice_attribute*devt_attr;structsemaphoresem;/*semaphoretosynchronizecallstoitsdriver.*/structbus_type*bus;/*标识该设备连接在何种类型的总线上*/structdevice_driver*driver;/*管理该设备的驱动程序*/void*driver_data;/*该设备驱动使用的私有数据成员*/void*platform_data;/*Platformspecificdata,devicecoredoesn'ttouchit*/structdev_pm_infopower;#ifdefCONFIG_NUMAintnuma_node;/*NUMAnodethisdeviceiscloseto*/#endifu64*dma_mask;/*dmamask(ifdma'abledevice)*/u64coherent_dma_mask;/*Likedma_mask,butforalloc_coherentmappingsasnotallhardwaresupports64bitaddressesforconsistentallocationssuchdescriptors.*/structlist_headdma_pools;/*dmapools(ifdma'ble)*/structdma_co
本文标题:Linux设备驱动程序学习-设备模型-总线`设备`驱动程序和类
链接地址:https://www.777doc.com/doc-6040323 .html