I2C设备构造的四种方法

下载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设备的实例化”。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值