q2113435929的个人空间 https://www.eechina.com/space-uid-126185.html [收藏] [复制] [RSS]

博客

UT4412BV03开发板学习Linux设备驱动模型(二)

已有 1390 次阅读2015-10-16 14:25 | 4412, 开发板

UT4412BV03开发板学习Linux设备驱动模型(二)

 

设备驱动模型有三个重要部分分别是总线(bus_type),设备(device),驱动(driver)

下面对三个组件分别进行介绍。

 

一.总线

从硬件上来讲,物理总线有数据总线和地址总线,在设备驱动模型中所有设备都是通过总线相连接的,驱动程序依附在总线上,下面将表示总线,设备,驱动三者之间的关系

了解了总线的结构之后,下面具体说明总线中用到的一些结构体及相关的函数。

#include <asm/device.h>

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': unregistering\n", 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': %s\n", 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 methods\n", 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设备驱动模型中设备,总线,驱动三者之间的关系,并将设备驱动模型中的总线,设备,驱动的重要结构体,及函数进行了相关分析,这样将有助于在以后用到设备驱动模型编程时,能够快速的理解和编写设备驱动程序。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
返回顶部