linux驱动总结

linux驱动总结

一.前言

做linux开发也有一段时间了,对整个系统已经熟悉了很多,linux是一个非常大的系统,现在对常见的驱动做一个总结,以此来加深记忆和理解。

二.常见驱动及其子系统分类

1.Linux设备分类

linux系统抽象出的设备可以分为三类:char_dev,block_dev,net_dev。字符设备是产品开发用的最多的设备,其总类繁多,所以linux抽象出了许多子系统来适配不同设备,大大减少了代码量。

三.设备模型

1.设备模型的概念

设备模型通过几个数据结构来反映当前系统中总线、设备以及驱动的工作状况,提出了以下几个重要概念。

设备(device) :

挂载在某个总线的物理设备;/sys/devices目录记录了系统中所有设备,实际上在sys目录下所有设备文件最终都会指向该目录对应的设备文件;此外还有另一个目录/sys/dev记录所有的设备节点, 但实际上都是些链接文件,同样指向了devices目录下的文件。

驱动(driver) :

与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;

总线(bus) :

负责管理挂载对应总线的设备以及驱动;在“/sys/bus”目录下的每个子目录都是注册好了的总线类型,每个总线类型下包含两个子目录——devices和drivers文件夹;其中devices下是该总线类型下的所有设备(符号链接),它们分别指向真正的设备在"/sys/devices/"下;而drivers下是所有注册在这个总线上的驱动,每个driver子目录下 是一些可以观察和修改的driver参数。常见的总线类型参考如下:

类(class) :

对于具有相同功能的设备,归结到一种类别,进行分类管理;/sys/class目录下则是包含所有注册在kernel里面的设备类型,这是按照设备功能分类的设备模型, 我们知道每种设备都具有自己特定的功能,比如:鼠标的功能是作为人机交互的输入,按照设备功能分类无论它 挂载在哪条总线上都是归类到/sys/class/input下。

3.总线-设备-驱动工作原理

总线上面有device和driver两个链表,向系统注册一个驱动时,驱动的管理链表会被插入一个新的驱动节点,同理,向系统注册一个设备时,会向设备的管理连链表插入一个新的设备。在插入的同时总线会执行一个bus_type结构体中match的方法对新插入的设备/驱动进行匹配。 在匹配成功的时候会调用驱动device_driver结构体中probe方法(通常在probe中获取设备资源,具体的功能可由驱动编写人员自定义), 并且在移除设备或驱动时,会调用device_driver结构体中remove方法。match、probe、remove等方法需需要驱动coder实现。

四."总线-设备-驱动" 的数据结构

4.1 bus_type(内核源码/include/linux/device.h)

在实际编写linux驱动模块时,Linux内核已经为我们写好了大部分总线驱动,正常情况下我们一般不会去注册一个新的总线, 内核中提供了bus_register函数来注册总线,以及bus_unregister函数来注销总线,其函数原型如下:

点击查看代码

struct bus_type {

const char *name; //总线名称

const char *dev_name; //

struct device *dev_root; //

struct device_attribute *dev_attrs; /* use dev_groups instead */ //

const struct attribute_group **bus_groups; //总线的属性

const struct attribute_group **dev_groups; //设备属性

const struct attribute_group **drv_groups; //驱动属性

int (*match)(struct device *dev, struct device_driver *drv); //当向总线注册一个新的设备或者是新的驱动时,会调用该回调函数。该回调函数主要负责判断是否有注册了的驱动适合新的设备,或者新的驱动能否驱动总线上已注册但没有驱动匹配的设备;

int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //总线上的设备发生添加、移除或者其它动作时,就会调用该函数,来通知驱动做出相应的对策。

int (*probe)(struct device *dev); //当总线将设备以及驱动相匹配之后,执行该回调函数,最终会调用驱动提供的probe函数。

int (*remove)(struct device *dev); //当设备从总线移除时,调用该回调函数;

void (*shutdown)(struct device *dev); //

int (*online)(struct device *dev);

int (*offline)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state); //电源管理的相关函数,当总线进入睡眠模式时,会调用suspend回调函数;

int (*resume)(struct device *dev); //电源管理的相关函数,当唤醒总线时,调用resume的回调函数;

const struct dev_pm_ops *pm; //电源管理的结构体,存放了一系列跟总线电源管理有关的函数,与device_driver结构体中的pm_ops有关;

const struct iommu_ops *iommu_ops;

struct subsys_private *p; //该结构体用于存放特定的私有数据,其成员klist_devices和klist_drivers记录了挂载在该总线的设备和驱动;

struct lock_class_key lock_key;

};

当我们成功注册总线时,会在/sys/bus/目录下创建一个新目录,目录名为我们新注册的总线名。bus目录中包含了当前系统中已经注册了的所有总线,例如i2c,spi,platform等。我们看到每个总线目录都拥有两个子目录devices和drivers, 分别记录着挂载在该总线的所有设备以及驱动。

4.2device结构体(内核源码/include/linux/device.h)

点击查看代码

struct device {

struct device *parent;

struct device_private *p;

struct kobject kobj;

const char *init_name;

const struct device_type *type;

struct mutex mutex;

struct bus_type *bus; /* type of bus device is on */

struct device_driver *driver; /* which driver has allocated this

device */

void *platform_data; /* Platform specific data, device

core doesn't touch it */

void *driver_data; /* Driver data, set and get with

dev_set/get_drvdata */

struct dev_pm_info power;

struct dev_pm_domain *pm_domain;

#ifdef CONFIG_PINCTRL

struct dev_pin_info *pins;

#endif

#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;

unsigned long dma_pfn_offset;

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 */

#ifdef CONFIG_DMA_CMA

struct cma *cma_area; /* contiguous memory area for dma

allocations */

#endif

/* arch specific additions */

struct dev_archdata archdata;

struct device_node *of_node; /* associated device tree node */

struct fwnode_handle *fwnode; /* firmware device node */

dev_t devt; /* dev_t, creates the sysfs "dev" */

u32 id; /* device instance */

spinlock_t devres_lock;

struct list_head devres_head;

struct klist_node knode_class;

struct class *class;

const struct attribute_group **groups; /* optional groups */

void (*release)(struct device *dev);

struct iommu_group *iommu_group;

bool offline_disabled:1;

bool offline:1;

};

4.3 device_driver结构体(内核源码/include/linux/device.h)

点击查看代码

struct device_driver {

const char *name; //指定驱动名称,总线进行匹配时,利用该成员与设备名进行比较;

struct bus_type *bus; //表示该驱动依赖于哪个总线,内核需要保证在驱动执行之前,对应的总线能够正常工作;

struct module *owner; //表示该驱动的拥有者,一般设置为THIS_MODULE;

const char *mod_name; /* used for built-in modules */

bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

const struct of_device_id *of_match_table; //指定该驱动支持的设备类型。当内核使能设备树时,会利用该成员与设备树中的compatible属性进行比较。

const struct acpi_device_id *acpi_match_table;//

int (*probe) (struct device *dev); //当驱动以及设备匹配后,会执行该回调函数,对设备进行初始化。通常的代码,都是以main函数开始执行的,但是在内核的驱动代码,都是从probe函数开始的。

int (*remove) (struct device *dev); //当设备从操作系统中拔出或者是系统重启时,会调用该回调函数;

void (*shutdown) (struct device *dev); //

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

const struct attribute_group **groups; //指定该驱动的属性;

const struct dev_pm_ops *pm;

struct driver_private *p;

};

4.4数据结构关系图

相关推荐