内容参照韦东山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)
参数:
- int busnum:I2C总线号,哪一个适配器
- struct i2c_board_info const *info:单板信息结构体
- 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)
参数:
- struct i2c_adapter *adap :适配器
- struct i2c_board_info const *info:单板信息
返回值:
- 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))
参数:
- struct i2c_adapter *adap:适配器
- struct i2c_board_info const *info:单板信息
- unsigned short const *addr_list:地址列表 (确认i2c设备是否真实存在,发送start,能否收到ACK信号)
- int (*probe) :probe函数指针
返回值:
- 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");