LINUX下IIC子系统分析

本文深入分析了Linux平台下IIC子系统的工作原理,特别是针对内核中新模式(new style)的IIC驱动。通过S3C2440平台上的24C08驱动为例,讲解了如何生成用户空间的设备节点,以及用户空间如何与内核驱动进行数据交互。重点介绍了设备驱动的注册、匹配过程,以及I2C消息的构造和传输机制。
摘要由CSDN通过智能技术生成

    LINUX平台下IIC子系统的经典分布图如下:

下面主要针对内核中IIC新模式(new style)进行分析.

    下面以S3C2440平台搭载的24C08进行整个LINUX IIC子系统的分析.

 

1.如何生成用户空间的设备节点:

    1-1.24c08驱动端:

        24c08的驱动代码位于drivers/misc/eeprom/at24.c

        入口函数:

module_init(at24_init);
static int __init at24_init(void)
{
	return i2c_add_driver(&at24_driver);
}

在LINUX内核中,每个IIC驱动对应的结构体为struct i2c_driver.24c08也不例外:

static struct i2c_driver at24_driver = {
	.driver = {
		.name = "at24",
		.owner = THIS_MODULE,
	},
	.probe = at24_probe,
	.remove = __devexit_p(at24_remove),
	.id_table = at24_ids,
};

    这里有一个很重要的域:id_table:

static const struct i2c_device_id at24_ids[] = {
	/* needs 8 addresses as A0-A2 are ignored */
	{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
	/* old variants can't be handled with this generic entry! */
	{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
	{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
	/* spd is a 24c02 in memory DIMMs */
	{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
		AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
	{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
	/* 24rf08 quirk is handled at i2c-core */
	{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
	{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
	{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
	{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
	{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
	{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
	{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
	{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
	{ "at24", 0 },
	{ /* END OF LIST */ }
};

    id_table域是探测此驱动支持哪些设备的依据,下面便是整个系统的调用流程追踪,主要是函数i2c_add_driver(&at24_driver):

static inline int i2c_add_driver(struct i2c_driver *driver)
{
	return i2c_register_driver(THIS_MODULE, driver);
}

-->

/*
 * An i2c_driver is used with one or more i2c_client (device) nodes to access
 * i2c slave chips, on a bus instance associated with some i2c_adapter.
 */

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	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);
	if (res)
		return res;

	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	mutex_lock(&core_lock);
	bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
	mutex_unlock(&core_lock);

	return 0;
}

    linux每个IIC驱动都是挂在IIC这条总线上的,见上面代码:

	driver->driver.bus = &i2c_bus_type;

    类似usb、spi等子系统一样,每个驱动都会标明其所属的总线类型.这种驱动依附总线的一个重要意义在于匹配条件:IIC有IIC的匹配条件、USB和USB的匹配条件等.在这里需要铭记我们这个24c08属于iic driver,而iic driver是隶属IIC BUS的.下面继承跟踪域id_table的意义,在函数i2c_register_driver()函数里面第二个关注的地方语句是:

	res = driver_register(&driver->driver);

    其实,这是LINUX驱动里面一个"核心基类"函数,所有的隶属总线的驱动(像uart、spi、usb等)均路由此函数,因此,可以独立来理解,因为此函数与子系统无关.其最重大的意义在于回调到具体调用此函数的总线的match函数的执行.下面通过代码验证这一点:  

int driver_register(struct device_driver *drv)
{
        ... ...;
	ret = bus_add_driver(drv);
        ... ...;
}

    展开函数bus_add_driver():

int bus_add_driver(struct device_driver *drv)
{
	... ...;

	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	
	... ...;
}

    展开函数driver_attach():

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

    函数bus_for_each_dev会遍历挂在此总线上的设备,再根据一定的"匹配条件"(主要通过回调函数__driver_attach)来看这个设备是否被这个驱动所支持.展开如下:

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;
}

     下面我们来看函数__driver_attach():

static int __driver_attach(struct device *dev, void *data)
{

	if (!driver_match_device(drv, dev))
		return 0;

	if (!dev->driver)
		driver_probe_device(drv, dev);
}

    这里有两个比较重要的函数:driver_match_device()和driver_probe_device().其中和id_table域相关的函数是driver_match_device()函数;而后者是device和driver相关的函数.

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

    可以看到,这里将会回调总线上的match函数,对于IIC总线而言,其match函数是:i2c_device_match()

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.suspend	= i2c_device_suspend,
	.resume		= i2c_device_resume,
};

    展开函数i2c_device_match():

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}  

    继续展开函数i2c_match_id():

static const struct i2c_d
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值