Linux kernel I2C linux驱动程序可以';找不到设备

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:

我写了一个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:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
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
)使用实例化时,您必须传递设备名,而不是驱动程序名。在里面