转载自
http://blog.csdn.net/mayaoyao11/article/details/6645990
看linux i2c驱动也有一段时间了,由于中间暑假回家一趟搁浅了,回家之前看的忘的一干二净。还是把自己理解的内容写下来,一是下次用的时候便于快速捡起来,二梳理IIC驱动复杂框架,三欢迎大家给我指正,驱动复杂,希望大家互相帮助。
Linux中I2C体系结构如下图所示(图片来源于网络)。图中用分割线分成了三个层次:用户空间(也就是应用程序),内核(也就是驱动部分)和硬件(也就是实际物理设备,这里就是6410中的i2c控制器和at24xx)。这个够清晰了吧?我们主要研究的是中间那一层。
中间一层又分为i2c设备驱动、i2c-core层、i2c控制器驱动三部分。其中i2c-core提供了i2c设备驱动和控制器驱动的注册、注销方法。其上三部分Driver、Client、i2c-dev用来描述i2c设备(如at24xx)及其驱动,下面Algorithm、Adapter、及Adapter specific code 用来描述i2c控制器驱动。
以s3c6410 linux 3.0.1下iic器件 at24xx驱动为例进行分析,主要包含以下文件。
1、i2c-core.c 实现了I2C的核心功能
2、i2c-dev.c 实现了I2C控制器设备文件的功能
3、At24.c 实现了at24xx系列IIC接口设备驱动
4、i2c-s3c2410.c 实现了6410 IIC控制器驱动
5、Algos 实现了一些IIC控制器的algorithm
6、mach-mini6410.c 定义并注册了I2C平台设备
我们根据系统加载有关I2C模块的顺序进行分析。
1、文件mach-smdk64106410.c中 MACHINE_START .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在arch_initcall(customize_machine)中
-
MACHINE_START(OK6410, "OK6410")
-
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
-
//.phys_io = S3C_PA_UART & 0xfff00000,
-
//.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
-
.boot_params = S3C64XX_PA_SDRAM + 0x100,
-
.init_irq = s3c6410_init_irq,
-
.map_io = smdk6410_map_io,
-
.init_machine = smdk6410_machine_init,
-
.timer = &s3c24xx_timer,
-
MACHINE_END
再看看 smdk6410_machine_init(void) 中和 i2c 有关的部分:
-
static void __init smdk6410_machine_init(void)
-
{
-
u32 cs1;
-
-
s3c_i2c0_set_platdata(NULL);
-
#ifdef CONFIG_S3C_DEV_I2C1
-
s3c_i2c1_set_platdata(NULL);
-
#endif
-
...
-
-
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
-
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
-
-
...
-
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices))
-
}
platform_add_devices 添加平台设备,并向系统注册平台设备,包括s3c_device_i2c0。
-
static struct platform_device *smdk6410_devices[] __initdata = {
-
...
-
&s3c_device_i2c0,
-
#ifdef CONFIG_S3C_DEV_I2C1
-
&s3c_device_i2c1,
-
#endif
-
&s3c_device_fb,
-
...
-
};
其中 s3c_device_i2c0定义在/arch/arm/plat-samsung/dev-i2c0.c 中:
-
struct platform_device s3c_device_i2c0 = {
-
.name = "s3c2410-i2c", /* 此处的设备名称 s3c2410-i2c */
-
#ifdef CONFIG_S3C_DEV_I2C1
-
.id = 0,
-
#else
-
.id = -1,
-
#endif
-
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
-
.resource = s3c_i2c_resource,
-
};
2.到系统注册了platform_device 我们会想到 platform_driver 在何处注册,只有设备没有驱动也是不行滴。
在 /drivers/i2c/busses/i2c-s3c2410.c 中我们看到了 i2c 平台驱动注册 platform_driver_register(&s3c24xx_i2c_driver)。由 subsys_initcall(i2c_adap_s3c_init)我们可以看出此模块是系统启动时自己加载的。
-
static struct platform_driver s3c24xx_i2c_driver = {
-
.probe = s3c24xx_i2c_probe,
-
.remove = s3c24xx_i2c_remove,
-
.id_table = s3c24xx_driver_ids, /* 此处要注意 */
-
.driver = {
-
.owner = THIS_MODULE,
-
.name = "s3c-i2c", /* 此处的驱动名称 s3c-i2c 与上面的设备名称 s3c2410-i2c 不一致 */
-
.pm = S3C24XX_DEV_PM_OPS,
-
},
-
};
-
-
static int __init i2c_adap_s3c_init(void)
-
{
-
return platform_driver_register(&s3c24xx_i2c_driver);
-
}
-
-
subsys_initcall(i2c_adap_s3c_init);
在这里发现s3c_device_i2c0.name 和 s3c24xx_i2c_driver.driver.name 不一致,设备和驱动如何绑定的呢?
3.接着上次分析s3c24xx_i2c_driver 中有一个成员 .id_table = s3c24xx_driver_ids 这个好像以前没见到过,孤陋寡闻了哈。先来看看是如何定义的
-
static struct platform_device_id s3c24xx_driver_ids[] = {
-
{
-
.name = "s3c2410-i2c",
-
.driver_data = TYPE_S3C2410,
-
}, {
-
.name = "s3c2440-i2c",
-
.driver_data = TYPE_S3C2440,
-
}, { },
-
};
细心会发现这里的 .name 和 s3c_device_i2c0 中的成员 .name 一致。
我们来具体追踪 平台驱动注册时是如何查找总线上的设备并与其进行匹配并绑定的。
platform_driver_register(&s3c24xx_i2c_driver)
--> driver_register(&drv->driver)
--> bus_add_driver(drv)
--> driver_attach(drv)
--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
--> driver_match_device(drv, dev)
-
static int __driver_attach(struct device *dev, void *data)
-
{
-
struct device_driver *drv = data;
-
-
if (!driver_match_device(drv, dev)) /* 对添加的驱动进行匹配 */
-
return 0;
-
-
if (dev->parent) /* Needed for USB */
-
device_lock(dev->parent);
-
device_lock(dev);
-
if (!dev->driver)
-
driver_probe_device(drv, dev);
-
device_unlock(dev);
-
if (dev->parent)
-
device_unlock(dev->parent);
-
-
return 0;
-
static inline int driver_match_device(struct device_driver *drv, struct device *dev)
-
{
-
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
-
}
继续分析的话就要知道 drv->bus->match 何时进行的赋值。通过以下两段代码我们知道 drv->bus->match 调用了platform_bus_type中的 platform_match。
-
int platform_driver_register(struct platform_driver *drv)
-
{
-
drv->driver.bus = &platform_bus_type;
-
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;
-
-
return driver_register(&drv->driver);
-
}
-
struct bus_type platform_bus_type = {
-
.name = "platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.pm = &platform_dev_pm_ops,
-
};
我们看platform_bus_type中的 platform_match代码:
-
static int platform_match(struct device *dev, struct device_driver *drv)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct platform_driver *pdrv = to_platform_driver(drv);
-
-
/* Attempt an OF style match first */
-
if (of_driver_match_device(dev, drv))
-
return 1;
-
-
/* Then try to match against the id table */
-
if (pdrv->id_table)
-
return platform_match_id(pdrv->id_table, pdev) != NULL;
-
-
/* fall-back to driver name match */
-
return (strcmp(pdev->name, drv->name) == 0);
-
}
继续
-
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;
-
}
这里的strcmp(pdev->name, id->name) 就完成了 pdev->name和id->name 的匹配。
4.上一此说到了驱动和设备匹配,中间隔了几天读了下 LDD3 ch14: The Linux Device Model,受益匪浅。
平台驱动中添加 .id_table 成员好处是使驱动和设备可以一对多,即一个驱动可以支持多个设备。如果我们只使用设备里的 name 和驱动里的name进行匹配匹配那么设备和驱动只能一对一了。
关于设备和驱动匹配后如何绑定的我们来继续分析代码。
-
static int __driver_attach(struct device *dev, void *data)
-
{
-
struct device_driver *drv = data;
-
-
if (!driver_match_device(drv, dev)) /* 对添加的驱动进行匹配 */
-
return 0;
-
-
if (dev->parent) /* Needed for USB */
-
device_lock(dev->parent);
-
device_lock(dev);
-
if (!dev->driver)
-
driver_probe_device(drv, dev); /* 绑定 */
-
device_unlock(dev);
-
if (dev->parent)
-
device_unlock(dev->parent);
-
-
return 0;
上节分析到在driver_match_device(drv, dev) 中完成了驱动和设备的匹配, 向下看 driver_probe_device(drv, dev) 函数 注释上写了试图把设备和驱动绑定到一起。
-
int driver_probe_device(struct device_driver *drv, struct device *dev)
-
{
-
int ret = 0;
-
-
if (!device_is_registered(dev))
-
return -ENODEV;
-
-
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
-
drv->bus->name, __func__, dev_name(dev), drv->name);
-
-
pm_runtime_get_noresume(dev);
-
pm_runtime_barrier(dev);
-
ret = really_probe(dev, drv);
-
pm_runtime_put_sync(dev);
-
-
return ret;
-
}
继续分析里面有个 real_probe(dev, drv) 函数
-
static int really_probe(struct device *dev, struct device_driver *drv)
-
{
-
int ret = 0;
-
-
atomic_inc(&probe_count);
-
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
-
drv->bus->name, __func__, drv->name, dev_name(dev));
-
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",
-
__func__, dev_name(dev));
-
goto probe_failed;
-
}
-
-
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev);
-
if (ret)
-
goto probe_failed;
-
} else if (drv->probe) {
-
ret = drv->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
-
-
driver_bound(dev);
-
ret = 1;
-
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
-
drv->bus->name, __func__, dev_name(dev), 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_name(dev), 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->driver = drv, 这里应该就是把设备结构里的驱动指向了我们注册的 driver。那下面为什么还有一行driver_bound(dev),看样还要继续分析。
-
static void driver_bound(struct device *dev)
-
{
-
if (klist_node_attached(&dev->p->knode_driver)) {
-
printk(KERN_WARNING "%s: device %s already bound\n",
-
__func__, kobject_name(&dev->kobj));
-
return;
-
}
-
-
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
-
__func__, dev->driver->name);
-
-
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);/* 加入到驱动的的设备链表 */
-
-
if (dev->bus)
-
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
-
BUS_NOTIFY_BOUND_DRIVER, dev);
-
}
把函数设备加入了驱动的的设备链表。
中间还落下了一部分 就是 real_probe(dev, drv) 函数的
-
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev);
-
if (ret)
-
goto probe_failed;
-
} else if (drv->probe) {
-
ret = drv->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
platform_bus_type中没有probe函数,dev->bus->probe为空,因此该处调用了drv->probe
-
static struct platform_driver s3c24xx_i2c_driver = {
-
.probe = s3c24xx_i2c_probe,
-
.remove = s3c24xx_i2c_remove,
-
.id_table = s3c24xx_driver_ids,
-
.driver = {
-
.owner = THIS_MODULE,
-
.name = "s3c-i2c",
-
.pm = S3C24XX_DEV_PM_OPS,
-
},
-
};
中的 s3c_i2c_probe 函数。此处要明确下,处传给s3c24xx_i2c_probe 的参数是我们注册的i2c 平台设备s3c_device_i2c0。
5.s3c24xx_i2c_probe分析:
-
static int s3c24xx_i2c_probe(struct platform_device *pdev)
-
{
-
struct s3c24xx_i2c *i2c;
-
struct s3c2410_platform_i2c *pdata;
-
struct resource *res;
-
int ret;
-
-
pdata = pdev->dev.platform_data; /* 获得s3c_device_i2c0.dev.platform_data*/
-
if (!pdata) {
-
dev_err(&pdev->dev, "no platform data\n");
-
return -EINVAL;
-
}
-
-
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
-
if (!i2c) {
-
dev_err(&pdev->dev, "no memory for state\n");
-
return -ENOMEM;
-
}
-
-
/* 初始化i2c适配器adap */
-
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
-
i2c->adap.owner = THIS_MODULE;
-
i2c->adap.algo = &s3c24xx_i2c_algorithm; /* 总线通信方法 */
-
i2c->adap.retries = 2;
-
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
-
i2c->tx_setup = 50;
-
-
spin_lock_init(&i2c->lock);
-
init_waitqueue_head(&i2c->wait);
-
-
/* find the clock and enable it */
-
-
/* 使能i2c时钟 */
-
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 */
-
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, resource_size(res),
-
pdev->name);
-
-
if (i2c->ioarea == NULL) {
-
dev_err(&pdev->dev, "cannot request IO\n");
-
ret = -ENXIO;
-
goto err_clk;
-
}
-
-
i2c->regs = ioremap(res->start, resource_size(res));
-
-
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->adap.dev.parent = &pdev->dev;
-
-
/* initialise the i2c controller */
-
ret = s3c24xx_i2c_init(i2c); /* 初始化IO, s3c_device_i2c0从器件地址, 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
-
*/
-
i2c->irq = ret = platform_get_irq(pdev, 0);
-
if (ret <= 0) {
-
dev_err(&pdev->dev, "cannot find IRQ\n");
-
goto err_iomap;
-
}
-
-
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
-
dev_name(&pdev->dev), i2c);
-
-
if (ret != 0) {
-
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
-
goto err_iomap;
-
}
-
-
ret = s3c24xx_i2c_register_cpufreq(i2c);
-
if (ret < 0) {
-
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
-
goto err_irq;
-
}
-
-
/* Note, previous versions of the driver used i2c_add_adapter()
-
* to add the bus at any number. We now pass the bus number via
-
* the platform data, so if unset it will now default to always
-
* being bus 0.
-
*/
-
-
i2c->adap.nr = pdata->bus_num;
-
-
ret = i2c_add_numbered_adapter(&i2c->adap); /* 将i2c->adap按照i2c->adap.nr注册进i2c_bus_type, 注册该设备device_register(&adap->dev)并将i2c->adap->dev添加至i2c_bus_type->klist_devices中, 完成板文件中的设备client初始化 */
-
if (ret < 0) {
-
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
-
goto err_cpufreq;
-
}
-
-
platform_set_drvdata(pdev, i2c);
-
-
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
-
clk_disable(i2c->clk);
-
return 0;
-
-
err_cpufreq:
-
s3c24xx_i2c_deregister_cpufreq(i2c);
-
-
err_irq:
-
free_irq(i2c->irq, 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:
-
kfree(i2c);
-
return ret;
-
}
i2c_add_numbered_adapter将adapter注册进具有总线号的i2c:
-
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
-
{
-
int id;
-
int status;
-
-
if (adap->nr & ~MAX_ID_MASK)
-
return -EINVAL;
-
-
retry:
-
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
-
return -ENOMEM;
-
-
mutex_lock(&core_lock);
-
/* "above" here means "above or equal to", sigh;
-
* we need the "equal to" result to force the result
-
*/
-
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
-
if (status == 0 && id != adap->nr) {
-
status = -EBUSY;
-
idr_remove(&i2c_adapter_idr, id);
-
}
-
mutex_unlock(&core_lock);
-
if (status == -EAGAIN)
-
goto retry;
-
-
if (status == 0)
-
status = i2c_register_adapter(adap);/* 注册adapter */
-
return status;
-
}
i2c_register_adapter(adap)函数:
-
static int i2c_register_adapter(struct i2c_adapter *adap)
-
{
-
int res = 0;
-
-
/* Can't register until after driver model init */
-
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
-
res = -EAGAIN;
-
goto out_list;
-
}
-
-
/* Sanity checks */
-
if (unlikely(adap->name[0] == '\0')) {
-
pr_err("i2c-core: Attempt to register an adapter with "
-
"no name!\n");
-
return -EINVAL;
-
}
-
if (unlikely(!adap->algo)) {
-
pr_err("i2c-core: Attempt to register adapter '%s' with "
-
"no algo!\n", adap->name);
-
return -EINVAL;
-
}
-
-
rt_mutex_init(&adap->bus_lock);
-
mutex_init(&adap->userspace_clients_lock);
-
INIT_LIST_HEAD(&adap->userspace_clients);
-
-
/* Set default timeout to 1 second if not already set */
-
if (adap->timeout == 0)
-
adap->timeout = HZ;
-
-
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
-
adap->dev.bus = &i2c_bus_type; /* 关联i2c_bus_type */
-
adap->dev.type = &i2c_adapter_type;
-
res = device_register(&adap->dev); /* 注册adapter->dev, 将i2c->adap->dev添加至i2c_bus_type->klist_devices */
-
if (res)
-
goto out_list;
-
-
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
-
-
#ifdef CONFIG_I2C_COMPAT
-
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
-
adap->dev.parent);
-
if (res)
-
dev_warn(&adap->dev,
-
"Failed to create compatibility class link\n");
-
#endif
-
-
/* create pre-declared device nodes */
-
if (adap->nr < __i2c_first_dynamic_bus_num)
-
i2c_scan_static_board_info(adap); /* 完成板文件中的设备client初始化, 将板文件中的__i2c_board_list添加进i2c_bus_type,并关联adapter */
-
-
/* Notify drivers */
-
mutex_lock(&core_lock);
-
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); /* 列举bus->p->klist_drivers, 此时i2c-core.c中的dummy_driver出现在该驱动列表中, 调用__process_new_adapter */
-
mutex_unlock(&core_lock);
-
-
return 0;
-
-
out_list:
-
mutex_lock(&core_lock);
-
idr_remove(&i2c_adapter_idr, adap->nr);
-
mutex_unlock(&core_lock);
-
return res;
-
}
可以看出,该函数获取板文件中的相关硬件信息并完成注册,完成s3c6410的i2c控制器初始化,并添加进i2c-core。
6.驱动前面做的工作:1、注册了 i2c控制器设备及驱动,2、对s3c6410的控制器进行了初始化。
呵呵,如果整过单片机知道如何用单片机读取 AT24XX存储器里的内容。 应该是一开始初始化I2C控制器,然后就可以按照I2C时序进行读取存储器里的数据。
但是linux下就不一样了,linux系统把设备当成文件对待。那如果想读取数据应该打开哪个设备文件,还有和设备文件惺惺相惜的 file_operations 具体如何实现。
带着这些问题继续分析代码。看/drivers/i2c/i2c-dev.c,此文件实现了I2C 字符设备文件操作接口。
先看设备驱动模块加载函数module_init(i2c_dev_init)。
-
static int __init i2c_dev_init(void)
-
{
-
int res;
-
-
printk(KERN_INFO "i2c /dev entries driver\n");
-
-
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); /* 注册字符设备 */
-
if (res)
-
goto out;
-
-
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); /* 创建i2v-dev类 */
-
if (IS_ERR(i2c_dev_class)) {
-
res = PTR_ERR(i2c_dev_class);
-
goto out_unreg_chrdev;
-
}
-
-
/* Keep track of adapters which will be added or removed later */
-
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
-
if (res)
-
goto out_unreg_class;
-
-
/* Bind to already existing adapters right away */
-
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
-
-
return 0;
-
-
out_unreg_class:
-
class_destroy(i2c_dev_class);
-
out_unreg_chrdev:
-
unregister_chrdev(I2C_MAJOR, "i2c");
-
out:
-
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
-
return res;
-
}
i2c_for_each_dev(NULL, i2cdev_attach_adapter)
--> bus_for_each_dev(&i2c_bus_type, NULL, data, i2cdev_attach_adapter)
-
int bus_for_each_dev(struct bus_type *bus, struct device *start,
-
void *data, int (*fn)(struct device *, void *))
-
{
-
struct klist_iter i;
-
struct device *dev;
-
int error = 0;
-
-
if (!bus)
-
return -EINVAL;
-
-
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;
-
}
遍历i2c_bus_type->p->klist_devices并调用 i2cdev_attach_adapter(dev,data)
-
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
-
{
-
struct i2c_adapter *adap;
-
struct i2c_dev *i2c_dev;
-
int res;
-
-
if (dev->type != &i2c_adapter_type)
-
return 0;
-
adap = to_i2c_adapter(dev);
-
-
i2c_dev = get_free_i2c_dev(adap);
-
if (IS_ERR(i2c_dev))
-
return PTR_ERR(i2c_dev);
-
-
/* register this i2c device with the driver core */
-
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
-
MKDEV(I2C_MAJOR, adap->nr), NULL,
-
"i2c-%d", adap->nr);
-
if (IS_ERR(i2c_dev->dev)) {
-
res = PTR_ERR(i2c_dev->dev);
-
goto error;
-
}
-
res = device_create_file(i2c_dev->dev, &dev_attr_name);
-
if (res)
-
goto error_destroy;
-
-
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
-
adap->name, adap->nr);
-
return 0;
-
error_destroy:
-
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
-
error:
-
return_i2c_dev(i2c_dev);
-
return res;
-
}
将设备dev与s3c6410的i2c适配器关联,并注册进i2c_dev_class。
后面的dev设备操作就可以通过适配器完成了。
对于该方法下操作EEPROM给出一段示例代码,EEPROM的从机地址为0x50
(1)向0x10中写入数据0x58
(2)从0x10中读出存储的数据
点击(此处)折叠或打开
-
/*i2c_test.c
-
* hongtao_liu <lht@farsight.com.cn>
-
*/
-
#include <stdio.h>
-
#include <linux/types.h>
-
#include <stdlib.h>
-
#include <fcntl.h>
-
#include <unistd.h>
-
#include <sys/types.h>
-
#include <errno.h>
-
#define I2C_RETRIES 0x0701
-
#define I2C_TIMEOUT 0x0702
-
#define I2C_RDWR 0x0707
-
-
/*********定义struct i2c_rdwr_ioctl_data和struct i2c_msg,要和内核一致*******/
-
struct i2c_msg {
-
unsigned short addr;
-
unsigned short flags;
-
#define I2C_M_TEN 0x0010
-
#define I2C_M_RD 0x0001
-
unsigned short len;
-
unsigned char *buf;
-
};
-
-
struct i2c_rdwr_ioctl_data {
-
struct i2c_msg *msgs;
-
int nmsgs;
-
/* nmsgs这个数量决定了有多少开始信号,对于“单开始时序”,取1*/
-
};
-
-
/***********主程序***********/
-
int main()
-
{
-
int fd,ret;
-
struct i2c_rdwr_ioctl_data e2prom_data;
-
fd = open("/dev/i2c-0",O_RDWR);
-
/*
-
* /dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c
-
* 的方式,就没有,也不需要这个节点。
-
*/
-
if(fd<0) {
-
perror("open error");
-
}
-
e2prom_data.nmsgs = 2;
-
/*
-
* 因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将
-
* e2prom_data.nmsgs配置为2
-
*/
-
e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));
-
if(!e2prom_data.msgs) {
-
perror("malloc error");
-
exit(1);
-
}
-
-
ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/
-
ioctl(fd,I2C_RETRIES,2);/*重复次数*/
-
-
/***write data to e2prom**/
-
e2prom_data.nmsgs=1;
-
(e2prom_data.msgs[0]).len=2; //1个 e2prom 写入目标的地址和1个数据
-
(e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址
-
(e2prom_data.msgs[0]).flags=0; //write
-
(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);
-
(e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 写入目标的地址
-
(e2prom_data.msgs[0]).buf[1]=0x58;//the data to write
-
-
ret = ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
-
if(ret<0) {
-
perror("ioctl error1");
-
}
-
-
sleep(1);
-
-
/******read data from e2prom*******/
-
e2prom_data.nmsgs=2;
-
(e2prom_data.msgs[0]).len=1; //e2prom 目标数据的地址
-
(e2prom_data.msgs[0]).addr=0x50; // e2prom 设备地址
-
(e2prom_data.msgs[0]).flags=0;//write
-
(e2prom_data.msgs[0]).buf[0]=0x10;//e2prom数据地址
-
(e2prom_data.msgs[1]).len=1;//读出的数据
-
(e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址
-
(e2prom_data.msgs[1]).flags=I2C_M_RD;//read
-
(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。
-
(e2prom_data.msgs[1]).buf[0]=0;//初始化读缓冲
-
-
ret = ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
-
if(ret<0) {
-
perror("ioctl error2");
-
}
-
printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);
-
/***打印读出的值,没错的话,就应该是前面写的0x58了***/
-
free(e2prom_data.msgs);
-
close(fd);
-
return 0;
-
}
7.i2c-dev.c文件定的主设备号为89的设备可以方便地给应用程序提供读写i2c设备寄存器的能力,使得工程师大多数时候并不需要为具体的i2c设备驱动定义文件操作接口。
对于某些设备驱动,比如/drivers/misc/eeprom/at24.c,它调用了i2c核心提供的api,不依赖于cpu的类型和i2c适配器的硬件特性,通用型很强。
at24设备驱动的注册过程如下:
at24_init(void)
-->i2c_add_driver(&at24_driver)
-->i2c_register_driver(THIS_MODULE, driver)
-->driver_register(&driver->driver)
-->bus_add_driver(drv)
-->driver_attach(drv)
-->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
-->__driver_attach(dev, drv)
-->driver_match_device(drv, dev)
-->driver_probe_device(drv, dev)
-->really_probe(dev, drv)
-->i2c_device_probe(dev)
-->driver->probe(client, i2c_match_id(driver->id_table, client))
-->at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
注册过程虽然比较复杂,通过i2c-core将__i2c_board_list中的client(在适配器注册过程中i2c_add_numbered_adapter()-->i2c_register_adapter(adap)-->i2c_scan_static_board_info(adap)-->i2c_new_device(adapter, &devinfo->board_info) 中完成板文件 client->adapter = adap的关联)与at24驱动关联,在对at24读写时调用i2c-core.c中的i2c_transfer,从而调用adatper完成操作,具体过程就不详细分析了。
阅读(1319) | 评论(0) | 转发(0) |