I2C linux 驱动程序找不到设备
I2C linux driver can't find device
我写了一个小的 i2c 驱动程序只是为了测试目的。
我正在使用 Rasperry Pi 3,并且已将两个 ssd1306 OLED displays 连接到 GPIO 排针上的 I2C 引脚。我可以使用地址为 0x3c 和 0x3d 的 i2c-tools 连接到它。
我可以使用 i2c-set 将数据发送到显示器:
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");
我通过 dmesg 得到以下输出:
[ 1676.649683] init /home/pi/projects/playground/i2c_test/i2c_test.c
[ 1676.649790] driver added!
[ 1812.043182] remove driver
[ 1812.043301] driver removed
[ 1812.043306] exit /home/pi/projects/playground/i2c_test/i2c_test.c
如果有人知道该怎么做或我做错了什么并且可以帮助我,那就太好了。
谢谢
p0kR
加载模块时不会自动探测 I2C 设备(I2C 不为此提供任何标准方法)。因此,要调用您的驱动程序的探测函数,您需要告诉内核您的驱动程序应该处理哪个 I2C 地址。使用 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
函数都会被调用。
其他方法包括,例如,在设备树数据中指定您的设备和驱动程序。有关详细信息,请参阅 linux kernel documentation。
编辑: 实际上在某些情况下存在自动检测(然后调用 detect
函数),但 I2C 总线必须同意(和设备 class 必须匹配,等等)。但据我所知,这种方法并不用于一般设备,而是用于 PC 等内部监控传感器。在大多数情况下,明确指定你的设备是首选方法。
首先,I2C 设备不像 USB 设备那样被动态枚举。
如果您的驱动程序是内置的,显然您的驱动程序将被调用。无需通过 sysfs 接口访问它。
如果您正在使用设备树,请在 .dts 中添加您的 i2c 设备详细信息,并让您的驱动程序在启动期间被调用。
我写了一个小的 i2c 驱动程序只是为了测试目的。
我正在使用 Rasperry Pi 3,并且已将两个 ssd1306 OLED displays 连接到 GPIO 排针上的 I2C 引脚。我可以使用地址为 0x3c 和 0x3d 的 i2c-tools 连接到它。
我可以使用 i2c-set 将数据发送到显示器:
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");
我通过 dmesg 得到以下输出:
[ 1676.649683] init /home/pi/projects/playground/i2c_test/i2c_test.c
[ 1676.649790] driver added!
[ 1812.043182] remove driver
[ 1812.043301] driver removed
[ 1812.043306] exit /home/pi/projects/playground/i2c_test/i2c_test.c
如果有人知道该怎么做或我做错了什么并且可以帮助我,那就太好了。
谢谢
p0kR
加载模块时不会自动探测 I2C 设备(I2C 不为此提供任何标准方法)。因此,要调用您的驱动程序的探测函数,您需要告诉内核您的驱动程序应该处理哪个 I2C 地址。使用 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
函数都会被调用。
其他方法包括,例如,在设备树数据中指定您的设备和驱动程序。有关详细信息,请参阅 linux kernel documentation。
编辑: 实际上在某些情况下存在自动检测(然后调用 detect
函数),但 I2C 总线必须同意(和设备 class 必须匹配,等等)。但据我所知,这种方法并不用于一般设备,而是用于 PC 等内部监控传感器。在大多数情况下,明确指定你的设备是首选方法。
首先,I2C 设备不像 USB 设备那样被动态枚举。 如果您的驱动程序是内置的,显然您的驱动程序将被调用。无需通过 sysfs 接口访问它。 如果您正在使用设备树,请在 .dts 中添加您的 i2c 设备详细信息,并让您的驱动程序在启动期间被调用。