本文共 13466 字,大约阅读时间需要 44 分钟。
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_type2.令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", ____, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed/n", ____, 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", ____, 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适配器注册成功!!!
转载地址:http://cakci.baihongyu.com/