Linux I2C驱动注册详解

 更新时间:2025年07月22日 09:24:45   作者:大肥周  
文章介绍了I2C驱动注册机制,核心是structi2c_driver结构体,需填充name、probe和id_table字段,name用于显示驱动名称,probe在设备匹配后调用(无论设备是否存在),通过设备树或静态注册识别设备,最终触发驱动探针流程

注册接口

#define i2c_add_driver(driver) \
       i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

注册的结构体是struct i2c_driver,主要填充name、probe和id_table字段,其中name是驱动名称,到时会在/sys/bus/i2c/drivers显示这个驱动名称,probe会在I2C设备匹配后调用,不是发现I2C设备,即便实际上这个设备不在,但是代码里注册了这个设备,或者设备树里配置了这个设备,就会被调用。

driver.of_match_table和 id_table两个成员列出的是驱动程序所能支持的所有设备,其中driver.of_match_table里的设备,是跟设备树匹配的,id_table是跟静态注册是调用的struct i2c_board_info里的name做比较的。

示例代码

驱动注册:

static int __devinit zsl_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{	
	struct property *pp = NULL;
	uint8_t buf[4];
	struct i2c_msg msgs[2] = {{0}, {0}};
	int len = sizeof(msgs) / sizeof(msgs[0]);

	printk(KERN_INFO "%s: %x\n",__func__,client->addr);

	pp = of_find_property(client->dev.of_node, "testdata", NULL);
	if (pp)
		printk(KERN_INFO "%s: %d:%s \n",__func__,pp->length,(char *)pp->value);
	
	dump_stack();

	if (client->adapter)
	{
		buf[0] = 0;
		buf[1] = 4;
		buf[2] = 0;
		buf[3] = 0;
		memset(msgs,0,sizeof(struct i2c_msg)*2);
		msgs[0].addr  = client->addr;
		msgs[0].flags = 0x0;
		msgs[0].len   = 2;
		msgs[0].buf   = buf;
		
		msgs[1].addr  = client->addr;
		msgs[1].flags = I2C_M_RD;
		msgs[1].len   = 4;
		msgs[1].buf   = buf;

		if (i2c_transfer(client->adapter, msgs,	len) == len)
			printk(KERN_INFO "zsl i2c_transfer r: %d:%x %x %x %x\n",len,buf[0],buf[1],buf[2],buf[3]);
		
		buf[0] = 0;
		buf[1] = 4;
		buf[2] = 7;
		buf[3] = 8;

		memset(msgs,0,sizeof(struct i2c_msg)*2);
		msgs[0].addr = client->addr;
		msgs[0].flags = 0;
		msgs[0].len = 4;
		msgs[0].buf = buf;
		
		if (i2c_transfer(client->adapter, msgs,	1) == 1)
			printk(KERN_INFO "zsl i2c_transfer w: %d:%x %x %x %x\n",1,buf[0],buf[1],buf[2],buf[3]);

		buf[0] = 0;
		buf[1] = 4;
		buf[2] = 0;
		buf[3] = 0;
		memset(msgs,0,sizeof(struct i2c_msg)*2);
		msgs[0].addr  = client->addr;
		msgs[0].flags = 0x0;
		msgs[0].len   = 2;
		msgs[0].buf   = buf;
		
		msgs[1].addr  = client->addr;
		msgs[1].flags = I2C_M_RD;
		msgs[1].len   = 4;
		msgs[1].buf   = buf;
		msleep(100);
		len = i2c_transfer(client->adapter, msgs, len);
		if (2 == len)
			printk(KERN_INFO "zsl i2c_transfer r: %d:%x %x %x %x\n",len,buf[0],buf[1],buf[2],buf[3]);				
	}
	
    return 0;
}
 
static void __devexit zsl_i2c_drv_remove(struct i2c_client *client)
{
	printk(KERN_INFO "%s: \n",__func__);
}
 
static const struct i2c_device_id zsl_dev_id_table[] = {
    { "zsl_i2c_dev", 0 },
    {}
};//这里的名字很重要,驱动第一种匹配设备的方式要用到
 
static const struct of_device_id zsl_of_match_ids[] = {
	{ .compatible = "i2c_name,zsl",		.data = NULL },
	{ /* END OF LIST */ } //最后一项为空,用于判断数组遍历完成
};

static struct i2c_driver zsl_i2c_driver = {
    .driver        = {
        .name        = "zsl_i2c",
        .owner        = THIS_MODULE,
        .of_match_table = zsl_of_match_ids,//设备树匹配用
    },
    .probe		= zsl_i2c_drv_probe,
    .remove		= __devexit_p(zsl_i2c_drv_remove),
    .id_table	= zsl_dev_id_table,  //设备树不需要,i2c_register_board_info匹配使用
};

void zsl_i2c_drv_init(void)
{
    i2c_add_driver(&zsl_i2c_driver);
}

静态设备注册

static struct i2c_board_info zsl_i2c_dev = {
    I2C_BOARD_INFO("zsl_i2c_dev", 0x50),//这个名字很重要,用于匹配 I2C 驱动
};

static struct i2c_client *zsl_i2c_client;

void zsl_i2c_dev_init(void)
{
       i2c_register_board_info(4, &zsl_i2c_dev, 1);
}

设备树注册

&i2c4 {
       zsli2c0: zsli2c@50 {
              compatible = "i2c_name,zsl";
              status = "okay";
              testdata = "asdfg";
              reg = <0x50>;
       };
};

运行后probe就会被调用,无论配的是什么地址,0x50设备存在,0x60设备不存在,probe都能被调用,但是0x60的读不到数据。

调用关系

probe里的堆栈打印如下:

从芯片厂家的I2C驱动初始化接口rk3x_i2c_driver_init开始,使用platform_driver_register接口注册rk3x_i2c_driver,当设备树里有对应的I2C总线时,就会注册I2C设备,匹配后调用这里的probe接口rk3x_i2c_probe。

rk3x_i2c_probe里会初始化 Rockchip I2C控制器,调用 i2c_register_adapter将 I2C 适配器注册到内核,进而调用of_i2c_register_devices 扫描设备树,调用i2c_new_client_device创建配置的I2C设备。

i2c_new_client_device里会创建struct i2c_client并调用device_register注册到内核,触发 device_add,内核会尝试匹配驱动(通过 compatible 字符串或者name)。如果匹配成功,调用 i2c_device_probe到probe接口的zsl_i2c_drv_probe。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Linux中如何通过端口号查找进程号

    Linux中如何通过端口号查找进程号

    这篇文章主要介绍了Linux中如何通过端口号查找进程号问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • Apache 二级域名实现方法介绍

    Apache 二级域名实现方法介绍

    首先,你的拥有一个有泛域名解析的顶级域名,例如:domain.com
    2009-05-05
  • CentOS 7.2配置Apache服务httpd(上)

    CentOS 7.2配置Apache服务httpd(上)

    这篇文章主要为大家详细介绍了CentOS 7.2配置Apache服务 httpd上篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Linux用户管理与常见权限命令

    Linux用户管理与常见权限命令

    在Linux系统中,用户和组是管理权限和资源访问的基本单元,用户可以属于一个或多个组,组用于集中管理一组用户的权限,文件权限决定了用户或组对文件的访问级别,每个文件或目录都有三个权限集,分别适用于文件所有者(User)、所属组(Group)和其他用户(Others)
    2024-08-08
  • 在Fedora 10下配置SVN服务器的步骤

    在Fedora 10下配置SVN服务器的步骤

    subversion(简称svn)是近年来崛起的版本管理工具,是cvs的接班人。目前,绝大多数开源软件都使用svn作为代码版本管理软件。
    2010-12-12
  • linux查找大文件指定内容的实现方法

    linux查找大文件指定内容的实现方法

    今天小编就为大家分享一篇linux查找大文件指定内容的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • Centos7升级glibc导致系统异常(无法开机)解决方法

    Centos7升级glibc导致系统异常(无法开机)解决方法

    大家好,本篇文章主要讲的是Centos7升级glibc导致系统异常(无法开机)解决方法,感兴趣的同学赶快来看看吧,希望对你有帮助
    2021-11-11
  • linux线程间的同步与互斥知识点总结

    linux线程间的同步与互斥知识点总结

    在本篇文章里小编给大家整理的是关于linux线程间的同步与互斥的相关知识点,有兴趣的朋友们学习下。
    2019-11-11
  • Linux PXE高效批量网络装机过程

    Linux PXE高效批量网络装机过程

    PXE(预启动执行环境)是一种网络引导技术,允许从远程服务器通过网络下载引导镜像来安装操作系统,本文介绍了PXE的优点如规模化、自动化和远程实现,以及搭建PXE服务器的基本步骤,包括安装和配置TFTP、DHCP服务
    2024-09-09
  • 吸引发烧友的视听Linux发行版

    吸引发烧友的视听Linux发行版

    今天小编就为大家分享一篇关于吸引发烧友的Linux发行版,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-09-09

最新评论