Linux kernel I2C linux驱动程序可以';找不到设备
我写了一个i2c驱动程序,只是为了测试 我使用的是Rasperry Pi 3,我在GPIO引脚头上连接了两个I2C引脚。我能够使用i2c工具连接到它,使用地址0x3c和0x3d 我能够使用i2c设置向显示器发送数据:Linux kernel I2C linux驱动程序可以';找不到设备,linux-kernel,raspberry-pi,linux-device-driver,embedded-linux,i2c,Linux Kernel,Raspberry Pi,Linux Device Driver,Embedded Linux,I2c,我写了一个i2c驱动程序,只是为了测试 我使用的是Rasperry Pi 3,我在GPIO引脚头上连接了两个I2C引脚。我能够使用i2c工具连接到它,使用地址0x3c和0x3d 我能够使用i2c设置向显示器发送数据: i2cset -y 1 0x3c [data] i2cset -y 1 0x3d [data] 命令 i2cdetect -y 1 提供以下输出: 0 1 2 3 4 5 6 7 8 9 a b c d e f 00:
i2cset -y 1 0x3c [data]
i2cset -y 1 0x3d [data]
命令
i2cdetect -y 1
提供以下输出:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c 3d -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
我正在使用以下驱动程序来了解它的功能以及Linux中I2C的工作原理:
#include "i2c_test.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#define print_client(c) printk("%s: Client (Name: %s), (Flags: %x), (Addr: %x), (Adapter: ), (Device: ), (IRQ: %d)\n",\
__FUNCTION__, c->name, c->flags, c->addr, c->irq)
#define print_board(b) printk("%s: Board (Type: %s), (Flags: %x), (Addr: %x), (IRQ: %d)\n",\
__FUNCTION__, b->type, (unsigned int)b->flags, (unsigned int)b->addr, b->irq)
static struct i2c_device_id ssd1306_idtable[] = {
{ "ssd1306", 0 },
{}
};
const unsigned short ssd1306_address_list[] = {
0x3c,
0x3d,
0x7a,
0x78,
};
MODULE_DEVICE_TABLE(i2c, ssd1306_idtable);
struct dev_pm_ops ssd1306_pm_ops = {
// Don't know how to use
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
int ssd1306_probe(struct i2c_client* client, const struct i2c_device_id * dev_id)
{
print_client(client);
return 0;
}
#else //LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0)
int ssd1306_probe_new(struct i2c_client* client)
{
print_client(client);
return 0;
}
#endif // Kernel version
int ssd1306_remove(struct i2c_client* client)
{
print_client(client);
return 0;
}
void ssd1306_shutdown(struct i2c_client* client)
{
print_client(client);
}
int ssd1306_detect(struct i2c_client* client, struct i2c_board_info* board_info)
{
print_client(client);
print_board(board_info);
return 0;
}
static struct i2c_driver ssd1306_driver = {
.driver = {
.name = "i2c_test",
.pm = &ssd1306_pm_ops
},
.id_table = ssd1306_idtable,
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
.probe = ssd1306_probe,
#else //LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0)
.probe_new = ssd1306_probe_new, //needs Kernel 4.10 or later
#endif //Kernel Version
.remove = ssd1306_remove,
.class = I2C_CLASS_HWMON, // correct??
.detect = ssd1306_detect,
.address_list = ssd1306_address_list,
//.command = ssd1306_command,
.shutdown = ssd1306_shutdown
};
static int __init mod_init(void)
{
printk("init " __FILE__ "\n");
i2c_add_driver(&ssd1306_driver);
printk("driver added!\n");
return 0;
}
static void __exit mod_exit(void)
{
printk("remove driver\n");
i2c_del_driver(&ssd1306_driver);
printk("driver removed\n");
printk("exit " __FILE__ "\n");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_LICENSE("GPL");
如果有人知道该做什么或我做错了什么并能帮助我,那就太好了
谢谢
p0kR模块加载时,I2C设备不会自动探测(I2C不提供任何标准方法)。因此,要调用驱动程序的探测函数,需要告诉内核驱动程序应该处理哪个I2C addr。使用sysfs的简单方法是:
# echo [your_device_name] [your_device_i2c_addr] > /sys/bus/i2c/devices/i2c-[i2c_bus_number]/new_device
(用适当的数字和名称替换方括号中的零件)
在你的特殊情况下
# echo ssd1306 0x3c > /sys/bus/i2c/devices/i2c-1/new_device
请注意,内核不会为您执行任何检查,因此无论I2C设备是否连接,都会调用probe
函数
其他方法包括,例如,在设备树数据中指定设备和驱动程序。有关更多详细信息,请参阅
编辑:实际上,在某些情况下,自动检测存在(然后调用
检测
函数),但I2C总线必须对此达成一致(设备类别必须匹配,等等)。但据我所知,这种方法不适用于一般设备,而是用于PC等的内部监控传感器。在大多数情况下,明确指定设备是首选方法。I2C设备在加载模块时不会自动探测(I2C不提供任何标准方法)。因此,要调用驱动程序的探测函数,需要告诉内核驱动程序应该处理哪个I2C addr。使用sysfs的简单方法是:
# echo [your_device_name] [your_device_i2c_addr] > /sys/bus/i2c/devices/i2c-[i2c_bus_number]/new_device
(用适当的数字和名称替换方括号中的零件)
在你的特殊情况下
# echo ssd1306 0x3c > /sys/bus/i2c/devices/i2c-1/new_device
请注意,内核不会为您执行任何检查,因此无论I2C设备是否连接,都会调用probe
函数
其他方法包括,例如,在设备树数据中指定设备和驱动程序。有关更多详细信息,请参阅
编辑:实际上,在某些情况下,自动检测存在(然后调用
检测
函数),但I2C总线必须对此达成一致(设备类别必须匹配,等等)。但据我所知,这种方法不适用于一般设备,而是用于PC等设备的内部监控传感器。在大多数情况下,明确指定设备是首选方法。首先,I2C设备不像USB设备那样动态枚举。
如果你的驱动程序是内置的,很明显你的驱动程序会被调用。无需通过sysfs接口访问它。
如果您使用的是设备树,请在.dts中添加i2c设备详细信息,并在启动过程中调用驱动程序。首先,i2c设备不会像USB设备那样动态枚举。 如果你的驱动程序是内置的,很明显你的驱动程序会被调用。无需通过sysfs接口访问它。
如果您使用的是设备树,请在.dts中添加i2c设备的详细信息,并在启动过程中调用驱动程序。Err,yes。您有什么错误消息/问题?你试过只连接一个显示器吗?我想当我加载驱动程序时,至少会调用“probe”函数。然后我会打印输出。除了初始和退出,我没有打印。我在每只水獭之后都移除并重新连接了两个显示器,但仍然没有输出。我将尝试使用您所说的单个显示链接。但您的代码显示为添加然后删除您的驱动程序。结果,驱动程序被添加,然后被删除。你们期望会发生什么,神奇的事情?我想当它是内置的,当一个设备连接时,探测器会被调用。所以我打电话给insmod,等了一段时间,断开设备,重新连接,再次等待,然后打电话给rmmod。我该怎么办,那个探测器被调用了?可能是重复的,呃,是的。您有什么错误消息/问题?你试过只连接一个显示器吗?我想当我加载驱动程序时,至少会调用“probe”函数。然后我会打印输出。除了初始和退出,我没有打印。我在每只水獭之后都移除并重新连接了两个显示器,但仍然没有输出。我将尝试使用您所说的单个显示链接。但您的代码显示为添加然后删除您的驱动程序。结果,驱动程序被添加,然后被删除。你们期望会发生什么,神奇的事情?我想当它是内置的,当一个设备连接时,探测器会被调用。所以我打电话给insmod,等了一段时间,断开设备,重新连接,再次等待,然后打电话给rmmod。我该怎么办,那个探测器被调用了?可能是重复的,这看起来就像我在寻找的答案。我知道没有标准,但我想知道调用probe需要什么。当您从用户空间(
sysfs
)使用实例化时,您必须传递设备名,而不是驱动程序名。在您的情况下,应该是echo ssd1306 0x3c>/sys/bus/i2c/devices/i2c-1/new_device
@p0kR根据您的h/w连接更改i2c
总线号。我刚刚在上面的评论中添加了这个例子。是的,这个解决方案对我来说很有效,就像@vinodmaverick所说的,它必须是ssd1306。我根据“非常感谢”编辑了这个答案。你当然正确地使用了设备和驱动程序的名称@vinodmaverick,谢谢你指出我的错误。这似乎是我想要的答案。我知道没有标准,但我想知道调用probe需要什么。当您从用户空间(sysfs
)使用实例化时,您必须传递设备名,而不是驱动程序名。在里面