linux I2C子系统构架(2)

在linux内核中I2C构架中主要元素的相互作用框图如下,用户程序可以通过struct i2c_driver 和struct i2c-dev提供的接口操作具体的i2c从设备即(struct i2_client)。上面两种用户操作设备的方法,最终都是通过 i2c-core交互,i2c-core 通过I2C总线驱动(struct i2c_adapter)提供的具体操作方法(struct i2c_algorithm)来操作具体的i2c总线。下面对各个重要对象做简要介绍。

1)在linux内核中每天总线对应一个struct i2c_adapter,且每条总线对应一个与硬件无关的编号,在内核中struct i2c_adapter就是一条i2c的总线驱动。在内核的i2c-core.c中定义了两个全局变量 core_lock 和i2c_adatpter_idr,前者是是保护后者及其他相关i2c临界资源的一个互斥锁,后者是一个struct idr结构,注册在内核中的struct i2c_adapter都保存在i2c_adatpter_idr结构中,其关系是用总线编号映射具体的总线驱动。下面对idr的机制做简要说明。

idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制,在内核的很多地方都可以找到idr的身影。idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。举个例子,在I2C总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。如果我们的PC是一个I2C总线上的主节点,那么要访问总线上的其他设备,首先要知道他们的ID号,同时要在pc的驱动程序中建立一个用于描述该设备的结构体。

此时,问题来了,我们怎么才能将这个设备的ID号和他的设备结构体联系起来呢?最简单的方法当然是通过数组进行索引,但如果ID号的范围很大(比如32位的ID号),则用数组索引显然不可能;第二种方法是用链表,但如果网络中实际存在的设备较多,则链表的查询效率会很低。遇到这种清况,我们就可以采用idr机制,该机制内部采用radix树实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。

① 获得idr

要在代码中使用idr,首先要包括<linux/idr.h>。接下来,我们要在代码中分配idr结构体,并初始化:

void idr_init(struct idr *idp);

其中idr定义如下:

struct idr {

     struct idr_layer *top;

     struct idr_layer *id_free;

     int         layers;

     int         id_free_cnt;

     spinlock_t      lock;

};

/* idr是idr机制的核心结构体 */

② 为idr分配内存

int idr_pre_get(struct idr *idp, unsigned int gfp_mask);

每次通过idr获得ID号之前,需要先分配内存。

返回0表示错误,非零值代表正常

③ 分配ID号并将ID号和指针关联

int idr_get_new(struct idr *idp, void *ptr, int *id);

int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);

idp: 之前通过idr_init初始化的idr指针

id:  由内核自动分配的ID号

ptr: 和ID号相关联的指针

start_id: 起始ID号。内核在分配ID号时,会从start_id开始。如果为I2C节点分配ID号,可以将设备地址作为start_id

函数调用正常返回0,如果没有ID可以分配,则返回-ENOSPC

在实际中,上述函数常常采用如下方式使用:

again:

  if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) {

/* No memory, give up entirely */

  }

  spin_lock(&my_lock);

  result = idr_get_new(&my_idr, &target, &id);

  if (result == -EAGAIN) {

sigh();

spin_unlock(&my_lock);

goto again;

  }

④ 通过ID号搜索对应的指针

void *idr_find(struct idr *idp, int id);

返回值是和给定id相关联的指针,如果没有,则返回NULL

⑤ 删除ID

要删除一个ID,使用:

void idr_remove(struct idr *idp, int id);

2)struct i2c_algorthim 代表的是一条总线底层的具体操作方法,每条总线中都有一个algo指针指向其具体的操作方法,而每种struct i2c_algorthim在内核中提供了一种指定操作方式的总线驱动注册方式,一般给出的形式如:i2c_xxxx_add_bus和i2c_xxxx_add_numbered_bus。因此在注册自己的总线驱动时可以使用特定i2c_algorthim的接口。

3)Struct i2c_driver 在内核中表示设备的驱动程序,有新旧两种风格,在以后的内核中渐渐都使用新风格方式,与现有的总线风格相配匹。Struct i2c_driver是完全独立于struct i2c_adapter,同一个struct i2c_driver可以豪不修改的使用于同一板子上的不同struct i2c_adapter上。

4)在linux 内核中struct i2c_client表示一个具体的设备,在linux内核中具体设备的加入有两种方法,有一种soc上设备集成在设备上,通过i2c_boardinfo.c提供的方式加入,另外一种就是我们普通的总线的方式加入。

5)Linux 内核中还有个struct i2c-dev 这个是为用户空间操作适配器(struct i2c_adapter)提供了一种方式,为用户空间提供了一种通用的操作I2C设备的方法。每个适配器对应一个字符设备,内核中用i2c_dev_list来集中管理这种设备,其中i2c_dev_list_lock是保护设备链表的自旋锁。用户空间可以通过/dev/i2c-n访问编号为n的主机适配器通过制ioctl去操作对应的slave设备。

6)整个I2C子系统在linux/drivers/i2c目录下,首先i2c-core.c是i2c核心的代码提供了各种管理其他结构的主要结、i2c_dev.c中实现用户空间操作i2c设备的通用方法、i2c-boardinfo.c为片上i2c设备的管理提供一些操作接口。Algos目录下面是各种struct i2c_algorthim的实现、busses是各种适配器驱动(struct i2c_adapter)的实现、chips是各种设备驱动的实现即(struct i2c_driver).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值