I2C linux驱动程序找不到设备

时间:2017-04-07 15:18:49

标签: linux-kernel raspberry-pi linux-device-driver embedded-linux i2c

我已经编写了一个i2c驱动程序,仅用于测试目的。

我正在使用Rasperry Pi 3,并且已将两个ssd1306 OLED displays连接到GPIO引脚接头上的I2C引脚。我可以使用i2c-tools使用地址0x3c和0x3d连接到它。

我可以使用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

2 个答案:

答案 0 :(得分:1)

首先,I2C设备不像USB设备那样动态枚举。 如果您的驱动程序是内置的,显然您的驱动程序将被调用。无需通过sysfs接口访问它。 如果您正在使用设备树,请在.dts中添加您的i2c设备详细信息,并在启动期间调用您的驱动程序。

答案 1 :(得分:0)

模块加载时不会自动探测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总线必须同意它(并且设备类必须匹配,等等) 。但据我所知,此方法不适用于一般设备,而是用于PC等内部监控传感器。在大多数情况下,明确指定设备是首选方法。