分类: LINUX
2014-02-11 14:50:09
使用主机Linux作为host/master
要建立一个驱动程序,您需要做几件事情。有些是可选的,
有些事情可以做小,甚至完全不同。作为本
指南,而不是作为一个规则的书!
一般意见
===============
尽量保持清洁,尽量为内核的命名空间。最好的办法
这样做是用于所有的全局符号独特的前缀。这是
尤其重要的是导出的符号,但它是一个好主意
它非导出的符号了。我们将利用这个前缀`foo_'
教程。
驱动程序的结构
====================
通常,您将实现一个单一的驱动结构,并以此实现其他client
记住,一个驱动程序的结构包含一般的访问
例程和应为零除了与数据字段初始化你
提供。client端结构保存特定于设备的信息,如
模型设备驱动程序节点,和I2C地址。
static i2c_device_id foo_idtable [] = {
{“fool”,my_id_for_foo},
{“bar”,my_id_for_bar},
{设备驱动程序节点,I2C地址}
{}
};
MODULE_DEVICE_TABLE(i2c,foo_idtable);
static struct i2c_driver foo_driver = {
.driver = {
.name = "foo",
},
.id_table = foo_ids,
.probe = foo_probe,
.remove = foo_remove,
/* if device autodetection is needed: */
.class = I2C_CLASS_SOMETHING,
.detect = foo_detect,
.address_data = &addr_data,
.shutdown = foo_shutdown, /* optional */
.suspend = foo_suspend, /* optional */
.resume = foo_resume, /* optional */
.command = foo_command, /* optional, deprecated */
}
名称字段是驱动程序的名称,不得包含空格。这
应匹配模块的名称(如果该驱动程序可以作为一个模块编译),
虽然您可以使用MODULE_ALIAS(通过在这个例子中为“foo”),以添加
另一个模块的名字。如果驱动程序的名称不匹配的模块
名称,模块将不会被自动加载(热插拔/ coldplug)。
所有其他字段用于回叫函数将解释
下文。
client数据
=================
每个client结构有一个特殊的'数据'字段,可以指向任何
在所有结构。你应该用它来保持设备的具体数据。
/ *存储值* /
无效i2c_set_clientdata(结构i2c_client *client,无效*数据);
/ *取得值* /
无效* i2c_get_clientdata(const结构i2c_client *client端);
访问client端
====================
假设我们有一个有效的client结构。在某些时候,我们需要
从client端收集的信息,或写入新的信息
client端。
我发现它很有用的定义foo_read和foo_write此功能。
在某些情况下,这将是容易的I2C功能直接调用,
但许多芯片有一些寄存器一种价值理念,可以轻松
被封装。
以下函数的简单的例子,不应该被复制
从字面上。
int foo_read_value(struct i2c_client *client, u8 reg)
{
if (reg < 0x10) /* byte-sized register */
return i2c_smbus_read_byte_data(client, reg);
else /* word-sized register */
return i2c_smbus_read_word_data(client, reg);
}
int foo_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == 0x10) /* Impossible to write - driver error! */
return -EINVAL;
else if (reg < 0x10) /* byte-sized register */
return i2c_smbus_write_byte_data(client, reg, value);
else /* word-sized register */
return i2c_smbus_write_word_data(client, reg, value);
}
Probing and attaching
=====================
堆栈的Linux的I2C最初是访问硬件支持
监测PC主板芯片,从而用来嵌入一些假设
这更适合于到I2C的SMBus(和PC)。其中一个
假设是,大多数适配器和设备驱动程序支持SMBUS_QUICK
协议探测设备是否存在。一种说法是,设备及其驱动程序
可以充分配置只使用这种探头基元。
随着Linux及其I2C堆栈变得更加广泛地使用在嵌入式系统
如DVB适配器和复杂的组件,这些假设变得更加
有问题的。 I2C设备的驱动程序的中断问题需要更多(和
不同的)配置信息,就如同司机处理芯片的变种
不能按协议杰出的探测,或需要一些电路板
具体信息能够正常运行。
因此,现在的I2C栈I2C设备相关联两种模式
他们的驱动程序:原有的“legacy”的模式,以及一个新的
完全兼容的Linux 2.6驱动程序模型。这些模型不混合,
因为“legacy”模式要求司机创造“i2c_client”装置
SMBus的样式对象后,探测,而Linux驱动程序模型预测
司机在他们的调查得到这样的设备对象()程序。
旧的模式已经过时了,很快被删除,所以我们没有
较长的文件在这里。
标准驱动程序模型绑定(“新风格”)
-------------------------------------------
系统基础设施,通常电路板或特定的初始化代码
引导固件,报告哪些I2C设备存在。例如,可能有
在内核或引导程序表,确定I2C设备
并将其连接到电路板的具体有关的IRQ配置信息
文物和其他布线,芯片类型,等等。这可能被用来
为每个I2C器件i2c_client对象。
I2C设备驱动程序使用,就像任何其他这种结合的典范作品
种驱动程序在Linux:他们提供了一个probe()方法绑定到
这些设备,并remove()方法来解除绑定。
static int foo_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int foo_remove(struct i2c_client *client);
请记住,i2c_driver不会产生这些client端处理。该
可用于处理在foo_probe()。如果foo_probe()报告成功
(零不是消极的状态代码)它保存这些,直到
foo_remove()返回。这种方式在大多数Linux驱动程序使用。
探头函数被调用时,在名称字段输入id_table
相匹配的设备的名称。它是通过使被匹配的
驱动知道哪一个匹配表中的一个。
设备创建
---------------
如果你知道一个一I2C设备连接到一个给定的I2C总线,
你可以通过简单的实例化该设备填补了i2c_board_info
结构与设备驱动程序的名称和地址,并调用
i2c_new_device()。这将创建的设备,然后将驱动核心
要找到正确的驱动程序将调用它的probe()方法。
如果驱动程序支持不同的设备类型,您可以指定类型
要使用类型字段。您也可以指定一个IRQ和平台数据
如果需要的话。
有时候你知道,一个设备连接到一个给定的I2C总线,但是你
不知道确切的地址,它使用。发生这种情况对电视适配器
例如,在相同的驱动程序支持几十略有不同
模型和I2C设备地址从一个模式转变到下一个。在
这种情况下,你可以使用i2c_new_probed_device()的变种,这是
类似i2c_new_device(),但它需要一个额外的list
的设备以便probe。创建一个设备是第一
响应地址在列表中。如果你希望多个器件进行
目前在地址范围,只需使用i2c_new_probed_device()的
很多次。
调用i2c_new_device()或i2c_new_probed_device()通常发生
在I2C总线驱动程序。你可能想保存返回i2c_client
引用后使用。
设备检测
----------------
有时候,你事先不知道其中的I2C设备连接到
一个给定的I2C总线。这是硬件监控的情况,例如
PC机上设备的SMBus的。在这种情况下,您可能需要让您的驱动程序
支持的设备会自动检测。这是怎样的传统模式
在工作,现在是作为对现有标准的扩展
驱动程序模型(所以我们终于可以摆脱了传统模式。)
你只需要定义一个检测回调将尝试
确定支持的设备(返回为支持的0和- ENODEV
对于那些不支持)是探测地址的列表,以及设备类型
(或类),以便I2C总线只可能有这种设备的类型
连接(而不是别的列举)将被进行了探讨。例如,
一家硬件监控芯片驱动的自动检测
需要将其设置类I2C_CLASS_HWMON,只有的I2C适配器
与一类包括I2C_CLASS_HWMON将探讨由该驱动程序。
请注意,如果没有匹配的类并不妨碍使用
一组在给定的I2C适配器类型的设备。它可以防止所有的
自动检测;明确实体的设备仍然是可能的。
请注意,这纯粹是可选的机制和所有不适合
设备。你需要一些可靠的方法来确定支持的设备
(通常使用特定设备,专用标识寄存器),
否则misdetections都可能发生任何事情都出错
很快。请记住,I2C协议不包括任何
标准的方法来检测一个给定的地址在一个芯片的存在,让
仅一个标准的方法来识别设备。更糟糕的是缺乏
总线传输相关的语义,这意味着相同的
传输可以被看作是由芯片读操作和写
操作由另一个芯片。基于这些原因,明确设备
实例应该总是倾向于自动检测哪里
可能。
装置删除
---------------
每个I2C设备已创建使用i2c_new_device()或
i2c_new_probed_device()可以通过调用未注册
i2c_unregister_device()。如果你不调用,将在i2c总线删除时自动调用
驱动程序初始化
=======================
当内核引导时,或当您的fool驱动模块插入
你必须做一些初始化。幸运的是,刚刚登记
驱动模块通常是不够的。
static int __init foo_init(void)
{
return i2c_add_driver(&foo_driver);
}
static void __exit foo_cleanup(void)
{
i2c_del_driver(&foo_driver);
}
/* Substitute your own name and email address */
MODULE_AUTHOR("Frodo Looijaard <>"
MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices");
/* a few non-GPL license types are also allowed */
MODULE_LICENSE("GPL");
module_init(foo_init);
module_exit(foo_cleanup);
请注意,有些`__init'标记。这些在kernel启动后删除
同样,职能由`__exit标记'被编译进kernel后被编译器丢弃
因为不会被调用
电源管理
================
如果您需要特殊处理I2C器件时,系统进入低
电源状态 - 就像把进入低功耗模式收发器,或
激活一个系统唤醒机制 - 使用suspend()方法。
resume()方法于suspend()方法相反。
这些都是标准的驱动程序模型调用
。调用睡眠,并且可以使用
该设备的I2C通讯被暂停或恢复(因为他们
父母的I2C适配器处于活动状态时发出上述呼吁,和IRQ
仍启用)。
系统关闭
===============
如果您需要特殊处理I2C器件时,系统关机
或重新启动(包括的kexec)使用shutdown()方法。
再次,这是一个标准的驱动程序模型调用,就像它的工作
将任何其他驱动程序堆栈:调用可以睡眠,并能使用
的I2C通讯。
命令功能
================
一个普通的ioctl样函数回调支持。你很少会
需要这个,反正它的使用已过时,因此,新的设计不应该
使用它。
发送和接收
=====================
如果你想与您进行沟通的设备,有几个功能
要做到这一点。你可以在
如果你可以选择普通的I2C和SMBus通信,请使用后者。
所有适配器了解SMBus的水平
命令,但其中只有一些明白I2C!
I2C通信
-----------------------
int i2c_master_send(struct i2c_client *client, const char *buf,
int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);
这些例程读取和写入/向client端一些字节。client端
包含I2C地址,所以你不必把它列入。第二
参数包含的字节读/写,第三的字节数
读/写(必须大于缓冲区的长度。)返回
实际数目的字节读/写的。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num);
这发出了一系列消息。每条消息可以是读或写,
他们可以以任何方式进行混合。相结合的trans:无
发送停止位之间的trans。该结构包含i2c_msg
每个邮件client端的地址,邮件的字节数
和消息数据本身。
你可以阅读文件'i2c-protocol'来获取更多有关信息
实际I2C协议。
SMBus通信
-------------------
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data);
这是通用SMBus的功能。
下面是实现的所有功能。切勿直接使用此功能!
s32 i2c_smbus_read_byte(struct i2c_client *client);
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client *client,
u8 command, u8 value);
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(struct i2c_client *client,
u8 command, u16 value);
s32 i2c_smbus_process_call(struct i2c_client *client,
u8 command, u16 value);
s32 i2c_smbus_read_block_data(struct i2c_client *client,
u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_client *client,
u8 command, u8 length, const u8 *values);
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client,
u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,
u8 command, u8 length,
const u8 *values);
这些被免职的I2C的核心,因为他们没有用户,但可以
重新添加后,如果需要的:
s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value);
s32 i2c_smbus_block_process_call(struct i2c_client *client,
u8 command, u8 length, u8 *values);
所有这些trans失败时返回一个负errno值。
在'写'trans成功返回0;的'读'trans返回read value,
除了block trans,返回读的数目
块缓冲区不需要超过32个字节。
你可以阅读文件'smbus-protocol' 来获取更多有关信息
实际SMBus协议。
通用例程
========================
以下所有的通用例程上市,即没有提到
前。
/* Return the adapter number for a specific adapter */
int i2c_adapter_id(struct i2c_adapter *adap);