Linux从内核的角度看外设芯片的驱动

一、前言:

面向对象的思想就是一切事物皆对象,Linux的设备驱动管理将运用这一思想对各式各样的设备、总线以及驱动进行管理。在此可以感受到老子说的:一生二,二生三,三生万物。

二、对象的“一生二”

内核对象(kernel object)是将一个数据结构 Struct kobject 作为一种公共连接部件定义到更高层的其他数据结构中去,而各个高层数据结构之间的关系通过 kobject结构以不同的链表方式表示,从而形成结构紧密的上下层次关联。通过这种 kobject 结构,可以清晰地描述总线、设备、设备上的接口等相互间的关系,使之变得更有层次,也便于系统管理者了解系统状况。

1、内核对象机制主要数据结构

Kobject是内核对象机制中最基本的数据结构,在最初的设计构思中,仅仅是想将其作为一个引用计数器。

随着时间的推移,kobjects 的作用越来越大。该结构一般不独立存在,它总是被包含在上层数据结构中,包含 kobject 的上层数据结构通过内核对象来描述与其他数据结构之间的关系。看看 kobject 的处理函数,就会发现他们其实是为其他对象提供服务的,从另一个角度来说,kobject 跟自身利益没什么关系,它仅仅为高层对象与设备模型提供纽带,用面向对象的方法,kobjects 可以看作是顶层,其他类都由该抽象类派生的。


struct kobject {
    const char *name;
    struct list_head entry;
    struct kobject *parent;
    struct kset *kset;
    struct kobj_type *ktype;
    struct kernfs_node *sd;     /* sysfs directory entry */
    struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
    struct delayed_work release;
#endif
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
};
  • 其中parent表示父对象,诸如总线和设备就是父子关系。
  • sd则与sysfs的目录项进行关联,简而言之就是每一个kobject都对应着一个sysfs下面的目录。
  • 至于kref主要是对该对象的引用进行计数管理。
  • 对 ktype 的类型kobj_type进行展开

struct kobj_type {
    void (*release)(struct kobject * kobj);
    const struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;   /* use default_groups instead */
    const struct attribute_group **default_groups;
    const struct kobj_ns_type_operations *(*child_ns_type) (struct kobject * kobj);
    const void *(*namespace)(struct kobject * kobj);
    void (*get_ownership)(struct kobject * kobj, kuid_t * uid, kgid_t * gid);
};
  • sysfs_ops这是打开sysfs文件对象后的操作接口集合
  • default_groups表示对象的属性信息

struct attribute_group {
    const char *name;
     umode_t(*is_visible) (struct kobject *, struct attribute *, int);
     umode_t(*is_bin_visible) (struct kobject *, struct bin_attribute *, int);
    struct attribute **attrs;
    struct bin_attribute **bin_attrs;
};

三、对象的“二生三”

管理的基础对象kobjectkset定义好了,那么就需要开始贴合实际,延伸出一些通用的管理类型结构。于是就有了:设备、驱动和总线,它们都将视为对象而存在。

1、device


struct device {
    /*父设备 */
    struct device *parent;
    /*该device的私有变量 */
    struct device_private *p;
    /*该设备对应的kobject */
    struct kobject kobj;
    const char *init_name;      /* initial name of the device */
    const struct device_type *type;

    struct mutex mutex;         /* mutex to synchronize calls to
                                 * its driver.
                                 */
    /*该device所需要注册的总线 */
    struct bus_type *bus;       /* type of bus device is on */
    /*该设备所绑定的驱动 */
    struct device_driver *driver;       /* which driver has allocated this
                                           device */
    /*platform模块相关的参数,device-bus-driver模型不关心该变量 */
    void *platform_data;        /* Platform specific data, device
                                   core doesn't touch it */
    /*电源管理相关的内容 */
    struct dev_pm_info power;
    struct dev_pm_domain *pm_domain;
    /*引脚配置相关的内容,可实现对该设备关联的引脚进行初始化与设置操作 */
#ifdef CONFIG_PINCTRL
    struct dev_pin_info *pins;
#endif
    /*numa相关的内容,本次不涉及 */
#ifdef CONFIG_NUMA
    int numa_node;              /* NUMA node this device is close to */
#endif
    /*以下为dma 相关的内容 */
    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 */
#ifdef CONFIG_CMA
    struct cma *cma_area;       /* contiguous memory area for dma
                                   allocations */
#endif
    /* arch specific additions */
    struct dev_archdata archdata;
    /*该变量主要用于设备树模块使用,该device_node表示dts中的节点信息,当开启OF宏时,driver会根据该device_node
       中的节点名称以及driver中of相关的节点名称,进行device-driver的匹配检测操作,从而支持设备树 */
    struct device_node *of_node;        /* associated device tree node */
    struct acpi_dev_node acpi_node;     /* associated ACPI device node */

    /*设备号,当为block/char设备时,需要设置该值,通过该值会实现该设备对应kobject与/sysfs/dev/char 、/sysfs/dev/block
       对应的kobject的链接关联 */
    dev_t devt;                 /* dev_t, creates the sysfs "dev" */
    u32 id;                     /* device instance */

    spinlock_t devres_lock;
    struct list_head devres_head;

    /*链接至系统class模块的kset的链表中 */
    struct klist_node knode_class;
    /*该设备所属的类 */
    struct class *class;
    /*该设备的默认属性组 */
    const struct attribute_group **groups;      /* optional groups */
    /*release接口,当对该设备进行释放其kobject时,会通过dev_kobj_type的release接口进行device的释放(
       该接口会调用device->release进行device的释放操作)。 */
    void (*release)(struct device * dev);
    struct iommu_group *iommu_group;
};

2、device_driver


struct device_driver {
	const char		*name;/*驱动名称*/
	struct bus_type		*bus;/*该驱动所依附的总线*/
 
    /*该驱动所属module*/
	struct module		*owner;
    /*模块的名称*/
	const char		*mod_name;	/* used for built-in modules */
 
    
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
 
    /*设备树使用的设备id*/
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;
 
    /*该驱动的探测接口*/
	int (*probe) (struct device *dev);
    /*该驱动的移除接口*/
	int (*remove) (struct device *dev);
    /*shutdown 、suspend、resume主要是对应电源管理方面的接口*/
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
    /*该驱动所相关的属性接口,主要用于与sysfs模块管理*/
	const struct attribute_group **groups;
    /*性能管理相关的内容*/
	const struct dev_pm_ops *pm;
    /*该驱动模块相关的私有变量,主要包括驱动对应的kobject、所属模块的kobject等*/
	struct driver_private *p;
};

struct driver_private {
    /*该驱动对应的kobject*/
	struct kobject kobj;
    /*用于汇聚该驱动所绑定的所有设备*/
	struct klist klist_devices;
    /*用于链接至xxx_bus_type的klist_drivers链表上*/
	struct klist_node knode_bus;
    /*该驱动所属模块的kobject*/
	struct module_kobject *mkobj;
    /*指向本driver_private类型变量所属的driver变量*/
	struct device_driver *driver;
};

3、bus_type


struct bus_type {
	/*总线名称*/
	const char		*name;
	/*总线对应设备名称*/
	const char		*dev_name;
	/*该总线对应的device*/
	struct device		*dev_root;
	struct bus_attribute	*bus_attrs;/*总线属性*/
	struct device_attribute	*dev_attrs;/*设备属性*/
	struct driver_attribute	*drv_attrs;/*驱动属性*/
	/*match接口,用于进行device与driver的匹配*/
	int (*match)(struct device *dev, struct device_driver *drv);
	/*uevent接口,用于发送kobject event,供应用层mdev/udev使用*/
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	/*总线的probe接口,该接口会调用具体驱动的probe接口*/
	int (*probe)(struct device *dev);
	/*总线的remove接口,一般该接口主要是调用具体驱动的remove接口*/
	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 dev_pm_ops *pm;/*pm相关*/
 
	struct iommu_ops *iommu_ops;
	/*该接口包含了该bus对应kobject、device对应的kset、driver对应的kset、
	链接所有设备的链表、链接所有驱动的链表等*/
	struct subsys_private *p;
	struct lock_class_key lock_key;
};

4、class


struct class {
	const char		*name;
	struct module		*owner;
 
    /*该变量定义了该类别默认的属性*/
	struct class_attribute		*class_attrs;
    /*该变量定义了所有属性该类别的设备所定义的默认的属性*/
	struct device_attribute		*dev_attrs;
	struct bin_attribute		*dev_bin_attrs;
    
	struct kobject			*dev_kobj;
    /*事件接口(用于向应用层发送事件如class设备的添加与移除等)*/
	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, umode_t *mode);
    /*释放接口*/
	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);
 
	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);
 
	const struct kobj_ns_type_operations *ns_type;
	const void *(*namespace)(struct device *dev);
 
	const struct dev_pm_ops *pm;
 
	struct subsys_private *p;
}

四、对象的“三生万物”

到现在已经定义好了设备、驱动、总线和类。如何衍生系统中的设备万物呢?

1、继承device特性进行扩展

设备种类纷繁复杂,我们必须挑个典型来看,否则就有可能迷失其中,那就从PCI开始,以PCI进行展开,然后举一反三进行类比思考。

首先还是设备相关的,PCI总线上的设备都会有一个设备类型,它就是pci_dev,它的结构体比较庞大。


struct pci_dev {
	struct list_head bus_list;	/* Node in per-bus list */
	struct pci_bus	*bus;		/* Bus this device is on */
	struct pci_bus	*subordinate;	/* Bus this device bridges to */
	...
	struct device	dev;			/* Generic device interface */
	...
}

我们可以看到有这么一个数据成员:struct device dev。对的,它就是继承了device特性扩展而来的,在Linux的设备驱动管理框架中则可以看到这么一个宏定义to_pci_dev,通过device找到pci_dev结构。同样的,类似的usb_device,定义usb设备类型的,它里面同样也嵌入了一个device结构成员,它也有一个to_usb_device的宏定义。继续推进i2c_device也是嵌入了device结构成员,只是它通过了i2c_adapter间接嵌入的,但是它同样有一个偏移定位结构位置的宏定义to_i2c_adapter,虽然只是定位到i2c_adapter而已。

2、device_driver衍生

它也是从device_driver衍生出来的数据类型,同样,它也类似于设备一样,有一个偏移定位结构位置的宏定义to_pci_driver。类似的USB设备驱动有usb_driverI2C设备驱动有i2c_driver


struct pci_driver {
	struct list_head	node;
	const char		*name;
	const struct pci_device_id *id_table;	/* Must be non-NULL for probe to be called */
	int  (*probe)(struct pci_dev *dev, const struct pci_device_id *id);	/* New device inserted */
	void (*remove)(struct pci_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
	int  (*suspend)(struct pci_dev *dev, pm_message_t state);	/* Device suspended */
	int  (*suspend_late)(struct pci_dev *dev, pm_message_t state);
	int  (*resume_early)(struct pci_dev *dev);
	int  (*resume)(struct pci_dev *dev);	/* Device woken up */
	void (*shutdown)(struct pci_dev *dev);
	int  (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */
	const struct pci_error_handlers *err_handler;
	const struct attribute_group **groups;
	struct device_driver	driver;
	struct pci_dynids	dynids;
};