微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > 嵌入式系统设计讨论 > UT4412BV03开发板学习Linux设备驱动模型(二)

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

时间:10-02 整理:3721RD 点击:

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


Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top