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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值