linux下i2c适配器的注册过程

i2c-adapter的注册过程


i2c-s3c2410.c
======================
module_init(12c_adap_s2c_init)

module_init申明函数

static int __init i2c_adap_s3c_init(void)
{
 int ret;

 ret = platform_driver_register(&s3c2410_i2c_driver);
 if (ret == 0) {
  ret = platform_driver_register(&s3c2440_i2c_driver);
  if (ret)
   platform_driver_unregister(&s3c2410_i2c_driver);
 }

 return ret;
}

已知:
static struct platform_driver s3c2410_i2c_driver = {
 .probe  = s3c24xx_i2c_probe,
 .remove  = s3c24xx_i2c_remove,
 .resume  = s3c24xx_i2c_resume,
 .driver  = {
  .owner = THIS_MODULE,
  .name = "s3c2410-i2c",
 },
};

且在源文件platform.c中定义函数:
int platform_driver_register(struct platform_driver *drv)
{  
 /**
  * s3c2410_i2c_driver
  */ 
 drv->driver.bus = &platform_bus_type;

 /**
  * drv->driver.brobe = s3c2410_i2c_driver.driver.probe = platform_drv_probe
  */
 if (drv->probe)
  drv->driver.probe = platform_drv_probe;
 if (drv->remove)
  drv->driver.remove = platform_drv_remove;
 if (drv->shutdown)
  drv->driver.shutdown = platform_drv_shutdown;
 if (drv->suspend)
  drv->driver.suspend = platform_drv_suspend;
 if (drv->resume)
  drv->driver.resume = platform_drv_resume;
 return driver_register(&drv->driver);
}

所以platform_driver_register(&s3c2410_i2c_driver)完成的主要功能为:
1.令s3c2410_i2c_driver.driver.bus为&platform_bus_type
2.令s3c2410_i2c_driver.driver.probe = platform_drv_probe ...

此后调用driver_register(&drv->driver)即:
driver_register(&s3c2410_i2c_driver->driver)

已知在源文件device.c中定义driver_register()函数:
int driver_register(struct device_driver * drv)
{
 /**
  *  此处: drv->bus->probe = &(s3c2410_i2c_driver.device_driver.bus.probe)
  *    即: &platform_bus_type
  *  如果总线的方法方法和设备自己的方法同时存在,将打印告警信息
  */

 if ((drv->bus->probe && drv->probe) ||
     (drv->bus->remove && drv->remove) ||
     (drv->bus->shutdown && drv->shutdown)) {
  printk(KERN_WARNING "Driver '%s' needs updating /
     - please use bus_type methods/n", drv->name);
 }
 /**
  *   klist_init:初始化一个struct klist的节点
  */
 klist_init(&drv->klist_devices, NULL, NULL);
 return bus_add_driver(drv);
}

由s3c2410_i2c_driver与上述分析可以知道,该函数if条件不满足,所以直接执行后续内容.

1.klist_init(&drv->klist_devices, NULL, NULL); 

2.bus_add_driver(drv)即bus_add_driver(s3c2410_i2c_driver.device_driver)

下面研究bus_add_driver(),该函数定义于源文件bus.c中:

int bus_add_driver(struct device_driver *drv)
{
 platform_bus_type
 struct bus_type * bus = get_bus(drv->bus);
 int error = 0;

 if (!bus)
  return -EINVAL;

 pr_debug("bus %s: add driver %s/n", bus->name, drv->name);
 error = kobject_set_name(&drv->kobj, "%s", drv->name);

 /* 设置kobject的名字错误,则释放总线
  */
 if (error)
  goto out_put_bus;


 drv->kobj.kset = &bus->drivers;

 /* 注册kobject
  */
 if ((error = kobject_register(&drv->kobj)))
  goto out_put_bus;

 /* drv->bus->drivers_autoprobe == 1
  */
 if (drv->bus->drivers_autoprobe) {

  /* Enter the "driver_attach"
   */
  error = driver_attach(drv);
  if (error)
   goto out_unregister;
 }
 klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
 module_add_driver(drv->owner, drv);

 error = driver_add_attrs(bus, drv);
 if (error) {
  /* How the hell do we get out of this pickle? Give up */
  printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
   __FUNCTION__, drv->name);
 }
 error = add_bind_files(drv);
 if (error) {
  /* Ditto */
  printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
   __FUNCTION__, drv->name);
 }

 return error;
out_unregister:
 kobject_unregister(&drv->kobj);
out_put_bus:
 put_bus(bus);
 return error;
}

已知在platform.c中:

struct bus_type platform_bus_type = {
 .name  = "platform",
 .dev_attrs = platform_dev_attrs,
 .match  = platform_match,
 .uevent  = platform_uevent,
 .suspend = platform_suspend,
 .suspend_late = platform_suspend_late,
 .resume_early = platform_resume_early,
 .resume  = platform_resume,
};


int __init platform_bus_init(void)
{
 int error;

 error = device_register(&platform_bus);
 if (error)
  return error;
 error =  bus_register(&platform_bus_type);
 if (error)
  device_unregister(&platform_bus);
 return error;
}
在调用bus_register(&platform_bus_type)后:
&platform_bus_type->drivers_autoprobe = 1;

回到上面bus_add_driver()函数,可知在该函数内必定调用driver_attach(drv)
已知driver_attach()函数定义于dd.c:

int driver_attach(struct device_driver * drv)
{
    /**
     *  bus_for_each_dev 遍历bus上的每个设备,找到和驱动匹配的设备
     *  drv->bus,NULL,drv:作为__driver_attach的输入参数
     */
 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

在driver_attach()整个函数中,调用bus_for_each_dev()函数,该函数用于遍历bus
上挂载的每个设备,找到与驱动匹配的设备,bus_for_each_dev()定义于bus.c中:

int bus_for_each_dev(struct bus_type * bus,   // platform_bus_tpye
        struct device * start,   // 0 :从总线上挂的第一个设备开始
                    void * data,   // &s3c2410_i2c_driver->device_driver
                          int (*fn)(struct device *, void *)) // __driver_attach
{
 struct klist_iter i;
 struct device * dev;
 int error = 0;

 if (!bus)
  return -EINVAL;

 /**
  *  klist_iter_init_node ??? 从总线上第一个设备开始匹配
  */
 klist_iter_init_node(&bus->klist_devices, &i,
        (start ? &start->knode_bus : NULL));
 while ((dev = next_device(&i)) && !error)

     /**
      * fn = __driver_attach 用于对指定驱动匹配各个设备
      * 当总线和设备匹配时候匹配函数__driver_attach返回0,跳出while循环,退出匹配
      * ----------
      * fn()函数的输入参数
      *     dev :挂载在总线上的各个驱动
      *     data:&s3c2410_i2c_driver->device_driver
         *
      * 现在跳转到__driver_attach()函数
      */
  error = fn(dev, data);
 klist_iter_exit(&i);
 return error;
}

接着分析__driver_attach()函数,该函数定义于dd.c:
/**
 *     dev :挂载在总线上的各个驱动
 *     data:&s3c2410_i2c_driver->device_driver
 */
static int __driver_attach(struct device * dev, void * data)
{
 struct device_driver * drv = data;

 /*
  * Lock device and try to bind to it. We drop the error
  * here and always return 0, because we need to keep trying
  * to bind to devices and some drivers will return an error
  * simply if it didn't support the device.
  *
  * driver_probe_device() will spit a warning if there
  * is an error.
  */

 if (dev->parent) /* Needed for USB */
  down(&dev->parent->sem);
 down(&dev->sem);
 if (!dev->driver)

     /**
      * driver_probe_device(drv,dev)
      * drv:&s3c2410_i2c_driver->device_driver
      * dev:挂载在总线上的各个驱动
      */
  driver_probe_device(drv, dev);
 up(&dev->sem);
 if (dev->parent)
  up(&dev->parent->sem);

 return 0;
}

接着调用函数driver_probe_device(drv,dev),dirver_probe_device()函数
定义于dd.c:

/**
 * drv:&s3c2410_i2c_driver->device_driver
 * dev:挂载在总线上的各个驱动
 */
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
 int ret = 0;

 if (!device_is_registered(dev))
  return -ENODEV;
 if (drv->bus->match && !drv->bus->match(dev, drv))
  goto done;

 pr_debug("%s: Matched Device %s with Driver %s/n",
   drv->bus->name, dev->bus_id, drv->name);

 ret = really_probe(dev, drv);

done:
 return ret;
}

在上述函数中,drv->bus->match = platform_match,该函数定义于platform.c:

static int platform_match(struct device * dev, struct device_driver * drv)
{
 struct platform_device *pdev = container_of(dev, struct platform_device, dev);

 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}

可见match匹配函数主要用于比较驱动和设备的名称,如果名称不一致则直接从
driver_probe_device()函数中跳出,否则需要进一步调用really_probe()函数.
really_probe()函数定义于dd.c:

static int really_probe(struct device *dev, struct device_driver *drv)
{
 int ret = 0;

 atomic_inc(&probe_count);
 pr_debug("%s: Probing driver %s with device %s/n",
   drv->bus->name, drv->name, dev->bus_id);
 WARN_ON(!list_empty(&dev->devres_head));

 dev->driver = drv;
 if (driver_sysfs_add(dev)) {
  printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
   __FUNCTION__, dev->bus_id);
  goto probe_failed;
 }
   
 /**
  *  dev->bus->probe : platform_bus_type.probe = none?
  */
 if (dev->bus->probe) {
  ret = dev->bus->probe(dev);
  if (ret)
   goto probe_failed;
 } else if (drv->probe) {       /* drv->probe = platform_drv_probe */
  ret = drv->probe(dev);     platform_drv_probe
  if (ret)
   goto probe_failed;
 }

 driver_bound(dev);
 ret = 1;
 pr_debug("%s: Bound Device %s to Driver %s/n",
   drv->bus->name, dev->bus_id, drv->name);
 goto done;

probe_failed:
 devres_release_all(dev);
 driver_sysfs_remove(dev);
 dev->driver = NULL;

 if (ret != -ENODEV && ret != -ENXIO) {
  /* driver matched but the probe failed */
  printk(KERN_WARNING
         "%s: probe of %s failed with error %d/n",
         drv->name, dev->bus_id, ret);
 }
 /*
  * Ignore errors returned by ->probe so that the next driver can try
  * its luck.
  */
 ret = 0;
done:
 atomic_dec(&probe_count);
 wake_up(&probe_waitqueue);
 return ret;
}

上段代码中绿色部分为研究的重要部分
首先 dev->bus->probe()中,dev->bus为&platform_bus_tpye,观察platform_bus_tpye结构体,可知platform_bus_type.probe并未定义,所以需要调用drv->probe(dev)即:
platform_drv_probe(),该函数定义于platform.c:

static int platform_drv_probe(struct device *_dev)
{
 struct platform_driver *drv = to_platform_driver(_dev->driver);
 struct platform_device *dev = to_platform_device(_dev);

 return drv->probe(dev);
}

其中to_platfom_driver()函数的功能是根据platform_driver.device_driver的指针_dev->driver,返回相应的platform_driver的指针;to_platform_device()函数的功能是根据platfomr_device.device的指针_dev,返回相应的platform_device的指针。

最后,通过这两个函数调用后,drv = &s3c2410_i2c_driver .

接着调用 drv->probe(dev),即调用函数 s3c24xx_i2c_probe(&s3c_device_i2c).这里涉及到两个问题:
1. &s3c_device_i2c 这个platform_device型的指针是怎么得到的?
2. s3c24xx_i2c_probe()函数是如何工作的?

首先讨论第一个问题,在mach-s3c2410.c中定义如下:
static struct platform_device *smdk2410_devices[] __initdata = {
        &s3c_device_usb,
        &s3c_device_lcd,
        &s3c_device_wdt,
        &s3c_device_i2c,
        &s3c_device_iis,
};
在plat-s3c24xx/devs.c中定义如下:
static struct resource s3c_i2c_resource[] = {
        [0] = {
                .start = S3C24XX_PA_IIC,
                .end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = IRQ_IIC,
                .end   = IRQ_IIC,
                .flags = IORESOURCE_IRQ,
        }
 
};
struct platform_device s3c_device_i2c = {
        .name             = "s3c2410-i2c",
        .id               = -1,
        .num_resources    = ARRAY_SIZE(s3c_i2c_resource),
        .resource         = s3c_i2c_resource,
};
 

EXPORT_SYMBOL(s3c_device_i2c);

i2c设备s3c_device_i2c在i2c_adapter注册之前就已经加载了!

现在讨论第二个问题:s3c24xx_i2c_probe(&s3c_device_i2c),其中s3c24xx_i2c_probe()函数定义于i2c_s3c2410.c 文件中:
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
 struct s3c24xx_i2c *i2c = &s3c24xx_i2c; /*s3c24xx_i2c:已经初始化的全局变量*/
 struct resource *res;
 int ret;

 /* find the clock and enable it */

 i2c->dev = &pdev->dev;
 i2c->clk = clk_get(&pdev->dev, "i2c");
 if (IS_ERR(i2c->clk)) {
  dev_err(&pdev->dev, "cannot get clock/n");
  ret = -ENOENT;
  goto err_noclk;
 }

 dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);

 clk_enable(i2c->clk);

 /* map the registers */

 /*
  * 获取设备所占有的IO资源
  */
 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (res == NULL) {
  dev_err(&pdev->dev, "cannot find IO resource/n");
  ret = -ENOENT;
  goto err_clk;
 }

 i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
      pdev->name);

 if (i2c->ioarea == NULL) {
  dev_err(&pdev->dev, "cannot request IO/n");
  ret = -ENXIO;
  goto err_clk;
 }

 i2c->regs = ioremap(res->start, (res->end-res->start)+1);

 if (i2c->regs == NULL) {
  dev_err(&pdev->dev, "cannot map IO/n");
  ret = -ENXIO;
  goto err_ioarea;
 }

 dev_dbg(&pdev->dev, "registers %p (%p, %p)/n", i2c->regs, i2c->ioarea, res);

 /* setup info block for the i2c core */

 i2c->adap.algo_data = i2c;         // i2c_adapter私有信息结构数据为 s3c24xx_i2c *i2c
 i2c->adap.dev.parent = &pdev->dev;

 /* initialise the i2c controller */

 ret = s3c24xx_i2c_init(i2c);        // 硬件初始化
 if (ret != 0)
  goto err_iomap;

 /* find the IRQ for this unit (note, this relies on the init call to
  * ensure no current IRQs pending
  */

 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 if (res == NULL) {
  dev_err(&pdev->dev, "cannot find IRQ/n");
  ret = -ENOENT;
  goto err_iomap;
 }

 /*
  * s3c24xx_i2c_irq:中断处理函数
  */
 ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
     pdev->name, i2c);

 if (ret != 0) {
  dev_err(&pdev->dev, "cannot claim IRQ/n");
  goto err_iomap;
 }

 i2c->irq = res;
  
 dev_dbg(&pdev->dev, "irq resource %p (%lu)/n", res,
  (unsigned long)res->start);

 /*
  *  i2c_add_adapter:
  */
 ret = i2c_add_adapter(&i2c->adap);
 if (ret < 0) {
  dev_err(&pdev->dev, "failed to add bus to i2c core/n");
  goto err_irq;
 }

 platform_set_drvdata(pdev, i2c);

 dev_info(&pdev->dev, "%s: S3C I2C adapter/n", i2c->adap.dev.bus_id);
 return 0;

 err_irq:
 free_irq(i2c->irq->start, i2c);

 err_iomap:
 iounmap(i2c->regs);

 err_ioarea:
 release_resource(i2c->ioarea);
 kfree(i2c->ioarea);

 err_clk:
 clk_disable(i2c->clk);
 clk_put(i2c->clk);

 err_noclk:
 return ret;
}

在上面函数中,我们重点需要关注s3c24xx_i2c结构和i2c_add_adapter(&i2c->adap)函数,现在分别加以分析:

1. s3c24xx_i2c定义于i2c_s3c2410.c:
static struct s3c24xx_i2c s3c24xx_i2c = {
 .lock  = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
 .wait  = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
 .tx_setup = 50,
 .adap  = {
  .name   = "s3c2410-i2c",
  .owner   = THIS_MODULE,
  .algo   = &s3c24xx_i2c_algorithm,
  .retries  = 2,
  .class   = I2C_CLASS_HWMON,
 },
};
2. i2c_add_adapter(&i2c->adap):
i2c_add_adapter()函数定义于i2c_core.c ,该文件主要为各种处理器提供与i2c相关的公共接口:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
 int id, res = 0;

retry:
 if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
  return -ENOMEM;

 mutex_lock(&core_lists);
 /* "above" here means "above or equal to", sigh */
 res = idr_get_new_above(&i2c_adapter_idr, adapter,
    __i2c_first_dynamic_bus_num, &id);
 mutex_unlock(&core_lists);

 if (res < 0) {
  if (res == -EAGAIN)
   goto retry;
  return res;
 }

 adapter->nr = id;
 return i2c_register_adapter(adapter);  // 注册适配器
}

这里涉及到一个很陌生的算法,是02年才引入linux的,我们只需要了解该算法的一些函数即可:
static DEFINE_IDR(i2c_adapter_idr);
/**
 *  (add by lih)等价于:
 */
static struct idr  i2c_adapter_idr = LIST_HEAD_INIT(i2c_adapter_idr);
struct idr {
 struct idr_layer *top;
 struct idr_layer *id_free;
 int    layers;
 int    id_free_cnt;
 spinlock_t   lock;
};
该算法的作用是,动态生成一个int型的数据和一个指针对应,这样我们可以通过之前生成的int型数据获取我们需要的指针。
1. 在使用该算法之前,我们需要申请一个结构体,即上面的
static DEFINE_IDR(i2c_adapter_idr);
2. 使用idr_pre_get(&i2c_adapter_idr, GFP_KERNEL)为该结构分配空间;
3. 使用res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
把我们的指针adapter与int型变量id对应起来。
通过以上步骤,在我们需要使用adapter时,可以通过id变量来得到.

其中__i2c_first_dynamic_bus_num为一个常量,我们动态生成的int型变量id大于等于该值。

之后,我们通过adapter->nr = id把动态生成的id号付给该i2c适配器。注意,这个量相对重要,后来这个id号将作为i2c适配器设备的次设备号,我们打开i2c适配器,需要通过该量。

之后我们通过调用i2c_register_adapter(adapter)注册i2c适配器,其中i2c_register_adapter()函数定义于:i2c_core.c

至此一个i2c_adapter适配器注册成功!!!

--------------------------

该文章肯定有很多理解不到位的地方,我会在后期的学习中不断跟新!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值