一个驱动支持多个设备再usb子系统、input子系统、platform、iic子系统 中的实现

本文详细解析了Linux内核中平台总线、I2C子系统及USB子系统的设备驱动匹配机制,包括如何通过name、id_table进行设备识别。
platform

这里写图片描述

你写的驱动你应该知道它适用与哪些设备吧,如果你想支持一个设备,那么你就构造一个
usb_device_id (USB)、i2c_device_id (IIC)、platform_device_id(Platform)放到对应驱动的id_table中

/**
    先看平台总线。
*/
struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    /**
        匹配驱动与设备
    */
    .match      = platform_match,  
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};

    /**
        匹配的方法有两种 : 
                    1 : 是利用了platform_driver中定义的id_table,(一个驱动可能支持多个设备)
                        如果定义了id_table,那么,就会逐一遍历id_table表中的每一项
                        否则,使用name来匹配
                    2 : 使用name来匹配 (一个驱动只能支持一个设备)    
    */
    static int platform_match(struct device * dev,struct device_driver * drv)
    {
        /**
            底层调用container_of宏来获取平台设备、平台设备驱动
        */
        struct platform_device = to_platform_device(dev);
        struct platform_driver = to_platform_driver(drv);
        /**
            这个函数,如果drv->of_match_table不存在,或者dev->of_node不存在
            就会返回NULL,实际中用的不多
            最终还是比较 drv->of_match_table 和 dev->of_node的name、type等是不是一样
            if ((!matches) || (!dev->of_node))
                return NULL;
            return of_match_node(matches, dev->of_node);
        */
        if(of_driver_match_device(dev,drv))
        {
            return 1;
        }
        /**
            如果平台驱动提供了id_table,那么就会platform_match_id函数
            id_table的出现,让一个驱动支持多个设备成为可能。
            platform_match_id()这个函数会在下面说
        */
        if(pdrv->id_table)
        {
            return platform_match_id(pdrv->id_table,pdev) != NULL;
        }

        /**
          如果平台驱动没有提供id_table,那么就会利用名字来比较了
          如果是这样,一个驱动只能支持一个设备了
        */  
        return (strcmp(pdev->name,drv->name) == 0);
    }

    /**
        这个函数 会在驱动的id_table表里面 查找和platform_device的name字段相同的一项
        如果相同,那么就返回true,否则返回false,
        所以 :
            这样就可以一个驱动支持多个设备,
            如果只是简单的比较name的话,那么一个驱动只能支持一个设备  
    */
    static const struct platform_device_id *platform_match_id(
            const struct platform_device_id *id,
            struct platform_device *pdev)
    {
        while (id->name[0]) 
        {
            /**
                还是比较每一项的名字
            */
            if (strcmp(pdev->name, id->name) == 0) {
                pdev->id_entry = id;
                return id;
            }
            id++;
        }
        return NULL;
    }
iic子系统

在前面的博文中已经详细的分析过,这里简单的说一下,方便对比

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    /**
        匹配函数,下面看这个函数。
    */
    .match      = i2c_device_match,
    /**
        iic总线提供了probe,匹配成功后会调用这个probe,
        然后再这个probe中调用驱动的probe
    */
    .probe      = i2c_device_probe,
    .....
};
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    /**
        根据type类型来检查是不是iic_client类型,
    */
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;
    /**
        不是iic_client类型,就直接返回,因为这里要匹配是iic_client和对应的驱动
    */
    if (!client) 
    {
        return 0;
    }   
    /**
        获取iic_driver,是为了在下面取出来它的id_table
    */
    driver = to_i2c_driver(drv);
    /**
        如果提供了id_table就调用i2c_match_id()来比较
    */
    if (driver->id_table)
    {
        /**
            这个函数就是遍历id_table,取出每一个表项来与iic_client的name比较
            其实还是利用了name比较,
        */
        return i2c_match_id(driver->id_table, client) != NULL;
    }   

    return 0;
}
/**
    看最终还是和上面的platform调用的是同一个函数。
*/
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}
usb子系统

struct bus_type usb_bus_type = {
    .name =     "usb",
    /**
        当调用usb_register()注册一个usb_driver的时候
        __driver_attach()
            ....
               driver_match_device()
                     调用总线提供的match函数  
        如果这个match函数匹配不成功的话,那么就不会调用驱动提供的probe函数(这里总线没有提供probe)
        具体match函数怎么做的,下面有分析          
    */
    .match =    usb_device_match,
    .uevent =   usb_uevent,
};

static struct usb_driver usb_mouse_driver = {
    .name       = "usbmouse",
    .probe      = usb_mouse_probe,
    /**
        usb设备拔出后调用的函数
        主要做一些销毁、释放、杀死urb的工作
    */
    .disconnect = usb_mouse_disconnect,
    /**
        前面说过,要想你写的驱动支持某一个设备的话,
        可以构造一个usb_device_id放到usb_mouse_id_table这个数组中
        怎么构造,以前的博文已经说过,可以使用内核提供了一些宏来构造。
    */
    .id_table   = usb_mouse_id_table,
};

在Linux系统中,通常所说的IIC驱动IIC子系统驱动有一定的关联和区别。 IIC子系统是Linux内核中用于管理I2C设备驱动的一套机制。Linux启动阶段会创建一个IIC总线`i2c_bus_type`,该总线用于管理`i2c_client`设备和`i2c_driver`驱动,它实现了自己的`match`和`probe`函数等,和Linux驱动设备模型里的总线类似,起到协调和管理I2C相关设备驱动的作用 [^2]。 而IIC驱动则是基于IIC子系统实现对具体I2C设备进行操作的代码。Linux系统提供了2种IIC驱动实现方法 [^1]: - **IIC - DEV方式**:对应`driver/i2c/i2c - dev.c`,这种方法只是封装了主机的IIC基本操作,并且向应用层提供相应的操作接口。应用层代码需要自己去实现对`slave`的控制和操作,相当于传统的驱动中干的活都丢给了应用去做,所以又叫做“应用层驱动”。其优势在于把差异化都放在了应用层,当设备比较难缠(尤其是`slave`是非标准的IIC时),不用动驱动,只需要修改应用就可以实现对各种设备驱动。 - **驱动实现方式**:所有代码都放在驱动实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有IIC存在,例如电容式触摸屏驱动,直接向应用层提供`/dev/input/event1`的操作接口,应用层编程的人根本不知道`event1`中涉及到了IIC。 ### 区别总结 IIC子系统驱动更侧重于整个I2C设备管理机制的搭建和维护,是一个通用的框架;而IIC驱动则是利用这个框架针对具体的I2C设备编写的操作代码,不IIC设备的主要不点在于其寄存器列表不、初始化不,但对不IIC设备的操作方法基本相 [^1]。 ### 示例代码理解IIC操作 ```c // 假设这是一个简单的IIC写操作函数示例 #include <linux/i2c.h> int i2c_write(struct i2c_client *client, u8 reg, u8 value) { int ret; u8 buf[2]; buf[0] = reg; buf[1] = value; ret = i2c_master_send(client, buf, 2); if (ret != 2) { printk(KERN_ERR "I2C write failed\n"); return ret; } return 0; } ``` 此代码展示了一个简单的I2C写操作函数,通过`i2c_master_send`函数向指定的I2C设备寄存器写入数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值