i have written a little i2c driver just for testing purposes.
I am using a Rasperry Pi 3 and I have connected two ssd1306 OLED displays to my I2C pins on the GPIO pin header. I am able to connect to it, using the i2c-tools using the adresses 0x3c and 0x3d.
I am able to send data to the displays using i2c-set:
i2cset -y 1 0x3c [data]
i2cset -y 1 0x3d [data]
The command
i2cdetect -y 1
gives me the following output:
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: -- -- -- -- -- -- -- --
I am using the following driver to see what it does and how I2C in Linux may work:
#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");
I get the following output by 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
It would be nice if someone know what to do or what I am doing wrong and could help me.
Thanks
p0kR
I2C devices are not probed automatically when module is loaded (I2C does not offer any standard method for this). So, to have your driver's probe function called, you need to tell the kernel which I2C addr should be handled by your driver. Simple method using sysfs is:
# echo [your_device_name] [your_device_i2c_addr] > /sys/bus/i2c/devices/i2c-[i2c_bus_number]/new_device
(replace parts in square brackets by appropriate numbers and names)
In your particular case it would be
# echo ssd1306 0x3c > /sys/bus/i2c/devices/i2c-1/new_device
Note that kernel won't do any checking for you, so your probe
function will be called regardless the I2C device is connected or not.
Other methods include, for example, specifying your device and driver in device tree data. For more details refer to linux kernel documentation.
Edit: Actually in some situation autodetection exists (and then detect
function is called), but I2C bus has to agree on it (and device class have to match, among others). But this method is not used for general devices as far as I know, rather for internal monitoring sensors in PC etc. Specifying your device explicitly is preferred method in the most situations.