zoukankan      html  css  js  c++  java
  • I.MX6 Linux I2C device& driver hacking

    /*******************************************************************************************
     *                          I.MX6 Linux I2C device& driver hacking
     * 声明:
     *     1. 本文主要是对Linux I2C驱动进行代码跟踪,主要是为了能够对I2C驱动框架有个全面的了解;
     *     2. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2;
     *     3. 如果你有兴趣,请尽量自己去对代码进行跟踪,这样自己会对I2C有一个框架结构上的理解;
     *
     *                                                2015-6-4 晴 深圳 南山平山村 曾剑锋
     ******************************************************************************************/
    
                         \\\\\\\\-*- 目录 -*-/////////////////                  
                         |   一、跟踪板级文件: 
                         |   二、跟踪mx6q_sabresd_i2c_data参数:
                         |   三、跟踪imx6q_add_imx_i2c()函数:
                         |   四、跟踪imx6q_imx_i2c_data参数:
                         |   五、跟踪imx_add_imx_i2c()函数:
                         |   六、跟踪mxc_i2c0_board_info参数:
                         |   七、跟踪i2c_register_board_info()函数:
                         |   八、I2C adapter(适配器)跟踪:
                         |   九、I2C设备追踪:
                         |   十、跟踪__process_new_driver参数:
                         |   十一、跟踪i2c_for_each_dev()函数:
                         |   十二、跟踪max6875_probe()函数:
                         \\\\\\\\\\\///////////////////////                                        
    
    
    
    一、跟踪板级文件: 
        1. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            /*
             * initialize __mach_desc_MX6Q_SABRESD data structure.
             */
            MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
                /* Maintainer: Freescale Semiconductor, Inc. */
                .boot_params = MX6_PHYS_OFFSET + 0x100,
                .fixup = fixup_mxc_board,
                .map_io = mx6_map_io,
                .init_irq = mx6_init_irq,
                .init_machine = mx6_sabresd_board_init,   //跟踪板级初始化函数
                .timer = &mx6_sabresd_timer,
                .reserve = mx6q_sabresd_reserve,
            MACHINE_END
    
        2. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            /*!
             * Board specific initialization.
             */
            static void __init mx6_sabresd_board_init(void)
            {
                ......
                /**
                 * 接下来我们需要向以下4个方向去跟踪代码:
                 *     1. 跟踪mx6q_sabresd_i2c_data参数;
                 *     2. 跟踪imx6q_add_imx_i2c()函数;
                 *     3. 跟踪mxc_i2c0_board_info参数,在里面继续跟踪wm8962_config_data,
                 *         因为下面这两行代码修改了mxc_i2c0_board_info第一个元素;
                 *     4. 跟踪i2c_register_board_info()函数;
                 */
                strcpy(mxc_i2c0_board_info[0].type, "wm8962");
                    mxc_i2c0_board_info[0].platform_data = &wm8962_config_data;  //跟踪wm8962_config_data
    
                /**
                 * 这里相当于注册I2C控制器
                 */
                imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data);   //跟踪参数,函数
                imx6q_add_imx_i2c(1, &mx6q_sabresd_i2c_data);
                imx6q_add_imx_i2c(2, &mx6q_sabresd_i2c_data);
    
                /**
                 * 这里相当于注册I2C设备
                 */
                i2c_register_board_info(0, mxc_i2c0_board_info, //跟踪参数,函数
                        ARRAY_SIZE(mxc_i2c0_board_info));
                i2c_register_board_info(1, mxc_i2c1_board_info,
                        ARRAY_SIZE(mxc_i2c1_board_info));
                i2c_register_board_info(2, mxc_i2c2_board_info,
                        ARRAY_SIZE(mxc_i2c2_board_info));
                ......
            }
            ......
    
    二、跟踪mx6q_sabresd_i2c_data参数:
        1. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            static struct imxi2c_platform_data mx6q_sabresd_i2c_data = {  //跟踪结构体
                .bitrate = 100000,
            };
            ......
    
        2. cat arch/arm/plat-mxc/include/mach/i2c.h
            ......
            struct imxi2c_platform_data {
                int (*init)(struct device *dev);
                void (*exit)(struct device *dev);
                int bitrate;
            };
            ......
    
    三、跟踪imx6q_add_imx_i2c()函数:
        cat arch/arm/mach-mx6/devices-imx6q.h
            ......
            /**
             * 这里需要跟踪2个方向:
             *     1. 跟踪imx6q_imx_i2c_data参数;
             *     2. 跟踪imx_add_imx_i2c()函数;
             */
            extern const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst;
            #define imx6q_add_imx_i2c(id, pdata)    
                imx_add_imx_i2c(&imx6q_imx_i2c_data[id], pdata)   //跟踪结构体,函数
            ......
    
    四、跟踪imx6q_imx_i2c_data参数:
        1. cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
            ......
            #ifdef CONFIG_SOC_IMX6Q
            const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst = {
            #define imx6q_imx_i2c_data_entry(_id, _hwid)                
                imx_imx_i2c_data_entry(MX6Q, _id, _hwid, SZ_4K)
                imx6q_imx_i2c_data_entry(0, 1),     //跟踪这个宏
                imx6q_imx_i2c_data_entry(1, 2),
                imx6q_imx_i2c_data_entry(2, 3),
            };
            #endif /* ifdef CONFIG_SOC_IMX6Q */
            ......
    
        2. cat arch/arm/plat-mxc/include/mach/devices-common.h
            ......
            #include <mach/i2c.h>
            struct imx_imx_i2c_data {
                int id;
                resource_size_t iobase;
                resource_size_t iosize;
                resource_size_t irq;
            };
            ......
    
        3. cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
            ......
            /**
             *  1. 如果传入参数是:
             *      1. soc = MX6Q;
             *      2. _id = 0;
             *      3. _hwid = 1;
             *      4. size = SZ_4K;
             *  2. .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR合成结果:
             *      .iobase = MX6Q_I2C1_BASE_ADDR = 0x021A0000 (看后面的跟踪代码推演计算)
             *  3. .irq = soc ## _INT_I2C ## _hwid合成结果:
             *      .irq = MX6Q_INT_I2C1 = 68 
             */
            #define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)        
                {                                
                    .id = _id,                        
                    .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR,           //跟踪合成后的宏
                    .iosize = _size,                    
                    .irq = soc ## _INT_I2C ## _hwid,            
                }
    
            #define imx_imx_i2c_data_entry(soc, _id, _hwid, _size)            
                [_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)
            ......
    
        4. cat arch/arm/plat-mxc/include/mach/mx6.h
            ......
            /**
             * 1. I2C1首地址推演计算:
             *      MX6Q_I2C1_BASE_ADDR = (AIPS2_OFF_BASE_ADDR + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = ((ATZ2_BASE_ADDR + 0x80000) + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = ((AIPS2_ARB_BASE_ADDR + 0x80000) + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = ((0x02100000 + 0x80000) + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = (0x02100000 + 0x80000 + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = (0x02100000 + 0x80000 + 0x20000)
             *      MX6Q_I2C1_BASE_ADDR = 0x021A0000 (符合下面参考书给出的首地址)
             * 2. 参考书IMX6DQRM_revC.pdf给出的I2C1的首地址:
             *      -------------------------------------------------------------
             *      | Start Address | End Address | Region | Allocation  | Size |
             *      +---------------+-------------+--------+-------------+------+
             *      |   021A_0000   |  021A_3FFF  | AIPS-2 |    I2C1     | 16KB |
             *      -------------------------------------------------------------
             */
            #define MX6Q_I2C1_BASE_ADDR        (AIPS2_OFF_BASE_ADDR + 0x20000)
            ......
            /* ATZ#2- Off Platform */
            #define AIPS2_OFF_BASE_ADDR        (ATZ2_BASE_ADDR + 0x80000)
            ......
            #define ATZ2_BASE_ADDR            AIPS2_ARB_BASE_ADDR
            ......
            #define AIPS2_ARB_BASE_ADDR        0x02100000
            ......
    
        5. cat arch/arm/plat-mxc/include/mach/mx6.h
            ......
            /**
             *  1. 参考书IMX6DQRM_revC.pdf给出的I2C1的中断号:
             *      ---------------------------------------------
             *      |IRQ | Interrupt  | Interrupt Description   |
             *      |    |  Source    |                         |
             *      +----+------------+-------------------------+
             *      |68  | ECSPI1I2C1 | I2C1 interrupt request. |
             *      ---------------------------------------------
             *  2. 非常精准的符合参好书  :)
             */
            #define MX6Q_INT_I2C1            68
            ......
    
    五、跟踪imx_add_imx_i2c()函数:
        1. cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
            ......
            struct platform_device *__init imx_add_imx_i2c(
                    const struct imx_imx_i2c_data *data,
                    const struct imxi2c_platform_data *pdata)
            {
                //利用传入参数合成平台资源数据
                struct resource res[] = {
                    {
                        .start = data->iobase,
                        .end = data->iobase + data->iosize - 1,
                        .flags = IORESOURCE_MEM,
                    }, {
                        .start = data->irq,
                        .end = data->irq,
                        .flags = IORESOURCE_IRQ,
                    },
                };
    
                /**
                 * 从这里可以知道设备匹配时候的名字,跟踪该函数
                 */
                return imx_add_platform_device("imx-i2c", data->id,
                        res, ARRAY_SIZE(res),
                        pdata, sizeof(*pdata));         
            }
            ......
    
        2. cat arch/arm/plat-mxc/include/mach/devices-common.h
            ......
            static inline struct platform_device *imx_add_platform_device(
                    const char *name, int id,
                    const struct resource *res, unsigned int num_resources,
                    const void *data, size_t size_data)
            {
                //跟踪该函数
                return imx_add_platform_device_dmamask(
                        name, id, res, num_resources, data, size_data, 0);
            }
            ......
    
        3. cat arch/arm/plat-mxc/devices.c
            ......
            struct platform_device *__init imx_add_platform_device_dmamask(
                    const char *name, int id,
                    const struct resource *res, unsigned int num_resources,
                    const void *data, size_t size_data, u64 dmamask)
            {
                int ret = -ENOMEM;
                struct platform_device *pdev;
    
                pdev = platform_device_alloc(name, id);
                if (!pdev)
                    goto err;
                ......
                if (res) {
                    ret = platform_device_add_resources(pdev, res, num_resources);
                    if (ret)
                        goto err;
                }
    
                if (data) {
                    ret = platform_device_add_data(pdev, data, size_data);
                    if (ret)
                        goto err;
                }
    
                ret = platform_device_add(pdev);    //设备注册
                if (ret) {
            err:
                    if (dmamask)
                        kfree(pdev->dev.dma_mask);
                    platform_device_put(pdev);
                    return ERR_PTR(ret);
                }
    
                return pdev;
            }
            ......
    
    六、跟踪mxc_i2c0_board_info参数:
        1. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {  //跟踪结构体
                /**
                 * 这是板级初始化函数中的另一部分:
                 *  strcpy(mxc_i2c0_board_info[0].type, "wm8962");
                 *      mxc_i2c0_board_info[0].platform_data = &wm8962_config_data;  //跟踪目标wm8962_config_data
                 *
                 *  通过这里可以知道wm8962的I2C地址是:0x1a
                 */
                {
                    I2C_BOARD_INFO("wm89**", 0x1a),
                },
                {
                    I2C_BOARD_INFO("ov564x", 0x3c),
                    .platform_data = (void *)&camera_data,
                },
                {
                    I2C_BOARD_INFO("mma8451", 0x1d),
                    .platform_data = (void *)&mma8451_position,
                },
                {
                    I2C_BOARD_INFO("isl1208", 0x6f),
                },
            };
            ......
    
        2. cat include/linux/i2c.h
            ......
            struct i2c_board_info {
                char        type[I2C_NAME_SIZE];
                unsigned short    flags;
                unsigned short    addr;
                void        *platform_data;
                struct dev_archdata    *archdata;
                struct device_node *of_node;
                int        irq;
            };
            ......
    
        3. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            static struct wm8962_pdata wm8962_config_data = { //跟踪结构体
                .gpio_init = {
                    [2] = WM8962_GPIO_FN_DMICCLK,
                    [4] = 0x8000 | WM8962_GPIO_FN_DMICDAT,
                },
            };
            ......
    
        4. cat include/sound/wm8962.h
            ......
            struct wm8962_pdata {
                int gpio_base;
                u32 gpio_init[WM8962_MAX_GPIO];
    
                /* Setup for microphone detection, raw value to be written to
                 * R48(0x30) - only microphone related bits will be updated.
                 * Detection may be enabled here for use with signals brought
                 * out on the GPIOs. */
                u32 mic_cfg;
    
                bool irq_active_low;
    
                bool spk_mono;   /* Speaker outputs tied together as mono */
            };
            ......
    
    七、跟踪i2c_register_board_info()函数:
        1. cat drivers/i2c/i2c-boardinfo.c
            ......
            int __init i2c_register_board_info(int busnum,
                struct i2c_board_info const *info, unsigned len)
            {
                int status;
    
                down_write(&__i2c_board_lock);
    
                /* dynamic bus numbers will be assigned after the last static one */
                if (busnum >= __i2c_first_dynamic_bus_num)
                    __i2c_first_dynamic_bus_num = busnum + 1;
    
                for (status = 0; len; len--, info++) {
                    struct i2c_devinfo    *devinfo;
    
                    devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
                    if (!devinfo) {
                        pr_debug("i2c-core: can't register boardinfo!
    ");
                        status = -ENOMEM;
                        break;
                    }
    
                    devinfo->busnum = busnum;
                    devinfo->board_info = *info;
                    list_add_tail(&devinfo->list, &__i2c_board_list); //跟踪__i2c_board_list
                }
    
                up_write(&__i2c_board_lock);
    
                return status;
            }
            ......
    
        2. cat drivers/i2c/i2c-boardinfo.c
            ......
            LIST_HEAD(__i2c_board_list);    一个公用的I2C链表头
            EXPORT_SYMBOL_GPL(__i2c_board_list);
            ......
    
    八、I2C adapter(适配器)跟踪:
        1. cat drivers/i2c/busses/i2c-imx.c
            ......
            /* This will be the driver name the kernel reports */
            #define DRIVER_NAME "imx-i2c"       //adapter和I2C控制器匹配的名字
    
            /* Default value */
            #define IMX_I2C_BIT_RATE    100000    /* 100kHz */
    
            static struct platform_driver i2c_imx_driver = {
                .remove        = __exit_p(i2c_imx_remove),
                .driver    = {
                    .name    = DRIVER_NAME,
                    .owner    = THIS_MODULE,
                }
            };
    
            static int __init i2c_adap_imx_init(void)
            {
                return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);  //跟踪函数,参数
            }
            subsys_initcall(i2c_adap_imx_init);
    
            static void __exit i2c_adap_imx_exit(void)
            {
                platform_driver_unregister(&i2c_imx_driver);
            }
            module_exit(i2c_adap_imx_exit);
    
            MODULE_LICENSE("GPL");
            MODULE_AUTHOR("Darius Augulis");
            MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus");  //这里说明了这个驱动的作用
            MODULE_ALIAS("platform:" DRIVER_NAME);
    
        2. cat drivers/base/platform.c
            ......
            int __init_or_module platform_driver_probe(struct platform_driver *drv,
                    int (*probe)(struct platform_device *))
            {
                int retval, code;
    
                /* make sure driver won't have bind/unbind attributes */
                drv->driver.suppress_bind_attrs = true;
    
                /* temporary section violation during probe() */
                /**
                 * 主要注意下面这行代码
                 */
                drv->probe = probe;
                retval = code = platform_driver_register(drv);
    
                /*
                 * Fixup that section violation, being paranoid about code scanning
                 * the list of drivers in order to probe new devices.  Check to see
                 * if the probe was successful, and make sure any forced probes of
                 * new devices fail.
                 */
                spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);
                drv->probe = NULL;
                if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
                    retval = -ENODEV;
                drv->driver.probe = platform_drv_probe_fail;
                spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock);
    
                if (code != retval)
                    platform_driver_unregister(drv);
                return retval;
            }
            EXPORT_SYMBOL_GPL(platform_driver_probe);
            ......
    
        3. cat drivers/i2c/busses/i2c-imx.c
            ......
            static int __init i2c_imx_probe(struct platform_device *pdev)
            {
                struct imx_i2c_struct *i2c_imx;
                struct resource *res;
                struct imxi2c_platform_data *pdata;
                void __iomem *base;
                resource_size_t res_size;
                int irq;
                int ret;
    
                dev_dbg(&pdev->dev, "<%s>
    ", __func__);
    
                res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
                if (!res) {
                    dev_err(&pdev->dev, "can't get device resources
    ");
                    return -ENOENT;
                }
                irq = platform_get_irq(pdev, 0);
                if (irq < 0) {
                    dev_err(&pdev->dev, "can't get irq number
    ");
                    return -ENOENT;
                }
    
                pdata = pdev->dev.platform_data;
    
                if (pdata && pdata->init) {
                    ret = pdata->init(&pdev->dev);
                    if (ret)
                        return ret;
                }
    
                res_size = resource_size(res);
    
                if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
                    ret = -EBUSY;
                    goto fail0;
                }
    
                base = ioremap(res->start, res_size);
                if (!base) {
                    dev_err(&pdev->dev, "ioremap failed
    ");
                    ret = -EIO;
                    goto fail1;
                }
    
                i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
                if (!i2c_imx) {
                    dev_err(&pdev->dev, "can't allocate interface
    ");
                    ret = -ENOMEM;
                    goto fail2;
                }
    
                /* Setup i2c_imx driver structure */
                strcpy(i2c_imx->adapter.name, pdev->name);
                i2c_imx->adapter.owner        = THIS_MODULE;
                i2c_imx->adapter.algo        = &i2c_imx_algo;
                i2c_imx->adapter.dev.parent    = &pdev->dev;
                i2c_imx->adapter.nr         = pdev->id;
                i2c_imx->irq            = irq;
                i2c_imx->base            = base;
                i2c_imx->res            = res;
    
                /* Get I2C clock */
                i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
                if (IS_ERR(i2c_imx->clk)) {
                    ret = PTR_ERR(i2c_imx->clk);
                    dev_err(&pdev->dev, "can't get I2C clock
    ");
                    goto fail3;
                }
    
                /* Request IRQ */
                ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
                if (ret) {
                    dev_err(&pdev->dev, "can't claim irq %d
    ", i2c_imx->irq);
                    goto fail4;
                }
    
                /* Init queue */
                init_waitqueue_head(&i2c_imx->queue);
    
                /* Set up adapter data */
                i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
    
                /* Set up clock divider */
                if (pdata && pdata->bitrate)
                    i2c_imx_set_clk(i2c_imx, pdata->bitrate);
                else
                    i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
    
                /* Set up chip registers to defaults */
                writeb(0, i2c_imx->base + IMX_I2C_I2CR);
                writeb(0, i2c_imx->base + IMX_I2C_I2SR);
    
                /* Add I2C adapter */
                ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
                if (ret < 0) {
                    dev_err(&pdev->dev, "registration failed
    ");
                    goto fail5;
                }
    
                /* Set up platform driver data */
                platform_set_drvdata(pdev, i2c_imx);
    
                dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d
    ", i2c_imx->irq);
                dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x
    ",
                    i2c_imx->res->start, i2c_imx->res->end);
                dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x 
    ",
                    res_size, i2c_imx->res->start);
                dev_dbg(&i2c_imx->adapter.dev, "adapter name: "%s"
    ",
                    i2c_imx->adapter.name);
                dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered
    ");
    
                return 0;   /* Return OK */
    
            fail5:
                free_irq(i2c_imx->irq, i2c_imx);
            fail4:
                clk_put(i2c_imx->clk);
            fail3:
                kfree(i2c_imx);
            fail2:
                iounmap(base);
            fail1:
                release_mem_region(res->start, resource_size(res));
            fail0:
                if (pdata && pdata->exit)
                    pdata->exit(&pdev->dev);
                return ret; /* Return error number */
            }
            ......
    
    九、I2C设备追踪:
        1. cat drivers/misc/eeprom/max6875.c
            ......
            static const struct i2c_device_id max6875_id[] = {
                { "max6875", 0 },
                { }
            };
    
            static struct i2c_driver max6875_driver = {
                .driver = {
                    .name    = "max6875",
                },
                .probe        = max6875_probe,    //跟踪函数
                .remove        = max6875_remove,
                .id_table    = max6875_id,
            };
    
            static int __init max6875_init(void)
            {
                return i2c_add_driver(&max6875_driver); //跟踪函数
            }
    
            static void __exit max6875_exit(void)
            {
                i2c_del_driver(&max6875_driver);
            }
    
    
            MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
            MODULE_DESCRIPTION("MAX6875 driver");
            MODULE_LICENSE("GPL");
    
            module_init(max6875_init);
            module_exit(max6875_exit);
                   
        2. cat include/linux/i2c.h
            ......
            static inline int i2c_add_driver(struct i2c_driver *driver)
            {
                return i2c_register_driver(THIS_MODULE, driver);    //跟踪函数
            }
            ......
    
        3. cat drivers/i2c/i2c-core.c
            ......
            int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
            {
                ......
                driver->driver.owner = owner;
                driver->driver.bus = &i2c_bus_type;     //跟踪数据结构
    
                /* When registration returns, the driver core
                 * will have called probe() for all matching-but-unbound devices.
                 */
                res = driver_register(&driver->driver); //这里就跟踪到这里结束了
    
                ......  
    
                /* Walk the adapters that are already present */
                i2c_for_each_dev(driver, __process_new_driver);   //跟踪这个函数
    
                return 0;
            }
            EXPORT_SYMBOL(i2c_register_driver);
            ......
    
        4. cat drivers/i2c/i2c-core.c
            ...... 
            struct bus_type i2c_bus_type = {
                .name        = "i2c",
                .match        = i2c_device_match,
                .probe        = i2c_device_probe,
                .remove        = i2c_device_remove,
                .shutdown    = i2c_device_shutdown,
                .pm        = &i2c_device_pm_ops,
            };
            EXPORT_SYMBOL_GPL(i2c_bus_type);
            ......
            
    十、跟踪__process_new_driver参数:
        1. cat drivers/i2c/i2c-core.c
            ......
            static int __process_new_driver(struct device *dev, void *data)
            {
                if (dev->type != &i2c_adapter_type)
                    return 0;
                //跟踪to_i2c_adapter参数,部队i2c_do_add_adapter()函数进行跟踪,到这里结束
                return i2c_do_add_adapter(data, to_i2c_adapter(dev)); 
            }
            ......
    
       2. cat include/linux/i2c.h
            ......
            #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
            ......
    
    十一、跟踪i2c_for_each_dev()函数:
        1. cat drivers/i2c/i2c-core.c
            ......
            int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
            {
                int res;
    
                mutex_lock(&core_lock);
                res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);  //跟踪函数
                mutex_unlock(&core_lock);
    
                return res;
            }
            EXPORT_SYMBOL_GPL(i2c_for_each_dev);
            ......
    
       2. cat drivers/base/bus.c
            ......
            /**
             * 这一部分只跟踪到这里,因为从函数名,你可以知道没必要再跟下去了
             */
            int bus_for_each_dev(struct bus_type *bus, struct device *start,
                         void *data, int (*fn)(struct device *, void *))
            {
                /**
                 *
                 *  struct bus_type {
                 *      ......
                 *
                 *      struct subsys_private *p;
                 *  };
                 *
                 *  struct subsys_private {
                 *      ......
                 *      struct klist klist_devices;
                 *      struct klist klist_drivers;
                 *      ......
                 *  };
                 *
                 *  struct device {
                 *      ......
                 *      struct device_private    *p;
                 *      ......
                 *  };
                 *
                 *  struct device_private {
                 *      ......
                 *      struct klist_node knode_bus;
                 *      void *driver_data;
                 *      struct device *device;
                 *  };
                 */
                struct klist_iter i;
                struct device *dev;
                ......
    
                klist_iter_init_node(&bus->p->klist_devices, &i,
                             (start ? &start->p->knode_bus : NULL));
                while ((dev = next_device(&i)) && !error)    
                    error = fn(dev, data);
                klist_iter_exit(&i);
                return error;
            }
            EXPORT_SYMBOL_GPL(bus_for_each_dev);
            ......
    
    十二、跟踪max6875_probe()函数:
        cat drivers/misc/eeprom/max6875.c
            ......
            static int max6875_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
            {
                struct i2c_adapter *adapter = client->adapter;
                struct max6875_data *data;
                int err;
    
                if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA
                                 | I2C_FUNC_SMBUS_READ_BYTE))
                    return -ENODEV;
    
                /* Only bind to even addresses */
                if (client->addr & 1)
                    return -ENODEV;
    
                if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL)))
                    return -ENOMEM;
    
                /* A fake client is created on the odd address */
                data->fake_client = i2c_new_dummy(client->adapter, client->addr + 1);
                if (!data->fake_client) {
                    err = -ENOMEM;
                    goto exit_kfree;
                }
    
                /* Init real i2c_client */
                i2c_set_clientdata(client, data);
                mutex_init(&data->update_lock);
    
                err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);
                if (err)
                    goto exit_remove_fake;
    
                return 0;
    
            exit_remove_fake:
                i2c_unregister_device(data->fake_client);
            exit_kfree:
                kfree(data);
                return err;
            }
  • 相关阅读:
    POJ 2400 Supervisor, Supervisee(KM)
    HDU 1695 GCD (容斥原理+欧拉函数)
    URAL 1244. Gentlemen (DP)
    HDU 1573 X问题 (中国剩余定理)
    ZOJ 3757 Alice and Bob and Cue Sports(模拟)
    HDU 1452 Happy 2004(因子和的积性函数)
    第五届山东省省赛总结
    mybatis错误——java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
    springMVC+spring+mybatis搭建最近
    Log4j配置详解
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4553668.html
Copyright © 2011-2022 走看看