本文由韦东山JZ2440开发板提供的资料整理,学习过程中请注意内核版本。
关于i2c设备的实例化,可参考Documentation\i2c\instantiating-devices,该帮助文档列举了4中 i2c设备的构建方式。
方法1. 定义一个i2c_board_info数组,内部枚举各设备的名字和设备地址,通过i2c_register_board_info(busnum, ...) ;该注册函数将i2c设备信息放入__i2c_board_list链表;当i2c总线注册时会扫描该链表并且自动创建i2c设备;i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device;所以,我们在使用该方法时,应该在i2c_register_adapter 之前i2c_register_board_info,不适合动态加载驱动程序。
Example (from omap2 h4): static struct i2c_board_info h4_i2c_board_info[] __initdata = { { I2C_BOARD_INFO("isp1301_omap", 0x2d), .irq = OMAP_GPIO_IRQ(125), }, { /* EEPROM on mainboard */ I2C_BOARD_INFO("24c01", 0x52), .platform_data = &m24c01, }, { /* EEPROM on cpu card */ I2C_BOARD_INFO("24c01", 0x57), .platform_data = &m24c01, }, }; static void __init omap_h4_init(void) { (...) i2c_register_board_info(1, h4_i2c_board_info, ARRAY_SIZE(h4_i2c_board_info)); (...) }
当我们认为设备肯定存在时,可以直接调用i2c_new_device构建设备。
static struct i2c_board_info at24cxx_info = { I2C_BOARD_INFO("at24c08", 0x50), }; static struct i2c_client *at24cxx_client; static int at24cxx_dev_init(void) { struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(0); at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info); i2c_put_adapter(i2c_adap); return 0; } static void at24cxx_dev_exit(void) { i2c_unregister_device(at24cxx_client); } module_init(at24cxx_dev_init); module_exit(at24cxx_dev_exit); MODULE_LICENSE("GPL");
如果我们不确定该总线上存在该设备,我们可以提供一个地址列表,使用i2c_new_probed_device去探测,当探测识别出该设备时才会构建i2c设备。
static struct i2c_client *at24cxx_client; static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END }; static int at24cxx_dev_init(void) { struct i2c_adapter *i2c_adap; struct i2c_board_info at24cxx_info; memset(&at24cxx_info, 0, sizeof(struct i2c_board_info)); strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE); i2c_adap = i2c_get_adapter(0); at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL); i2c_put_adapter(i2c_adap); if (at24cxx_client) return 0; else return -ENODEV; } static void at24cxx_dev_exit(void) { i2c_unregister_device(at24cxx_client); } module_init(at24cxx_dev_init); module_exit(at24cxx_dev_exit); MODULE_LICENSE("GPL");
创建设备:echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device,导致i2c_new_device被调用
删除设备:echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device,导致i2c_unregister_device
方法4. 前面3中方法都要事先确定适配器(i2c总线、i2c控制器),如果我们们事先不知道该i2c设备连接到哪个适配器上怎么办?示例见 drivers/hwmon/lm90.c
驱动程序示例:
static int major; static struct class *class; static struct i2c_client *at24cxx_client; /* 传入: buf[0] : addr * 输出: buf[0] : data */ static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off) { unsigned char addr, data; copy_from_user(&addr, buf, 1); data = i2c_smbus_read_byte_data(at24cxx_client, addr); copy_to_user(buf, &data, 1); return 1; } /* buf[0] : addr * buf[1] : data */ static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off) { unsigned char ker_buf[2]; unsigned char addr, data; copy_from_user(ker_buf, buf, 2); addr = ker_buf[0]; data = ker_buf[1]; printk("addr = 0x%02x, data = 0x%02x\n", addr, data); if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data)) return 2; else return -EIO; } static struct file_operations at24cxx_fops = { .owner = THIS_MODULE, .read = at24cxx_read, .write = at24cxx_write, }; static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id) { at24cxx_client = client; //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); major = register_chrdev(0, "at24cxx", &at24cxx_fops); class = class_create(THIS_MODULE, "at24cxx"); device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */ return 0; } static int __devexit at24cxx_remove(struct i2c_client *client) { //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); device_destroy(class, MKDEV(major, 0)); class_destroy(class); unregister_chrdev(major, "at24cxx"); return 0; } static const struct i2c_device_id at24cxx_id_table[] = {// name很重要,需要和i2c_board_info中的type匹配 { "at24c08", 0 }, {} }; /* 1. 分配/设置i2c_driver */ static struct i2c_driver at24cxx_driver = { .driver = { .name = "100ask", .owner = THIS_MODULE, }, .probe = at24cxx_probe, .remove = __devexit_p(at24cxx_remove), .id_table = at24cxx_id_table, }; static int at24cxx_drv_init(void) { /* 2. 注册i2c_driver */ i2c_add_driver(&at24cxx_driver); return 0; } static void at24cxx_drv_exit(void) { i2c_del_driver(&at24cxx_driver); } module_init(at24cxx_drv_init); module_exit(at24cxx_drv_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* i2c_test r addr * i2c_test w addr val */ void print_usage(char *file) { printf("%s r addr\n", file); printf("%s w addr val\n", file); } int main(int argc, char **argv) { int fd; unsigned char buf[2]; if ((argc != 3) && (argc != 4)) { print_usage(argv[0]); return -1; } fd = open("/dev/at24cxx", O_RDWR); if (fd < 0) { printf("can't open /dev/at24cxx\n"); return -1; } if (strcmp(argv[1], "r") == 0) { buf[0] = strtoul(argv[2], NULL, 0); read(fd, buf, 1); printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]); } else if ((strcmp(argv[1], "w") == 0) && (argc == 4)) { buf[0] = strtoul(argv[2], NULL, 0); buf[1] = strtoul(argv[3], NULL, 0); if (write(fd, buf, 2) != 2) printf("write err, addr = 0x%02x, data = 0x%02x\n", buf[0], buf[1]); } else { print_usage(argv[0]); return -1; } return 0; }