writing-client-i2c.txt
时间:2010-04-07 来源:cao5170
建立一个驱动,你必须做几件事,有些是可选的,或有相些是相似的或有些是完全不相同的.这个文档只是一个指引,而不是一个规则手册.
基本注意
========
尽可能保持内核空间干净.最好的办法是为所有的全局符号加上唯一的前缀.这对输出符号来说是特别重要的,同样,那对不需要输出的符号来说也是一个好主意.在这个手册中,我们用'foo_' 为前缀,而 'FOO_' 即是预处理对象的前缀.
驱动结构
========
通常,实现一个单独的驱动结构,而用它来示例所有的从设备.记住,一个驱动结构包含基本的访问程序,同时除了你提供的数据域以外的域你都必需0初始化.一个客户端包含着设备特定的信息,如驱动模型,设备节点和它的I2C总线地址.
static struct i2c_driver foo_driver = { |
名字域是驱动的名称,它不能包括空格,且必须与模块名相一致(如果驱动可以被编译成模块的),然而你可以用MODULE_ALIAS(在这个例中是传递"foo")来为这个模块增加别名.如果驱动名与模块名不一致时,这个模块就不会自动被加载(热插/冷插).
下面解释其它所有的回调函数域
从设备额外数据
==============
任一个客户结构都有一个特定的用来指向任何结构的数据域.你可以用它来保存设备特殊的数据,特别是在处理多I2C总线和多SMBus总线的驱动中.你不一定都需要这个,但是在传感器驱动中,它显得特别有用.
/*保存数值*/ |
访问从设备
==========
假设我们已经有了一个有效的从设备结构.某些时候,我们需要从从设备里收集信息或者把新的信息写到从设备中去.这时候,我们多么希望这个信息对用户空间来说是次要的(或许,对一些抽象的从设备来说,我们根本不需要做这些) 但是,我们需要基本的读写程序.
我发现为它定义foo_read 的 foo_write 函数是有用的.那样更容易地直接调用I2C功能,另外一些芯片上各种各样的寄存器值定义更容易地被读取进来.
下面的函数是一个简单的例子,但是不能逐字复制.
int foo_read_value(struct i2c_client *client, u8 reg) |
探测和捆绑
==========
最初写的Linux I2C栈是用来支持访问PC主板上的控制芯片硬件的.它嵌入了一些假设,这些假设相应SMBus(和计算机)来说,比I2更适当.其中之一的假设是这样的,大多数的适配器和设备驱动支持SMBUS_QUICK协议探测设备的存在.另外一个假设是,只要使用最原始的探测,设备和它们的驱动就可以充分地被设置.
随着Linux和它的I2C栈广泛应用在嵌入式,和出现像DVB适配器这样的复杂组件,这些假设变得更有问题了.I2C设备的驱动引发的中断需要更多(和不同)的配置信息,因为驱动处理不同的芯片,不同的芯片是不能被探测协议所标识的,或者它需要特定主板信息来正确操作它.
因此,现在I2C栈驱动有两种模型来合并I2C设备:最先遗传模型和在Linux 2.6内核完全兼容的设备驱动模型.这些模型没有混合,SMBus类型探测之后,因为遗传模型(legacy) 需要驱动去创建"i2c_client"设备对象,而Linux驱动模型即希望在probe()函数里,驱动能给这样的一个设备对象.
标准驱动模型绑定("New Style")
-------------------------------
基本系统,典型板特定初始化代码或者启动固件来报告存在的I2C设备. 例如,或许有一个表在内核或者启动器里来表示I2C设备和把它们连接到特定主板上的配置信息,像中断,其它写入设备,芯片类型等等.它可以用来为每一个I2C设备创建i2c_client 对象.
I2C设备驱动这种绑定模型像其它类型的Linux驱动一样工作: 它们提供probe()来绑定设备,rmove()来解除绑定.
|
记住 i2c_driver 没有创建这些从设备句柄. 句柄在foo_probe()中可能被使用.如果foo_probe()报告成功的(0而不是一个负的状态值) 它可以保存和使用这个句柄直到foo_remove()返回为止.这种绑定被大多数Linux驱动所使用.
当i2c_client.driver_name和驱动的名称相同时,驱动比较设备;这种相近的办法也被用在另外总线中,那是在硬件中没有设备类型支持的. 驱动与模块的名称必须相配,所以热插/冷插都会(模块探测)modprobe 驱动.
创建设备(标准驱动模型)
----------------------
如果你知道一个I2C设备是连接在一个已经给定的I2C总线上的这个事实,你就可以通过用设备地址,设备名称和调用i2c_new_device()简单地填充i2c_board_info结构来表示设备.它创建设备,并在此时驱动的核心将维护查找适当的驱动,同时调用它的probe().如果一个驱动支持不同的设备类型,你可以指定你想要用的类型域的类型.如果必要,你也可以指定中断号的平台数据.
有时候,你知道一个设备是连接到一个给定的I2C总线,但是你不知它使用的确定地址.TV适配器就是一个例子,一个驱动可以支持几十个稍有不同的模块,而I2C设备之间的地址都不相同.在这种情况下,你可以使用i2c_new_probed_device()的变体,除了它在后面加一个可能的I2C地址来探测以外,它是和 i2c_new_device()相似的.在列表中第一个响应的地址的设备被创建.如果你希望在一定的地址范围内表示多于一个设备,简单的做法就是多次调用i2c_new_probed_device().
i2c_new_device()或i2c_new_probed_device()调用典型发生在I2C总线驱动中,或许你想保存i2c_client()的返回引用以便将来使用.
设备删除(标准设备模型)
---------------------
每个用i2c_new_device()或者i2c_new_probed_device()创建的I2c设备都可以通过调用 i2c_unregister_device()来注销.如果你不确切地调用它,它在I2C总线移除之前自动地调用,因为在设备驱动模型中,一个设备不能比它的父类活得更长.
遗传的驱动绑定模型
--------------------
多数的i2c设备可以通过几个I2C地址来表现;一些是依靠硬件来实现的(通过焊接一些芯片的引脚到VCC或地).另外一些也可以通过软件来改变(通过写设备特定的寄存器).一些设备通常有一个特定的地址,但不是全是;还有一些是智能的,所以你需要为你的从设备扫描几个i2c地址,和做一些排序来决定在你的驱动中,它是否实际支持你的设备的.
为了提供用户一个最大限度的可能性,定义了一些缺省的模型参数来决定什么地址被扫描.在i2c.h 定义了一个宏来帮助你实现那样的功能,像一些基本的探测算法.
你不必要一定使用这个参数接口,如果你不这样做,请尽量不要用 i2c_probe()这样的函数.
探测类(遗传模型)
----------------
所有的参数都是以无符号16位整数列表给出.这个列表是以I2C_CLIENT_END为结束的.
下面的列表是被用在内部的:
normal_i2c:由模块写者来填充.
一个I2C地址列表必须被常规地检查.
探测: 插入模块参数.
两个参数列表.第一个参数是总线号(任何I2C总线都是 -1),
第二个参数是I2C地址,这些地址值如果在常规的列表中也会被探测.
忽略: 插入模块参数
两个参数列表.第一个参数是总线号(任何I2C总线都是 -1).
第二个参数是I2C地址,这些地址值永远不会被探测.
这个参数只管理 'normal_i2c'列表.
强制: 插入模块参数
两个参数列表.第一个参数是总线号(任何I2C总线都是 -1).
第二个参数是I2C地址,设备是盲目地被假设在给定的地址上的,不做探测.
另外,如果驱动支持几种类型的芯片,各种强制的列表可能会被选择地定义,它们会被编组在一个命名为forces的NULL_terminated列表指针中.这些指针是上面提及到基本的force列表中的第一个元素.每个额外增加的列表对应一个以force_<kind>插入的模块参数.
幸运的是,作为一个模块编写者,你只要定义'normal_i2c'这个参数就可以.完整的定义可能像以下这样:
|
如果你使用多种形式,你可以定义一个枚举:
enum chips {any_chip, foo, bar, ...}
那时你就可以(准确地)在你的驱动代码中使用它.
注意你必须调用定义了没有任务前缀的变量 'normal_i2c'.
归属到一个适配器(遗传模型)
--------------------------
无论什么时候一个新的适配器插入或者驱动为所有的适配器注册,都会调用attach_adapter()这个回调函数.之后是探测什么样的设备存在在适配器中,且各自为他们注册一个客户端.
回调函数attach_adapter是非常容易的: 我们调用基本的探测函数.这个函数用上面已经解释过的那些定义过的信息列表为我们扫描总线.如果在一个特定地址上探测到设备,另一个回调函数将会被调用.
int foo_attach_adapter(struct i2c_adapter *adapter) |
记住, 'addr_data' 是上面已经解释过的宏定义,所以不必你自己定义它.
i2c_probe函数会为那些i2c地址有实际设备存在的i2c地址调用foo_detect_client函数(除非用了一个'force'的参数). 另外,那些已经使用了的地址(被另外一些从设备注册)将会被跳过.
探测从设备函数(遗传模型)
------------------------
探测从设备函数被i2c_probe()函数调用.如果参数'kind'是-1即是试探性的探测,如果是0即为强制性的探测,如果是一正值即是带有芯片类型的强制性探测.
返回不同-ENODEV的错误值将会引起探测函数停止探测:另外一些地址和适配器不再被扫描.只有发生重大或内部错误时才能这样做,像内存或者i2c_attach_client失效时.
现在,你可以忽略 'flags'这个参数,它是在将来要使用的.
int foo_detect_client(struct i2c_adapter *adapter, |
移除设备(遗传模型)
==================
移除一个设备时必须调用detach_client的回调函数.这个调用只有在蹦溃的时候才会失效.幸运的是,这部分代码比增加设备要简单得多.
int foo_detach_client(struct i2c_client *client) |
初始化模块或者内核
===================
当你的内核启动或者你的驱动模块被插入时,你必需做初始化的工作.幸运的是,我们通常只要附加(注册)这个驱动模块就已经足够.
static int __init foo_init(void) |
注意,标有 '__init'的函数和标有 '__initdata'的数据结构在内核启动完成或都模块加载完成后是可以移除的.
电源管理
========
系统进入低电状态时(像使收发机处于低电模式或者唤醒系统的机理),你的I2C设备需要特殊的处理时,可以用suspend()来使系统挂起,而resume()即是做suspend()相反的动作.
有几个标准的驱动模型调用,它们像为其它为其它驱动栈一样工作,这些调用可以睡眠,也可以用I2C消息来通知处于挂起和恢复状态的设备(这些调用能发生,是因为它的父I2C适配器处于活动状态,且中断是开启的).
系统关闭
========
系统关闭或者重启(包括kexec) 你的I2C设备需要一些特殊的处理时--像关闭一些东西--可以使用shutdown()的办法.
再有,这是一个标准的驱动模型调用,它像为其它驱动栈工作一样工作:这些调用可以睡眠和使用I2C消息.
命令函数
========
能支持一个基本的类ioctl回调函数,但你很少使用到它,另外也不赞成使用,所以在新的设计中不再使用它.设置它为NULL。
发送和接收
==========
如果你想与你的i2c设备进行通信,有几可函数可以使用.你可以在i2c.h头文件中找到他们.
如果你可以在无格式的i2c通信和SMBus级通信这两者之间选择的话,请使用最后一个.所用的适配器都可以理解SMBus级别的命令,但只有一些是可以理解无格式i2c的通信.
无格式的i2c通信
----------------
|
|
这个用来发送一系列的消息,每一个消息可以用来读也可以用来写,读写也可以以另外一种方式来混合.传送连接在一起:在各个传送动作之间没有停止位.i2c_msg结构包含了每一个消息要发送的从设备地址.字节数即为消息数据本身.
你可以读 'i2c-protocol'文档来获取实际i2c协议的更多信息.
SMBus 通信
------------
|
这是SMBus的基本函数,下面的函数必须以一组的形式来执行,不要直接使用这个函数.
|
其中一些在Linux 2.6.10 内核中已被删除,因没有人使用它们,但是,如果需要,可以在外面把它加回来.
extern s32 i2c_smbus_read_block_data(struct i2c_client * client, |
你可以读 'smbus-protocol'文档来获取实际SMBus协议的更多信息.
基本目的程序
=============
下面列出的所有基本目的程序在之前是没有提及的.
/*这个调用为每一个注册了的适配器返回唯一的一个低符号标识*/
extern int i2c_adapter_id(struct i2c_adapter *adap);