linux-I2C驱动(3)--怎么构造一个设备

内容参照韦东山linux视频教程--Linux_3.4.2_IIC驱动

4种方法注册设备

1.通过总线号声明i2c设备

都在内核编译种完成,修改内核重新编译,就能得到已经注册的设备

1、定义一个单板信息i2c_board_info结构体:名字、设备地址;

static struct i2c_board_info i2c_devs3[] __initdata = {
	{
		I2C_BOARD_INFO("ft5x0x_ts", 0x70>>1),
		.irq = IRQ_EINT(4),
		.platform_data = &ft5x0x_pdata,
	},
}

ft5x0x_ts:名字;0x70>>1:地址。

2、注册i2c_register_board_info();

i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));

i2c_register_board_info(int busnum,
	struct i2c_board_info const *info, unsigned len)

参数:

  1. int busnum:I2C总线号,哪一个适配器
  2. struct i2c_board_info const *info:单板信息结构体
  3. unsigned len:单板结构体数

3、i2c_register_board_info > list_add_tail放入链表

list_add_tail(&devinfo->list, &__i2c_board_list);

4、链表__i2c_board_list何时被使用?

注册适配器i2c_register_adapter() > I2C扫描静态单板信息i2c_scan_static_board_info() > 创建i2c设备,生成i2c_new_client, i2c_new_device()

使用限制:

必须在注册适配器(i2c_register_adapter)之前操作,注册i2c单板信息i2c_scan_static_board_info。所以不适合动态加载insmod。

2.实例化无误的创建设备

i2c_new_device :认为设备肯定存在

i2c_new_probed_device:对于“已经识别出来的设备”probed_device,才会创建“new” ,也就是说,如果实际设备不存在将会创建失败。使用probe函数测试设备是否存在。如果没有定义probe函数,则会采用默认的i2c_default_probe函数。i2c_default_probe函数内发送start信号,是否能收到ACK信号。

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

参数:

  1. struct i2c_adapter *adap :适配器
  2. struct i2c_board_info const *info:单板信息

返回值:

  1. i2c_client结构体指针

i2c_new_device 函数创建设备简单历程:

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/acpi.h>

struct i2c_board_info const at24cxx_info = {
	{
		I2C_BOARD_INFO("at24c08", 0x18),
	},
}

static struct i2c_client *at24cxx_client;

static int __init at24cxx_dev_init(void)
{
	struct i2c_adapter *i2c_adap;
	i2c_adap = i2c_get_adapter(0);
	i2c_new_device(i2c_adap,&at24cxx_info);
	i2c_put_adapter(i2c_adap);
	return 0;
}

static void __exit at24cxx_dev_exit(void)
{
	i2c_unregister_device(at24cxx_client);
}

module_exit(at24cxx_exit);
module_init(at24cxx_init);
MODULE_LICENSE("GPL");

struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
		      struct i2c_board_info *info,
		      unsigned short const *addr_list,
		      int (*probe)(struct i2c_adapter *, unsigned short addr))

参数:

  1. struct i2c_adapter *adap:适配器
  2. struct i2c_board_info const *info:单板信息
  3. unsigned short const *addr_list:地址列表 (确认i2c设备是否真实存在,发送start,能否收到ACK信号)
  4. int (*probe) :probe函数指针

返回值:

  1. i2c_client结构体指针

i2c_new_probed_device 函数创建设备简单历程:

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/acpi.h>

struct i2c_board_info const at24cxx_info = {
	{
		I2C_BOARD_INFO("at24c08", 0x18),
	},
}

static struct i2c_client *at24cxx_client;

static const unsigned short addr_list[] = { 0x60,0x50,I2C_CLIENT_END};

static int __init at24cxx_dev_init(void)
{
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info at24cxx_info;
	
	memset(&at24cxx_info,0sizeof(struct i2c_board_info));
	strlcpy(at24cxx_info.type,"at24c08",I2C_NAME_SIZE);
	
	i2c_adap = i2c_get_adapter(0);
	at24cxx_client = i2c_new_probed_device(i2c_adap,&at24cxx_info,addr_list,NULL);
	i2c_put_adapter(i2c_adap);
	
	if(at24cxx_client)
	{
		return 0;
	}
	else
		return -ENODEV;
}

static void __exit at24cxx_dev_exit(void)
{
	i2c_unregister_device(at24cxx_client);
}

module_exit(at24cxx_exit);
module_init(at24cxx_init);
MODULE_LICENSE("GPL");

3.从用户空间实例化设备

以2440为例:

/sys/class/i2c-adapter/内只有i2c-0,这是一个链接文件,链接到/sys/devices/platform/s3c2440-i2c/i2c-0,这个目录下面有new_device和delete_device。

创建设备:

echo at24c08 0x50 > /sys/devices/platform/s3c2440-i2c/i2c-0/new_device

-->会导致i2c_new_device被调用

echo 0x50 > /sys/devices/platform/s3c2440-i2c/i2c-0/delete_device

-->会导致i2c_unregister_device被调用

4.i2c_add_driver

简单描述:其他3种方法都要事先知道适配器(i2c总线,i2c控制器)。去“class”表示的这一类“i2c适配器”,用detect函数来确定能否找到address_list里的设备,如果能找到就用i2c_new_device来注册i2c_client,这会和i2c_driver的id_table比较,如果匹配就调用probe。

疑问1:如果事先不知道设备在哪个适配器上,怎么办? --去class表示的所有适配器上查找。

疑问2:有一些i2c设备的地址是一样的,怎么继续分配它是哪一款? --用detect函数继续区分。

I2C驱动注册过程分析i2c_add_driver:

i2c_add_driver > i2c_register_driver

   >driver_register at24cxx_driver放入i2c_bus_type的drv链表,并且从dev链表中取出i2c_client并调用probe。

   >i2c_for_each_dev(driver, __process_new_driver); 表示对于每一个适配器,调用__process_new_driver。

static int __process_new_driver(struct device *dev, void *data)
{
	if (dev->type != &i2c_adapter_type)
		return 0;
	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

dev的tpye是否是适配器,不是就返回0,是的话就调用i2c_do_add_adapter函数。

i2c_do_add_adapter > i2c_detect(adap, driver); 对于每一个适配器,调用它的函数确定能否找到address_list里的设备是否存在,如果存在,再调用detect函数进一步确定、设置,然后i2c_new_device。

	for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
			"addr 0x%02x\n", adap_id, address_list[i]);
		temp_client->addr = address_list[i];
		err = i2c_detect_address(temp_client, driver);
		if (unlikely(err))
			break;
	}

i2c_detect_address(temp_client, driver); 探测address_list上面的地址,发送一个S信号+地址,是否能收到ACK。

if (!i2c_default_probe(adapter, addr))
		return 0;

确定发这个地址可以收到信息。

 

示例代码:

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/acpi.h>

static int at24cxx_probe(struct i2c_client *client,
		       const struct i2c_device_id *id)
{
   printk("%S %s %d \n",__FILE__,__FUNCTION__,__LINE__);
   return 0;
}

static int at24cxx_remove(struct i2c_client *client,
		       const struct i2c_device_id *id)
{
   printk("%S %s %d \n",__FILE__,__FUNCTION__,__LINE__);
   return 0;
}

static int at24cxx_detect(struct i2c_client *client,
					struct i2c_board_info *info)
{
	/* 应该去访问硬件,确定设备确实存在 */
	printk("at24cxx_detect : addr = 0x%x\n",client->addr);
	if()
	{
		strlcpy(info->type,"at24c08",I2C_NAME_SIZE);
		return 0;
	}
	else
		return -ENODEV;
}
static const unsigned short addr_list[] = { 0x60,0x50,I2C_CLIENT_END};

static struct i2c_driver at24cxx_driver = {
	.class = I2C_CLIENT_HWMON, /* 表示去那些适配器上找设备*/
	.driver = {
		   .name = "pocky",
		   .owner = THIS_MODULE,
		   },
	.probe = at24cxx_probe,
	.remove = at24cxx_remove,
	.id_table = at24cxx_id,
	.detect = at24cxx_detect,
	.address_list = addr_list,
};


static int __init at24cxx_dev_init(void)
{
	/* 2.注册 i2c_driver */
	i2c_add_driver(&at24cxx_driver);
	return 0;
}

static void __exit at24cxx_dev_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}

module_exit(at24cxx_exit);
module_init(at24cxx_init);
MODULE_LICENSE("GPL");

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值