设备驱动中的device(kernel-4.7)

本文详细解析了Linux内核4.7中device结构体的定义及设备注册过程,涉及设备与总线、驱动的关系,以及sysfs的初始化。设备注册包括device结构体初始化、分配内存、添加到系统、创建属性文件和链接,以及探测匹配的驱动等功能。

device结构体定义,在kernel-4.7/include/linux/device.h中:


/**
 * struct device - The basic device structure
 * @driver_data: Private pointer for driver specific info.
 * @power:  For device power management.
 *      See Documentation/power/devices.txt for details.
 * @pm_domain:  Provide callbacks that are executed during system suspend,
 *      hibernation, system resume and during runtime PM transitions
 *      along with subsystem-level and driver-level callbacks.
 * @pins:   For device pin management.
 *      See Documentation/pinctrl.txt for details.
 * @msi_list:   Hosts MSI descriptors
 * @msi_domain: The generic MSI domain this device is using.
 * @numa_node:  NUMA node this device is close to.
 * @dma_mask:   Dma mask (if dma'ble device).
 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
 *      hardware supports 64-bit addresses for consistent allocations
 *      such descriptors.
 * @dma_pfn_offset: offset of DMA memory range relatively of RAM
 * @dma_parms:  A low level driver may set these to teach IOMMU code about
 *      segment limitations.
 * @dma_pools:  Dma pools (if dma'ble device).
 * @dma_mem:    Internal for coherent mem override.
 * @cma_area:   Contiguous memory area for dma allocations
 * @archdata:   For arch-specific additions.
 * @of_node:    Associated device tree node.
 * @fwnode: Associated device node supplied by platform firmware.
 * @devt:   For creating the sysfs "dev".
 * @id:     device instance
 * @devres_lock: Spinlock to protect the resource of the device.
 * @devres_head: The resources list of the device.
 * @knode_class: The node used to add the device to the class list.
 * @class:  The class of the device.
 * @groups: Optional attribute groups.
 * @release:    Callback to free the device after all references have
 *      gone away. This should be set by the allocator of the
 *      device (i.e. the bus driver that discovered the device).
 * @iommu_group: IOMMU group the device belongs to.
 *
 * @offline_disabled: If set, the device is permanently online.
 * @offline:    Set after successful invocation of bus type's .offline().
 *
 * At the lowest level, every device in a Linux system is represented by an
 * instance of struct device. The device structure contains the information
 * that the device model core needs to model the system. Most subsystems,
 * however, track additional information about the devices they host. As a
 * result, it is rare for devices to be represented by bare device structures;
 * instead, that structure, like kobject structures, is usually embedded within
 * a higher-level representation of the device.
 */
struct device {
    struct device       *parent;   //指向其父设备。

    struct device_private   *p;   // 设备驱动部分的核心私有数据

    struct kobject kobj;    // kobject类, 用于联系到sysfs中
    const char      *init_name; /* initial name of the device */
    const struct device_type *type;   //表示设备的类型

    struct mutex        mutex;  /* mutex 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        *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_GENERIC_MSI_IRQ_DOMAIN
    struct irq_domain   *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
    struct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
    struct list_head    msi_list;
#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;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */
    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;   //连入class链表时所用的klist节点
    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;
};

device结构体用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。



/**
 * struct device_private - structure to hold the private to the driver core portions of the device structure.
 *
 * @klist_children - klist containing all children of this device
 * @knode_parent - node in sibling list
 * @knode_driver - node in driver list
 * @knode_bus - node in bus list
 * @deferred_probe - entry in deferred_probe_list which is used to retry the
 *  binding of drivers which were unable to get all the resources needed by
 *  the device; typically because it depends on another driver getting
 *  probed first.
 * @device - pointer back to the struct device that this structure is
 * associated with.
 *
 * Nothing outside of the driver core should ever touch these fields.
 */
struct device_private {
    struct klist klist_children;   //子设备的链表
    struct klist_node knode_parent; //连入父设备的klist_children时所用的节点
    struct klist_node knode_driver; //连入驱动的设备链表所用的节点
    struct klist_node knode_bus;  //连入总线的设备链表时所用的节点
    struct list_head deferred_probe;
    struct device *device;
}; 
//从父设备的klist_children上节点,获得相应的device_private
#define to_device_private_parent(obj)   \     
    container_of(obj, struct device_private, knode_parent)

//从驱动的设备链表上节点,获得对应的device_private
#define to_device_private_driver(obj)   \
    container_of(obj, struct device_private, knode_driver) 

从总线的设备链表上节点,获得对应的device_private
#define to_device_private_bus(obj)  \
    container_of(obj, struct device_private, knode_bus)

struct device中有一部分不愿意让外界看到,所以做出struct device_private结构,包括了设备驱动模型内部的链接。这个结构体定义在drivers/base/base.h文件中。


Linux设备模型中,device相关的sysfs结构的初始化是由devices_init函数完成的,该函数定义在drivers/base/core.c文件中,其内容如下:


int __init devices_init(void)
{
    //创建devices_kset,它是所有device的集合,它的名字是“devices”,对应/sys/devices目录
    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
    if (!devices_kset)
        return -ENOMEM; 

    //创建dev_kobj,它的名字是“dev”,对应/sys/dev目录
    dev_kobj = kobject_create_and_add("dev", NULL);
    if (!dev_kobj)
        goto dev_kobj_err; 

    //创建sysfs_dev_block_kobj,它的名字是“block”,对应/sys/dev/block目录
    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
    if (!sysfs_dev_block_kobj)
        goto block_kobj_err; 

    创建sysfs_dev_char_kobj,它的名字是“char”,对应/sys/dev/char目录
    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
    if (!sysfs_dev_char_kobj)
        goto char_kobj_err;

    return 0;

 char_kobj_err:
    kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
    kobject_put(dev_kobj);
 dev_kobj_err:
    kset_unregister(devices_kset);
    return -ENOMEM;
}

注册一个device是通过调用device_register函数完成的,该函数定义在drivers/base/core.c文件中,其内容如下:


/**
 * device_register -
<think>嗯,用户想了解如何在Zynq平台上通过GPIO模拟I2C驱动,并且使用内核中已有的驱动。首先,我得回忆一下Zynq的启动流程和Linux内核中的I2C子系统。Zynq是Xilinx的SoC,结合了ARM处理器和FPGA,启动过程可能涉及到硬件初始化和设备树的配置。 用户提到使用GPIO模拟I2C,也就是bit-banging方式。Linux内核中确实有i2c-gpio驱动模块,可以用来通过GPIO引脚模拟I2C总线。不过需要确认内核是否已经配置了该驱动,通常需要启用CONFIG_I2C_GPIO选项。 接下来,可能需要修改设备树,添加i2c-gpio节点,指定使用的GPIO引脚和时钟延时的参数。例如,使用gpio0的某个引脚作为SDA,另一个作为SCL,并设置i2c-gpio的时钟频率。用户可能需要知道如何正确编写设备树节点,以及如何编译和加载设备树。 然后,启动系统后需要加载i2c-gpio模块,或者将其编译进内核。加载成功后,应该能在/sys/bus/i2c/devices/下看到新的I2C总线。用户可能需要使用i2c-tools工具进行测试,比如i2cdetect扫描设备地址。 另外,用户可能遇到的问题包括GPIO引脚配置是否正确、设备树语法错误、驱动模块未加载等。需要检查dmesg日志,确认是否有错误信息,比如GPIO申请失败或者i2c总线注册失败的情况。还有可能需要注意GPIO的电平和上拉电阻,确保物理连接正确。 用户可能已经有一定的嵌入式开发经验,但可能对设备树和内核驱动的具体配置不太熟悉。需要详细说明每一步的操作,比如如何修改设备树,如何编译和更新设备树,如何加载驱动模块,以及如何测试I2C总线是否正常工作。同时,提醒用户注意GPIO引脚的选择,避免与其他功能冲突,确保在设备树中正确引用GPIO控制器。 总结步骤应该是:配置内核支持i2c-gpio驱动,修改设备树添加i2c-gpio节点,编译并更新设备树,加载驱动,最后测试I2C总线。同时,提供示例代码和常见问题解决方法,帮助用户更好地理解和操作。</think>在Zynq平台上使用GPIO模拟I2C总线并利用内核已有驱动,可通过以下步骤实现: --- ### 一、技术原理 Linux内核自带的`i2c-gpio`驱动通过GPIO引脚模拟I2C协议(bit-banging),核心参数: - `sda-gpios`:数据线GPIO - `scl-gpios`:时钟线GPIO - `i2c-gpio,delay-us`:时钟脉冲宽度(默认2μs) --- ### 二、实现步骤 #### 1. 内核配置 确认内核已启用GPIO模拟I2C驱动: ```bash make menuconfig ``` 定位选项: ``` Device Drivers -> I2C support -> I2C Hardware Bus support -> GPIO-based bitbanging I2C ``` #### 2. 设备树配置(.dts文件) ```dts / { i2c_gpio: i2c-gpio { compatible = "i2c-gpio"; gpios = <&gpio0 50 0 /* SDA */ &gpio0 51 0>; /* SCL */ i2c-gpio,delay-us = <2>; /* 100kHz */ #address-cells = <1>; #size-cells = <0>; }; }; ``` #### 3. 设备树绑定验证 检查GPIO控制器编号: ```bash cat /sys/kernel/debug/gpio ``` #### 4. 驱动加载 静态编译进内核或动态加载: ```bash modprobe i2c-gpio ``` #### 5. 功能验证 使用i2c-tools测试: ```bash i2cdetect -l # 查看总线编号 i2cdetect -y 2 # 扫描总线2上的设备 ``` --- ### 三、调试技巧 1. **电平检测** ```bash echo 50 > /sys/class/gpio/export # SDA echo 51 > /sys/class/gpio/export # SCL cat /sys/class/gpio/gpio50/value ``` 2. **示波器观察**:检查SCL/SDA波形是否符合I2C时序 3. **内核日志分析** ```bash dmesg | grep i2c-gpio ``` --- ### 四、典型问题解决方案 | 现象 | 可能原因 | 解决方法 | |------|----------|----------| | 总线未识别 | GPIO被复用 | 检查pinctrl配置 | | 通信失败 | 未接上拉电阻 | 外接4.7kΩ上拉电阻 | | 设备无响应 | 地址错误 | 用i2cdetect重新扫描 | --- ### 五、性能优化建议 - 调整`delay-us`参数控制速率(值越小速度越快) - 使用高速GPIO控制器(如通过EMIO连接PL端高速GPIO) - 避免在中断上下文中进行I2C操作 通过这种方法,可以在不增加硬件成本的情况下实现灵活的I2C通信,特别适用于低速外设或调试场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值