下载Linux内核的网址:https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/
linux里面的i2c总线分为i2c总线驱动层,i2c核心层,i2c设备驱动层。核心层提供了注册函数接口以及相关的操作函数。
编写一个设备的驱动,需要关注的是设备驱动层的内容,涉及到核心层提供函数的使用以及分析。
挂接在i2c总线上的设备在内核中用一个i2c_client结构体表示,构造这个结构体并且注册进入总线的设备链表,实现设备与驱动匹配。匹配过程是调用总线的函数进行,总线匹配结束后就会调用驱动的probe函数。
其实,添加i2c设备的方法很灵活。根据Linux的官方文档《linux-3.4.2\Documentation\i2c\instantiating-devices》,添加i2c设备的方法总结有4种:
为了实现一个描述具体设备的i2c_client结构体,内核提供了4中方法。包括静态声明方式和动态添加方式。
1、静态声明方式
静态声明是在总线驱动(也就是一个adapter驱动,adapter适配器描述一个i2c控制器,表示总线的一种操作方法)注册之前完成,在__i2c_board_list链表上面放入struct i2c_board_info描述的一个设备。适配器注册后,就会扫描这个链表,通过总线号来判断是否要将里面的设备进行实例化和注册。
struct i2c_board_info pyl_info={
I2C_BOARD_INFO("i2c_pyl",0x50),
};
/**
* i2c_register_board_info - statically declare I2C devices
* @busnum: identifies the bus to which these devices belong
* @info: vector of i2c device descriptors
* @len: how many descriptors in the vector; may be zero to reserve
* the specified bus number.
*
* Systems using the Linux I2C driver stack can declare tables of board info
* while they initialize. This should be done in board-specific init code
* near arch_initcall() time, or equivalent, before any I2C adapter driver is
* registered. For example, mainboard init code could define several devices,
* as could the init code for each daughtercard in a board stack.
*
* The I2C devices will be created later, after the adapter for the relevant
* bus has been registered. After that moment, standard driver model tools
* are used to bind "new style" I2C drivers to the devices. The bus number
* for any device declared using this routine is not available for dynamic
* allocation.
*
* The board info passed can safely be __initdata, but be careful of embedded
* pointers (for platform_data, functions, etc) since that won't be copied.
*/
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!\n");
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;
}
i2c_register_board_info实现了在哪个busnum上面挂载设备,在i2c_add_adapter注册过程中,会调用到扫描i2c_board_info的函数i2c_scan_static_board_info
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
i2c_scan_static_board_info
——i2c_new_device
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))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
最总还是调用了i2c_new_device函数,这个函数是创建设备的最重要的函数,其实现了设备的实例化和注册的过程。
相应的设备驱动程序需要使用id_table进行名字的匹配,注意这里只是进行id_table里面的名字进行匹配,不进行device_driver里面的名字匹配。
初始化一个i2c_driver结构体
static struct i2c_driver pyl_drv={
.id=1,
.driver={
.owner=THIS_MODULE,
.name="hello",
},
.probe=pyl_i2c_probe,
.remove=pyl_i2c_remove,
.id_table=id_table,
};
struct i2c_device_id id_table[]={
[0]={
.name="i2c_pyl",
.driver_data=0,//私有数据
},
[1]={
.name="i2c_pyl1",
.driver_data=1,//私有数据
},
[2]={
.name="i2c_pyl2",
.driver_data=2,//私有数据
},
};
probe在匹配到设备(或者设备匹配驱动)后就调用。
最后是注册驱动
static int __init pyl_i2c_drv_init(void){
i2c_add_driver(&pyl_drv);
return 0;
}
static void __exit pyl_i2c_drv_exit(void){
i2c_del_driver(&pyl_drv);
}
2、动态添加方式
动态添加就是直接使用i2c_new_device函数进行一个设备的创建和注册。
/**
* i2c_new_device - instantiate an i2c device for use with a new style driver
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*
* Create a device to work with a new style i2c driver, where binding is
* handled through driver model probe()/remove() methods. This call is not
* appropriate for use by mainboad initialization logic, which usually runs
* during an arch_initcall() long before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or NULL to indicate an error.
*/
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;
//info->platform_data;私有数据给了client->dev.platform_data ,可以在i2c_driver中获得来使用
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
//进行赋值
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
//拷贝info的名字给client,这样驱动就可以利用这个名字与之匹配
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.
*/
//注册client
status = i2c_attach_client(client);
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}
/**
* i2c_unregister_device - reverse effect of i2c_new_device()
* @client: value returned from i2c_new_device()
* Context: can sleep
*/
void i2c_unregister_device(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
struct i2c_driver *driver = client->driver;
if (driver && !is_newstyle_driver(driver)) {
dev_err(&client->dev, "can't unregister devices "
"with legacy drivers\n");
WARN_ON(1);
return;
}
if (adapter->client_unregister) {
if (adapter->client_unregister(client)) {
dev_warn(&client->dev,
"client_unregister [%s] failed\n",
client->name);
}
}
mutex_lock(&adapter->clist_lock);
list_del(&client->list);
mutex_unlock(&adapter->clist_lock);
device_unregister(&client->dev);
}
实现的例子:
/*显示增加i2c_device,还是依靠先知adapter总线nr*/
/*1、使用i2c_new_device*/
/*2、使用i2c_new_probed_device*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/string.h>
static struct i2c_client* pyl_i2c_client=NULL;
struct i2c_board_info pyl_info={
I2C_BOARD_INFO("i2c_pyl",0x50),
};
static int __init pyl_i2c_dev_init(void){
struct i2c_adapter * adap;
adap=i2c_get_adapter(0);
pyl_i2c_client=i2c_new_device(adap,&pyl_info);
i2c_put_adapter(adap);
if(pyl_i2c_client)
printk(KERN_INFO"\n new OK \n");
return 0;
}
static void __exit pyl_i2c_dev_exit(void){
i2c_unregister_device(pyl_i2c_client);
}
MODULE_LICENSE("GPL");
module_init(pyl_i2c_dev_init);
module_exit(pyl_i2c_dev_exit);
在实现这个方法的过程中,i2c_new_device(adap,&pyl_info);中需要adapter,这个adapter是已经注册了的适配器,目的就是将设备与这个适配器联系起来,核心层提供了获取指定线路适配器的函数 i2c_get_adapter,使用结束后调用i2c_put_adapter还原。
//获取adapter指针
struct i2c_adapter* i2c_get_adapter(int id)
{
struct i2c_adapter *adapter;
mutex_lock(&core_lock);
adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
if (adapter && !try_module_get(adapter->owner))
adapter = NULL;
mutex_unlock(&core_lock);
return adapter;
}
EXPORT_SYMBOL(i2c_get_adapter);
//把获取的adapter去除
void i2c_put_adapter(struct i2c_adapter *adap)
{
module_put(adap->owner);
}
EXPORT_SYMBOL(i2c_put_adapter);
i2c_new_device返回一个已经创建好的i2c_client结构体,并且在这个函数里面实现了注册。
i2c_unregister_device(pyl_i2c_client);传入一个刚才注册的设备结构体指针,实现设备从总线上的卸载。
在i2c核心层提供的API接口中,有关于结构体获取的一些函数,需要是先去那里查找相关接口,如果自己直接赋值的话,会发生一些错误,并且有些查找函数用到了全局链表等的一些内容,需要大量的工作,核心层提供的函数接口简化了这些工作。
i2c_new_device这种方式是默认adapter上存在设备,直接为设备创建一个描述结构体并注册就可以。
i2c_new_probed_device与i2c_new_device过程类似,可是,在这之前还要加上probe,也就是探测是否真的存在addr_list所描述的设备,info中的type在声明变量时传入,addr是在探测到(也就是收到ACK回应)设备存在后,由addr_list[i]赋值,这也说明了i2c_new_probed_device函数中有一个for循环对地址链表进行遍历查找(不过,当有一个地址找到时,后面的地址就不找了,说说明addr_list里面的地址是对于一个设备而言,该设备可能存在不同地址,不能确定是具体哪一个,所以就用这个地址列表里面的地址逐个探测)。探测过程就是发送一个信号到指定地址设备,看有没有回应,回应则表示找到,不再进行后面地址的判断了。
struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
unsigned short const *addr_list)
{
int i;
/* Stop here if the bus doesn't support probing */
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) {
dev_err(&adap->dev, "Probing not supported\n");
return NULL;
}
for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
/* Check address validity */
if (addr_list[i] < 0x03 || addr_list[i] > 0x77) {
dev_warn(&adap->dev, "Invalid 7-bit address "
"0x%02x\n", addr_list[i]);
continue;
}
/* Check address availability */
if (i2c_check_addr(adap, addr_list[i])) {
dev_dbg(&adap->dev, "Address 0x%02x already in "
"use, not probing\n", addr_list[i]);
continue;
}
/* Test address responsiveness
The default probe method is a quick write, but it is known
to corrupt the 24RF08 EEPROMs due to a state machine bug,
and could also irreversibly write-protect some EEPROMs, so
for address ranges 0x30-0x37 and 0x50-0x5f, we use a byte
read instead. Also, some bus drivers don't implement
quick write, so we fallback to a byte read it that case
too. */
if ((addr_list[i] & ~0x07) == 0x30
|| (addr_list[i] & ~0x0f) == 0x50
|| !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) {
union i2c_smbus_data data;
if (i2c_smbus_xfer(adap, addr_list[i], 0,
I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE, &data) >= 0)
break;
} else {
if (i2c_smbus_xfer(adap, addr_list[i], 0,
I2C_SMBUS_WRITE, 0,
I2C_SMBUS_QUICK, NULL) >= 0)
break;
}
}
if (addr_list[i] == I2C_CLIENT_END) {
dev_dbg(&adap->dev, "Probing failed, no device found\n");
return NULL;
}
info->addr = addr_list[i];
return i2c_new_device(adap, info);
}
EXPORT_SYMBOL_GPL(i2c_new_probed_device);
设备的驱动与上面的一样。
上面的方法都是设备与驱动分离,还有一种方法是在注册驱动时,程序找出总线上所有的adapter,在这个adapter上进行地址检测,存在就会调用i2c_driver的detect函数和i2c_new_device(adap, info);
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/string.h>
/*1、设备和驱动的注册分离*/
/*2、在驱动注册过程中实现设备的探测和注册 <id_table只在两者分离的情况下使用>
* 实现步骤:
1、class指定
2、指定地址列表
*/
static int pyl_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id){
printk(KERN_INFO"\n probe client OK type=%s addr=%d \n",client->name,client->addr);
return 0;
}
static int pyl_i2c_remove(struct i2c_client *client){
printk(KERN_INFO"\n remove OK \n");
return 0;
}
static int pyl_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info){
//if(client->addr==0x50)
strncpy(info->type,"i2c_pyl",I2C_NAME_SIZE);
printk(KERN_INFO"\n detect OK \n");
return 0;
}
struct i2c_device_id id_table[]={
[0]={
.name="i2c_pyl",
.driver_data=0,//私有数据
},
[1]={
.name="i2c_pyl1",
.driver_data=1,//私有数据
},
[2]={
.name="i2c_pyl2",
.driver_data=2,//私有数据
},
};
static const unsigned short normal_i2c[]={0x51,0x50,I2C_CLIENT_END};
static const unsigned short normal_i2c1[]={0x51,0x50,I2C_CLIENT_END};
static const unsigned short normal_i2c2[]={I2C_CLIENT_END};
static const struct i2c_client_address_data pyl_address_data={
.normal_i2c=normal_i2c,
.probe=normal_i2c1,
.ignore=normal_i2c2,
.forces=NULL,
};
/*
kernel/drivers/i2c/busses/i2c-s3c2410.c:
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
ret = -EAGAIN;
goto out;
}
出错内容-----超时导致
*/
static struct i2c_driver pyl_drv={
.id=1,
.class=I2C_CLASS_HWMON,
.driver={
.owner=THIS_MODULE,
.name="hello",
},
.probe=pyl_i2c_probe,
.remove=pyl_i2c_remove,
.detect=pyl_i2c_detect,
.id_table=id_table,
.address_data=&pyl_address_data,
};
static int __init pyl_i2c_drv_init(void){
i2c_add_driver(&pyl_drv);
return 0;
}
static void __exit pyl_i2c_drv_exit(void){
i2c_del_driver(&pyl_drv);
}
MODULE_LICENSE("GPL");
module_init(pyl_i2c_drv_init);
module_exit(pyl_i2c_drv_exit);
为了区分到底去哪种类型的adapter查找,是class来区分。
/* i2c adapter classes (bitmask) */
#define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */
#define I2C_CLASS_TV_ANALOG (1<<1) /* bttv + friends */
#define I2C_CLASS_TV_DIGITAL (1<<2) /* dvb cards */
#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */
#define I2C_CLASS_SPD (1<<7) /* SPD EEPROMs and similar */
除了probe、remove函数之外,还有detect函数要实现。在detect里面要实现设备的进一步区分。根据传入的地址或者发送信号,接收信号来区分设备。
这里用到地址列表,所以就需要一个结构体保存这些。里面的内容在地址匹配函数里面分别都有一个for循环进行判断。
/* i2c_client_address_data is the struct for holding default client
* addresses for a driver and for the parameters supplied on the
* command line
*/
struct i2c_client_address_data {
const unsigned short *normal_i2c;
const unsigned short *probe;
const unsigned short *ignore;
const unsigned short * const *forces;
};
分析在注册驱动的过程:
i2c_add_driver
——i2c_register_driver
————class_for_each_device
————每个adapter都会调用__attach_adapter
——————i2c_detect 对address_data里面的所有地址for遍历,i2c_detect_address同时被调用
————————i2c_detect_address 实现地址匹配的函数
/* Separate detection function for new-style drivers */
static int i2c_detect_address(struct i2c_client *temp_client, int kind,
struct i2c_driver *driver)
{
struct i2c_board_info info;
struct i2c_adapter *adapter = temp_client->adapter;
int addr = temp_client->addr;
int err;
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, kind, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
return err == -ENODEV ? 0 : err;
}
/* Consistency check */
if (info.type[0] == '\0') {
dev_err(&adapter->dev, "%s detection function provided "
"no name for 0x%x\n", driver->driver.name,
addr);
} else {
struct i2c_client *client;
/* Detection succeeded, instantiate the device */
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
return 0;
}
——————————i2c_smbus_xfer 发送信号,判断是否接收到ACK,成功后继续执行下面的过程
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, kind, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
return err == -ENODEV ? 0 : err;
}
————————————i2c_new_device 回到第一步动态创建注册设备的过程
可以看到,在驱动注册过程中,通过struct i2c_client_address_data *address_data;里面地址来实现设备的查找,这个和第二个方法类似,i2c_new_probe_device也是探测到设备回应后才创建一个设备并且进行注册它。
最后一种方法是:
从用户空间实例化一个器件:这个方法相当智能快速,如下输入指令,即可增加一个i2c设备,同时增加了对应的设备文件。 # echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device 导致i2c_new_device被调用 删除设备 echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device 导致i2c_unregister_device
根据英文文档的标题,添加i2c设备有称之为“i2c设备的实例化”。