linux iic设备中断,Linux设备驱动之I2C架构分析(一)

Linux设备驱动之I2C架构分析

一:前言

I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以.而I2C是基于multi master机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可.

二:I2C架构概述

在linux中,I2C驱动架构如下所示:

如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作(struct i2c_adapter).再通过i2c core层将i2c设备与i2c adapter关联起来.

这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux 2.6.26.分析的代码基本位于: linux-2.6.26.3/drivers/i2c/位置.

三:adapter注册

在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已.

对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.

分别来看一下这两个函数的代码:

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_lock);

/* "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_lock);

if (res < 0) {

if (res == -EAGAIN)

goto retry;

return res;

}

adapter->nr = id;

return i2c_register_adapter(adapter);

}

在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<< linux文件系统之文件的读写>>中有关radix tree的分析.

注意一下idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,

然后将对应的id号存放在adapter->nr中.调用i2c_register_adapter(adapter)对这个adapter进行进一步注册.

看一下另外一人注册函数: i2c_add_numbered_adapter( ),如下所示:

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

return status;

}

对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.

过一步跟踪i2c_register_adapter().代码如下:

static int i2c_register_adapter(struct i2c_adapter *adap)

{

int res = 0, dummy;

mutex_init(&adap->bus_lock);

mutex_init(&adap->clist_lock);

INIT_LIST_HEAD(&adap->clients);

mutex_lock(&core_lock);

/* Add the adapter to the driver core.

* If the parent pointer is not set up,

* we add this adapter to the host bus.

*/

if (adap->dev.parent == NULL) {

adap->dev.parent = &platform_bus;

pr_debug("I2C adapter driver [%s] forgot to specify "

"physical device ", adap->name);

}

sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);

adap->dev.release = &i2c_adapter_dev_release;

adap->dev.class = &i2c_adapter_class;

res = device_register(&adap->dev);

if (res)

goto out_list;

dev_dbg(&adap->dev, "adapter [%s] registered ", adap->name);

/* create pre-declared device nodes for new-style drivers */

if (adap->nr < __i2c_first_dynamic_bus_num)

i2c_scan_static_board_info(adap);

/* let legacy drivers scan this bus for matching devices */

dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,

i2c_do_add_adapter);

out_unlock:

mutex_unlock(&core_lock);

return res;

out_list:

idr_remove(&i2c_adapter_idr, adap->nr);

goto out_unlock;

}

首先对adapter和adapter中内嵌的struct device结构进行必须的初始化.之后将adapter内嵌的struct device注册.

在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c + 总线号.

测试一下:

[eric@mochow i2c]$ cd /sys/class/i2c-adapter/

[eric@mochow i2c-adapter]$ ls

i2c-0

可以看到,在我的PC上,有一个I2C adapter,看下详细信息:

[eric@mochow i2c-adapter]$ tree

.

`-- i2c-0

|-- device -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-0

|-- name

|-- subsystem -> ../../../class/i2c-adapter

`-- uevent

3 directories, 2 files

可以看到,该adapter是一个PCI设备.

继续往下看:

之后,在注释中看到,有两种类型的driver,一种是new-style drivers,另外一种是legacy drivers

New-style drivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2c driver的匹配上.

3.1: new-style 形式的adapter注册

对于第一种,也就是new-style drivers,将相关代码再次列出如下:

if (adap->nr < __i2c_first_dynamic_bus_num)

i2c_scan_static_board_info(adap);

如果adap->nr 小于__i2c_first_dynamic_bus_num的话,就会进入到i2c_scan_static_board_info().

结合我们之前分析的adapter的两种注册分式: i2c_add_adapter()所分得的总线号肯会不会小于__i2c_first_dynamic_bus_num.只有i2c_add_numbered_adapter()才有可能满足:

(adap->nr < __i2c_first_dynamic_bus_num)

而且必须要调用i2c_register_board_info()将板子上的I2C设备信息预先注册时才会更改__i2c_first_dynamic_bus_num的值.在x86上只没有使用i2c_register_board_info()的.因此,x86平台上的分析可以忽略掉new-style driver的方式.不过,还是详细分析这种情况下.

首先看一下i2c_register_board_info(),如下:

int __init

i2c_register_board_info(int busnum,

struct i2c_board_info const *info, unsigned len)

{

int status;

mutex_lock(&__i2c_board_lock);

/* dynamic bus numbers will be assigned after the last static one */

if (busnum >= __i2c_first_dynamic_bus_num)

__i2c_first_dynamic_bus_num = busnum + 1;

for (status = 0; len; len--, info++) {

struct i2c_devinfo  *devinfo;

devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

if (!devinfo) {

pr_debug("i2c-core: can't register boardinfo! ");

status = -ENOMEM;

break;

}

devinfo->busnum = busnum;

devinfo->board_info = *info;

list_add_tail(&devinfo->list, &__i2c_board_list);

}

mutex_unlock(&__i2c_board_lock);

return status;

}

这个函数比较简单, struct i2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表.

跟踪i2c_scan_static_board_info():代码如下:

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

{

struct i2c_devinfo  *devinfo;

mutex_lock(&__i2c_board_lock);

list_for_each_entry(devinfo, &__i2c_board_list, list) {

if (devinfo->busnum == adapter->nr

&& !i2c_new_device(adapter,

&devinfo->board_info))

printk(KERN_ERR "i2c-core: can't create i2c%d-%04x ",

i2c_adapter_id(adapter),

devinfo->board_info.addr);

}

mutex_unlock(&__i2c_board_lock);

}

该函数遍历挂在__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息.

如果指定设备是位于adapter所在的I2C总线上,那么,就调用i2c_new_device().代码如下:

struct i2c_client *

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

{

struct i2c_client   *client;

int         status;

client = kzalloc(sizeof *client, GFP_KERNEL);

if (!client)

return NULL;

client->adapter = adap;

client->dev.platform_data = info->platform_data;

device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);

client->flags = info->flags & ~I2C_CLIENT_WAKE;

client->addr = info->addr;

client->irq = info->irq;

strlcpy(client->name, info->type, sizeof(client->name));

/* a new style driver may be bound to this device when we

* return from this function, or any later moment (e.g. maybe

* hotplugging will load the driver module).  and the device

* refcount model is the standard driver model one.

*/

status = i2c_attach_client(client);

if (status < 0) {

kfree(client);

client = NULL;

}

return client;

}

我们又遇到了一个新的结构:struct i2c_client,不要被这个结构吓倒了,其实它就是一个嵌入struct device的I2C设备的封装.它和我们之前遇到的struct usb_device结构的作用是一样的.

首先,在clinet里保存该设备的相关消息.特别的, client->adapter指向了它所在的adapter.

特别的,clinet->name为info->name.也是指定好了的.

一切初始化完成之后,便会调用i2c_attach_client( ).看这个函数的字面意思,是将clinet关联起来.到底怎么样关联呢?继续往下看:

int i2c_attach_client(struct i2c_client *client)

{

struct i2c_adapter *adapter = client->adapter;

int res = 0;

//初始化client内嵌的dev结构

//父结点为所在的adapter,所在bus为i2c_bus_type

client->dev.parent = &client->adapter->dev;

client->dev.bus = &i2c_bus_type;

//如果client已经指定了driver,将driver和内嵌的dev关联起来

if (client->driver)

client->dev.driver = &client->driver->driver;

//指定了driver, 但不是newstyle的

if (client->driver && !is_newstyle_driver(client->driver)) {

client->dev.release = i2c_client_release;

client->dev.uevent_suppress = 1;

} else

client->dev.release = i2c_client_dev_release;

//clinet->dev的名称

snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),

"%d-%04x", i2c_adapter_id(adapter), client->addr);

//将内嵌的dev注册

res = device_register(&client->dev);

if (res)

goto out_err;

//将clinet链到adapter->clients中

mutex_lock(&adapter->clist_lock);

list_add_tail(&client->list, &adapter->clients);

mutex_unlock(&adapter->clist_lock);

dev_dbg(&adapter->dev, "client [%s] registered with bus id %s ",

client->name, client->dev.bus_id);

//如果adapter->cleinet_reqister存在,就调用它

if (adapter->client_register)  {

if (adapter->client_register(client)) {

dev_dbg(&adapter->dev, "client_register "

"failed for client [%s] at 0x%02x ",

client->name, client->addr);

}

}

return 0;

out_err:

dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "

"(%d) ", client->name, client->addr, res);

return res;

}

参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?怎么没看到它的关系过程呢?

这是因为:在代码中设置了client->dev所在的bus为i2c_bus_type .以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2c driver的时候再来详细分析.

3.2: legacy形式的adapter注册

Legacy形式的adapter注册代码片段如下:

dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,

i2c_do_add_adapter);

这段代码遍历挂在i2c_bus_type上的驱动,然后对每一个驱动和adapter调用i2c_do_add_adapter().

代码如下:

static int i2c_do_add_adapter(struct device_driver *d, void *data)

{

struct i2c_driver *driver = to_i2c_driver(d);

struct i2c_adapter *adap = data;

if (driver->attach_adapter) {

/* We ignore the return code; if it fails, too bad */

driver->attach_adapter(adap);

}

return 0;

}

该函数很简单,就是调用driver的attach_adapter()接口.

到此为止,adapter的注册已经分析完了.

四:i2c driver注册

在分析i2c driver的时候,有必要先分析一下i2c架构的初始化

代码如下:

static int __init i2c_init(void)

{

int retval;

retval = bus_register(&i2c_bus_type);

if (retval)

return retval;

retval = class_register(&i2c_adapter_class);

if (retval)

goto bus_err;

retval = i2c_add_driver(&dummy_driver);

if (retval)

goto class_err;

return 0;

class_err:

class_unregister(&i2c_adapter_class);

bus_err:

bus_unregister(&i2c_bus_type);

return retval;

}

subsys_initcall(i2c_init);

很明显,i2c_init()会在系统初始化的时候被调用.

在i2c_init中,先注册了i2c_bus_type的bus,i2c_adapter_class的class.然后再调用i2c_add_driver()注册了一个i2c driver.

I2c_bus_type结构如下:

static struct bus_type i2c_bus_type = {

.name       = "i2c",

.dev_attrs  = i2c_dev_attrs,

.match      = i2c_device_match,

.uevent     = i2c_device_uevent,

.probe      = i2c_device_probe,

.remove     = i2c_device_remove,

.shutdown   = i2c_device_shutdown,

.suspend    = i2c_device_suspend,

.resume     = i2c_device_resume,

};

这个结构先放在这里吧,以后还会用到里面的信息的.

从上面的初始化函数里也看到了,注册i2c driver的接口为i2c_add_driver().代码如下:

static inline int i2c_add_driver(struct i2c_driver *driver)

{

return i2c_register_driver(THIS_MODULE, driver);

}

继续跟踪:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

int res;

/* new style driver methods can't mix with legacy ones */

//如果是一个newstyle的driver.但又定义了attach_adapter/detach_adapter.非法

if (is_newstyle_driver(driver)) {

if (driver->attach_adapter || driver->detach_adapter

|| driver->detach_client) {

printk(KERN_WARNING

"i2c-core: driver [%s] is confused ",

driver->driver.name);

return -EINVAL;

}

}

/* add the driver to the list of i2c drivers in the driver core */

//关联到i2c_bus_types

driver->driver.owner = owner;

driver->driver.bus = &i2c_bus_type;

/* for new style drivers, when registration returns the driver core

* will have called probe() for all matching-but-unbound devices.

*/

//注册内嵌的driver

res = driver_register(&driver->driver);

if (res)

return res;

mutex_lock(&core_lock);

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

/* legacy drivers scan i2c busses directly */

//遍历所有的adapter,对其都调用driver->attach_adapter

if (driver->attach_adapter) {

struct i2c_adapter *adapter;

down(&i2c_adapter_class.sem);

list_for_each_entry(adapter, &i2c_adapter_class.devices,

dev.node) {

driver->attach_adapter(adapter);

}

up(&i2c_adapter_class.sem);

}

mutex_unlock(&core_lock);

return 0;

}

这里也有两种形式的区分,对于第一种,只需要将内嵌的driver注册就可以了,对于legacy的情况,对每一个adapter都调用driver->attach_adapter().

现在,我们可以将adapter和i2c driver关联起来考虑一下了:

1:如果是news style形式的,在注册adapter的时候,将它上面的i2c 设备转换成了struct client.struct client->dev->bus又指定了和i2c driver同一个bus.因为,它们可以发生probe.

2:如果是legacy形式,就直接找到对应的对象,调用driver->attach_adapter().

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值