完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
UT4412BV03开发板学习Linux设备驱动模型(二) 设备驱动模型有三个重要部分,分别是总线(bus_type),设备(device),驱动(driver) 下面对三个组件分别进行介绍。 一.总线 从硬件上来讲,物理总线有数据总线和地址总线,在设备驱动模型中所有设备都是通过总线相连接的,驱动程序依附在总线上,下面将表示总线,设备,驱动三者之间的关系。 了解了总线的结构之后,下面具体说明总线中用到的一些结构体及相关的函数。 #include 1.总线的数据结构bus_type struct bus_type { const char *name; //总线的名字 struct bus_attribute *bus_attrs; //总线属性和导出到sysfs中的方法 struct device_attribute *dev_attrs; //设备属性和导出到sysfs中的方法 struct driver_attribute *drv_attrs; //驱动程序属性和导出到sysfs中的方法 int (*match)(struct device *dev, struct device_driver *drv);//匹配函数,检验参数二的驱动是否支持参数一的设备 //当一条总线上新设备或新驱动被添加时,会一次或多次调用该函数, // 如果指定的驱动能适用于指定的设备,那么该函数返回非0,否则返回0 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); //探测函数 int (*remove)(struct device *dev); //移除函数 void (*shutdown)(struct device *dev); //关闭函数 int (*suspend)(struct device *dev, pm_message_t state); //改变供电状态,使其节能 int (*suspend_late)(struct device *dev, pm_message_t state); //挂起函数 int (*resume_early)(struct device *dev); //唤醒函数 int (*resume)(struct device *dev); //恢复供电状态,是设备正常工作的方法 struct dev_pm_ops *pm; //关于电源管理的操作符 struct bus_type_private *p; //总线私有数据 }; 2.总线属性数据结构 struct bus_attribute { struct attribute attr; //总线属性的变量 ssize_t (*show)(struct bus_type *bus, char *buf); //属性读函数 ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); //属性写函数 }; struct attribute { const char *name; //属性名字 struct module *owner; mode_t mode; //属性读写权限 }; 3.初始化bus_attribute结构体用的宏 #define BUS_ATTR(_name, _mode, _show, _store) struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store) 例: static BUS_ATTR(config, 0644, ap_config_time_show,ap_config_time_store); 对此宏进行扩展为 struct bus_attribute bus_attr_config_time={ .attr={.name=config_time,.mode=0644}, .show=ap_config_time_show, .store=ap_config_time_store, } 4.创建总线属性的函数 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) { int error; if (bus_get(bus)) { error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); bus_put(bus); } else error = -EINVAL; return error; } 5.移除总线属性用到的函数 void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) { if (bus_get(bus)) { sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr); bus_put(bus); } } 6.总线上用到的方法 int (*match)(struct device *dev, struct device_driver *drv);//匹配函数,检验参数二的驱动是否支持参数一的设备 //当一条总线上新设备或新驱动被添加时,会一次或多次调用该函数, // 如果指定的驱动能适用于指定的设备,那么该函数返回非0,否则返回0 当用户空间产生热插拔事件前,可能需要内核传递一些参数给用户空间,这里只能使用环境变量来传递, 传递环境变量用到的函数 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); 该函数只能在内核支持热插拔事件时才有用,否则该函数被定义为NULL。 举例: static int amba_uevent(struct device *dev, struct kobj_uevent_env *env) { struct amba_device *pcdev = to_amba_device(dev); int retval = 0; //向env中添加环境变量“AMBA_ID” retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid); return retval; } #else #define amba_uevent NULL //表示不支持热插拔事件 #endif 7.总线注册函数 int bus_register(struct bus_type *bus) 为驱动程序定义一条新总线,调用此函数,这函数可能会调用失败,因此我们一般 要检查其返回值,如果调用成功将在/sys/bus目录下生成总线目录 8.总线注销函数 void bus_unregister(struct bus_type *bus) { pr_debug("bus: '%s': unregisteringn", bus->name); bus_remove_attrs(bus); remove_probe_files(bus); kset_unregister(bus->p->drivers_kset); kset_unregister(bus->p->devices_kset); bus_remove_file(bus, &bus_attr_uevent); kset_unregister(&bus->p->subsys); kfree(bus->p); bus->p = NULL; } 二.设备 在linux驱动中,每一个设备都由一个device结构体来描述,对于驱动开发者来说,当遇到新设备时, 需要定义一个新的设备结构体,并将device这个结构体包含在新的设备结构体中 1.device结构体 struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ struct device_type *type; struct semaphore sem; /* semaphore to synchronize calls to * its driver */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask; /* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ struct dev_archdata archdata; /* arch specific additions */ dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; 2.设备注册用到的函数 设备必须要向linux内核注册后才能使用,下面是设备的注册函数 int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } 3.设备卸载用到的函数 void device_unregister(struct device *dev) { pr_debug("device: '%s': %sn", dev_name(dev), __func__); device_del(dev); put_device(dev); } 4.设备属性结构体 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); //设置属性的方法 }; 写程序时,可以使用宏DEVICE_ATTR初始化attribute结构体 #define DEVICE_ATTR(_name, _mode, _show, _store) struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) 5.用device_create_file函数在device目录下创建一个属性文件 int device_create_file(struct device *dev, struct device_attribute *attr) { int error = 0; if (dev) error = sysfs_create_file(&dev->kobj, &attr->attr); return error; } 6.用device_remove_file函数在device目录下删除一个属性文件 void device_remove_file(struct device *dev, struct device_attribute *attr) { if (dev) sysfs_remove_file(&dev->kobj, &attr->attr); } 三.驱动 在设备驱动模型中,记录了注册到系统中的所有设备,并不是所有设备都能使用,本节重点介绍驱动和设备需要绑定在一起才能使用。一个设备对应一个驱动,一个驱动可能对应多个设备驱动,设备驱动模型中的探测函数自动探测新设备,并为其分配一个合适的驱动,这样新设备就能够使用了,对于驱动都应该有下面的驱动结构体。 1.设备驱动device_driver结构体 struct device_driver { const char *name; /*设备驱动程序的名字*/ struct bus_type *bus; /*指向驱动属于的总线,总线上有很多设备*/ struct module *owner; /*设备驱动自身的模块*/ const char *mod_name; /* used for built-in modules */ /*驱动模块的名字*/ /*探测设备的方法,并检测设备驱动可以控制哪些装备*/ int (*probe) (struct device *dev); int (*remove) (struct device *dev); /*移除设备时调用该方法*/ void (*shutdown) (struct device *dev); /*设备关闭时调用的方法*/ int (*suspend) (struct device *dev, pm_message_t state); /*设备置于低功率状态时所调用的方法*/ int (*resume) (struct device *dev); /*设备恢复正常状态时所调用的方法*/ struct attribute_group **groups; /*属性组*/ struct dev_pm_ops *pm; /*用于电源管理*/ struct driver_private *p; /*设备驱动的私有数据*/ }; struct driver_private { struct kobject kobj; /*内嵌的kobject结构,用来构建设备驱动模型的结构*/ struct klist klist_devices; /*该设备支持的所有设备链表*/ struct klist_node knode_bus; /*该驱动所属总线*/ struct module_kobject *mkobj; /*驱动的模块*/ struct device_driver *driver; /*指向驱动本身*/ }; 3.驱动的属性结构体 struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); }; 4.在驱动所属目录中创建一个属性 int driver_create_file(struct device_driver *drv, struct driver_attribute *attr) { int error; if (drv) error = sysfs_create_file(&drv->p->kobj, &attr->attr); else error = -EINVAL; return error; } 5.在驱动所属目录中删除一个属性 void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr) { if (drv) sysfs_remove_file(&drv->p->kobj, &attr->attr); } 4.驱动程序的注册 此函数是向设备驱动模型中插入一个新的device_driver对象 int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) printk(KERN_WARNING "Driver '%s' needs updating - please use " "bus_type methodsn", drv->name); other = driver_find(drv->name, drv->bus); if (other) { put_driver(other); printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...n", drv->name); return -EEXIST; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); return ret; } 4.设备驱动程序的注销 void driver_unregister(struct device_driver *drv) { if (!drv || !drv->p) { WARN(1, "Unexpected driver unregister!n"); return; } driver_remove_groups(drv, drv->groups);//从组中移除该驱动 bus_remove_driver(drv); //从总线中移除驱动 } 以上内容简单的梳理了一下linux系统中设备驱动模型的相关知识,分析了linux设备驱动模型中设备,总线,驱动三者之间的关系,并将设备驱动模型中的总线,设备,驱动的重要结构体,及函数进行了相关分析,这样将有助于在以后用到设备驱动模型编程时,能够快速的理解和编写设备驱动程序。 |
|
相关推荐
|
|
飞凌嵌入式-ELFBOARD 硬件知识分享-ELF 2电源电路讲解
563 浏览 0 评论
1515 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像
1357 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程之烧写方法
793 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-内核编译之初次编译
1239 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 20:20 , Processed in 0.522054 second(s), Total 36, Slave 29 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号