I2C设备驱动(三)--linux i2c驱动框架
2017-06-10 15:55
344 查看
转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter
i2c总线传输方法,总线传输协议的就是在此结构体成员函数中实现的。
上述两个数据结构i2c_adpter, i2c_algorithm都需要在控制器驱动中进行实现。
i2c_client是i2c slave设备的数据结构
i2c设备驱动数据结构,是i2c_client设备的驱动。
i2c_driver需要在i2c设备驱动中进行实现。
drivers/i2c/
├── algos
│ ├── i2c-algo-bit.c
│ ├── i2c-algo-pca.c
│ ├── i2c-algo-pcf.c
│ ├── i2c-algo-pcf.h
│ ├── Kconfig
│ └── Makefile
├── busses
│ ├── i2c-gpio.c
│ ├── i2c-vendor.c
│ ├── Kconfig
│ ├── Makefile
├── i2c-boardinfo.c
├── i2c-core.c
├── i2c-core.h
├── i2c-dev.c
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
├── Kconfig
└── Makefile
控制器驱动部分包括控制器设备注册,控制器驱动注册,总线传输和用户层接口四个部分,下面分别对它们进行说明。
dts中定义如下:
i2c controller是platform_device,因此驱动通过platform_driver_register进行注册。由于compatible属性为”vendor-i2c”的platform device已经在3.1中注册完成,vendor_i2c_probe会被加载执行。
其中,最后的adapter注册如下代码,在i2c-core.c实现。
(1)通过对dt进行解析,对挂在i2c controller下的i2c从设备进行注册。i2c从设备在linux中是通过i2c_client数据结构进行描述的,因此注册过程主要是对i2c_client数据结构的填充并将它注册到内核中。dt中的描述如下,本例中的i2c从设备是BOSCH的加速度传感器:
继续i2c_client设备的注册,代码如下:
(2) 扫描 __i2c_board_list,对已经添加到此list的i2c设备进行注册。在device tree应用之前,i2c从设备通过调用i2c_register_board_info将自己添加到 __i2c_board_list,目前此中方法已很少使用。
下面是在kernel的mach目录下找到的一个以此种方式添加到list中的例子。
至此,在控制器驱动中不仅对i2c_adapter设备进行了注册,还对i2c_client设备进行了注册,并建立了adapter和client之间的关系,而且它们都是i2c_bus_type总线类型的设备。
此处要强调一下, i2c控制器是platform device,它的驱动注册是通过platform_bus, platform_device和platform_driver之间的关系进行probe的,与i2c_bus_type不要混淆。
vendor_i2c_send_target_addr, vendor_i2c_readbytes, vendor_i2c_writebytes等依赖于具体厂商的实现。
读写操作最终都是通过i2c-core提供的i2c_transfer函数实现的,i2c_transfer又是通过调用adap->algo->master_xfer实现的,此处不再进行展开。
此例中的i2c_driver使用传感器的驱动bma2x2_driver,注册代码如下。
驱动注册i2c_add_driver函数的调用过程中会调用dev->bus->probe,其中dev是通过遍历所有dev设备得到的。当i2c_client设备注册完成后,client->dev.bus = &i2c_bus_type,因此,此处调用的bus->probe就是i2c_bus_type.probe函数指针指向的函数。
i2c_bus_type在i2c-core.c中定义如下:
i2c_match_id的实现就是在比对client->name和id->name。client->name在controller驱动中注册i2c_client设备的过程中有过赋值,它来自于dt中slave设备的compatible属性。id_name在设备驱动中定义,本例中就是宏定义SENSOR_NAME,即 “bma2x2”,与dt中slave中的compatible属性逗号后面的字符串一致。因此driver->probe即 bma2x2_probe会被调用。
1. bus, device, device_driver的驱动模型。
2. controller和slave设备的关系。
3. i2c_client与i2c_driver的关系。
4. core层的价值。
1 体系结构
linux的i2c体系由以下三部分组成:1.1 i2c核心
由linux内核提供,定义基本数据结构,实现i2c总线,驱动和设备的注册、注销,通信方法等。与设备无关。1.2 i2c控制器驱动
由i2c控制器厂商提供,目前i2c控制器大多集中在soc芯片中,所以大多由soc厂商提供。代表i2c master,主要实现控制器的设备注册和驱动注册。1.3 i2c设备驱动
一般由外设厂商提供,代表i2c slave,主要实现从设备的注册和驱动注册。2 数据结构
i2c_adapter对应一个适配器设备,用于描述i2c master控制器struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; };
i2c总线传输方法,总线传输协议的就是在此结构体成员函数中实现的。
struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };
上述两个数据结构i2c_adpter, i2c_algorithm都需要在控制器驱动中进行实现。
i2c_client是i2c slave设备的数据结构
struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; };
i2c设备驱动数据结构,是i2c_client设备的驱动。
struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared. You should avoid * using this, it will be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *) __deprecated; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; };
i2c_driver需要在i2c设备驱动中进行实现。
3 控制器驱动
内核源码目录结构及config配置如下:drivers/i2c/
├── algos
│ ├── i2c-algo-bit.c
│ ├── i2c-algo-pca.c
│ ├── i2c-algo-pcf.c
│ ├── i2c-algo-pcf.h
│ ├── Kconfig
│ └── Makefile
├── busses
│ ├── i2c-gpio.c
│ ├── i2c-vendor.c
│ ├── Kconfig
│ ├── Makefile
├── i2c-boardinfo.c
├── i2c-core.c
├── i2c-core.h
├── i2c-dev.c
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
├── Kconfig
└── Makefile
obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-y += algos/ busses/ muxes/ obj-$(CONFIG_I2C_VENDORNAME) += i2c-vendor.o //控制器驱动,隐藏了厂商名 obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o//gpio模拟
控制器驱动部分包括控制器设备注册,控制器驱动注册,总线传输和用户层接口四个部分,下面分别对它们进行说明。
3.1 控制器设备注册
该部分代码在dt解析中实现,请参见kernel初始化时dt解析的代码。i2c controller是platform device。dts中定义如下:
i2c2: i2c@50d00000 { compatible = "vendor-i2c"; reg = <0 0x70d00000 0 0x1000>; interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>; clock-names = "i2c"; clocks = <&clk_i2c0>; clock-frequency = <400000>; };
3.2 控制器驱动注册
一般SOC厂商都会自己定义专有控制器结构体,不同厂商的数据结构成员不同,但肯定与自身控制器的使用密切相关的内容,比如内存基地址,clock,中断号等。linux驱动与设备是一对多的关系,在i2c_adapter设备注册时,控制器的结构体信息一般会提供给i2c_adapter作为私有数据。,本文中的结构体如下:struct vendor_i2c { struct i2c_msg *msg; struct i2c_adapter adap; void __iomem *membase; struct clk *clk; unsigned int src_clk; int irq; struct vendor_platform_i2c *pdata; };
static struct of_device_id vendor_i2c_of_match[] = { { .compatible = "vendor-i2c", }, }; static struct platform_driver vendor_i2c_driver = { .probe = vendor_i2c_probe, .remove = vendor_i2c_remove, .driver = { .owner = THIS_MODULE, .name = "vendor-i2c", .of_match_table = of_match_ptr(vendor_i2c_of_match), .pm = &vendor_i2c_pm_ops, }, }; static int __init vendor_i2c_init(void) { return platform_driver_register(&vendor_i2c_driver); } arch_initcall_sync(vendor_i2c_init);
i2c controller是platform_device,因此驱动通过platform_driver_register进行注册。由于compatible属性为”vendor-i2c”的platform device已经在3.1中注册完成,vendor_i2c_probe会被加载执行。
static int vendor_i2c_probe(struct platform_device *pdev) { struct vendor_i2c *pi2c; struct resource *res; struct device_node *np = pdev->dev.of_node; ... pi2c = devm_kzalloc(&pdev->dev, sizeof(struct vendor_i2c), GFP_KERNEL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //dt中reg资源 //i2c_adapter数据结构的填充 i2c_set_adapdata(&pi2c->adap, pi2c); //adapter->dev->drvdata=pi2c snprintf(pi2c->adap.name, sizeof(pi2c->adap.name), "%s", "vendor-i2c"); pi2c->adap.owner = THIS_MODULE; pi2c->adap.retries = 3; pi2c->adap.algo = &vendor_i2c_algo; pi2c->adap.algo_data = pi2c; pi2c->adap.dev.parent = &pdev->dev; pi2c->adap.nr = pdev->id; pi2c->adap.dev.of_node = pdev->dev.of_node; pi2c->membase = devm_ioremap_nocache(&pdev->dev, res->start, res->end - res->start); //clock相关 pi2c->pdata = devm_kzalloc(&pdev->dev, sizeof(struct vendor_platform_i2c), GFP_KERNEL); memcpy(pi2c->pdata, &vendor_platform_i2c_default, sizeof(struct vendor_platform_i2c)); if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", &prop)) pi2c->pdata->bus_freq = prop; ret = vendor_i2c_clk_init(pi2c); clk_prepare_enable(pi2c->clk); vendor_i2c_enable(pi2c); clk_disable(pi2c->clk); ret = i2c_add_numbered_adapter(&pi2c->adap); //adapter的注册过程在下面进行了展开 ... }
其中,最后的adapter注册如下代码,在i2c-core.c实现。
int i2c_add_numbered_adapter(struct i2c_adapter *adap) { return __i2c_add_numbered_adapter(adap); } int i2c_add_numbered_adapter(struct i2c_adapter *adap) { return i2c_register_adapter(adap); } static int i2c_register_adapter(struct i2c_adapter *adap) { ... dev_set_name(&adap->dev, "i2c-%d", adap->nr); //adapter->dev的name是i2c-0,1,2等,设备注册后udev会在/dev下创建对应的设备名节点 adap->dev.bus = &i2c_bus_type; //i2c_bus_type对adpater作用?? adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); //adapter设备注册 exit_recovery: /* create pre-declared device nodes */ of_i2c_register_devices(adap); //(1) acpi_i2c_register_devices(adap); acpi_i2c_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); //(2) }
(1)通过对dt进行解析,对挂在i2c controller下的i2c从设备进行注册。i2c从设备在linux中是通过i2c_client数据结构进行描述的,因此注册过程主要是对i2c_client数据结构的填充并将它注册到内核中。dt中的描述如下,本例中的i2c从设备是BOSCH的加速度传感器:
&i2c2 { status = "okay"; clock-frequency = <400000>; bma2x2@19{ compatible = "BOSCH,bma2x2"; reg = <0x19>; gpios = <&ap_gpio 92 0>; }; }
继续i2c_client设备的注册,代码如下:
static void of_i2c_register_devices(struct i2c_adapter *adap) { struct device_node *node; /* Only register child devices if the adapter has a node pointer set */ if (!adap->dev.of_node) return; dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); for_each_available_child_of_node(adap->dev.of_node, node) { if (of_node_test_and_set_flag(node, OF_POPULATED)) continue; of_i2c_register_device(adap, node); //调用of_i2c_register_device进行设备注册,下面有展开 } } static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, struct device_node *node) { struct i2c_client *result; struct i2c_board_info info = {}; struct dev_archdata dev_ad = {}; const __be32 *addr_be; u32 addr; int len; dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { //解析dt中设备compiatble属性填充info.type dev_err(&adap->dev, "of_i2c: modalias failure on %s\n", node->full_name); return ERR_PTR(-EINVAL); } addr_be = of_get_property(node, "reg", &len); //从设备地址 if (!addr_be || (len < sizeof(*addr_be))) { dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", node->full_name); return ERR_PTR(-EINVAL); } addr = be32_to_cpup(addr_be); if (addr & I2C_TEN_BIT_ADDRESS) { addr &= ~I2C_TEN_BIT_ADDRESS; info.flags |= I2C_CLIENT_TEN; } if (addr & I2C_OWN_SLAVE_ADDRESS) { addr &= ~I2C_OWN_SLAVE_ADDRESS; info.flags |= I2C_CLIENT_SLAVE; } if (i2c_check_addr_validity(addr, info.flags)) { dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", info.addr, node->full_name); return ERR_PTR(-EINVAL); } info.addr = addr; info.of_node = of_node_get(node); info.archdata = &dev_ad; if (of_get_property(node, "wakeup-source", NULL)) info.flags |= I2C_CLIENT_WAKE; result = i2c_new_device(adap, &info); //注册i2c device,在下面会进行展开 if (result == NULL) { dev_err(&adap->dev, "of_i2c: Failure registering %s\n", node->full_name); of_node_put(node); return ERR_PTR(-EINVAL); } return result; } struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return NULL; client->adapter = adap; //client对应的adapter,建立client与adapter的对应关系 client->dev.platform_data = info->platform_data; if (info->archdata) client->dev.archdata = *info->archdata; client->flags = info->flags; client->addr = info->addr; client->irq = info->irq; strlcpy(client->name, info->type, sizeof(client->name)); //client->name的赋值,它来源于dt中的slave设备的compatible属性,后面设备与驱动的probe主要是通过判断此设备成员和设备驱动的id_table成员是否一致 status = i2c_check_addr_validity(client->addr, client->flags); if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; } /* Check for address business */ status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); if (status) goto out_err; client->dev.parent = &client->adapter->dev; //client设备与adapter设备的关系 client->dev.bus = &i2c_bus_type; //client设备的总线类型是i2c_bus_type,client与driver的probe就是通过i2c_bus_type的probe实现的 client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client); status = device_register(&client->dev); //设备注册 if (status) goto out_err; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, status); out_err_silent: kfree(client); return NULL; }
(2) 扫描 __i2c_board_list,对已经添加到此list的i2c设备进行注册。在device tree应用之前,i2c从设备通过调用i2c_register_board_info将自己添加到 __i2c_board_list,目前此中方法已很少使用。
static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo *devinfo; down_read(&__i2c_board_lock); list_for_each_entry(devinfo, &__i2c_board_list, list) { if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info)) //设备注册 dev_err(&adapter->dev, "Can't create device at 0x%02x\n", devinfo->board_info.addr); } up_read(&__i2c_board_lock); }
下面是在kernel的mach目录下找到的一个以此种方式添加到list中的例子。
static struct i2c_board_info i2c_devs[] __initdata = { { I2C_BOARD_INFO("wm8753", 0x1A), }, { I2C_BOARD_INFO("24c08", 0x50), }, }; static void __init smdk6400_machine_init(void) { i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs)); }
至此,在控制器驱动中不仅对i2c_adapter设备进行了注册,还对i2c_client设备进行了注册,并建立了adapter和client之间的关系,而且它们都是i2c_bus_type总线类型的设备。
此处要强调一下, i2c控制器是platform device,它的驱动注册是通过platform_bus, platform_device和platform_driver之间的关系进行probe的,与i2c_bus_type不要混淆。
3.3 总线传输
在controller驱动中还对i2c另一重要结构体进行了赋值,它是发送和接收数据的实现函数。pi2c->adap.algo = &vendor_i2c_algo;
static const struct i2c_algorithm vendor_i2c_algo = { .master_xfer = vendor_i2c_master_xfer, .functionality = vendor_i2c_func, }; static int vendor_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { int im = 0, ret = 0; struct vendor_i2c *pi2c = i2c_adap->algo_data; clk_enable(pi2c->clk); for (im = 0; ret >= 0 && im != num; im++) { ret = vendor_i2c_handle_msg(i2c_adap, &msgs[im], im == num - 1); } clk_disable(pi2c->clk); return (ret >= 0) ? im : -1; } static int vendor_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg, int is_last_msg) { int rc; struct vendor_i2c *pi2c = i2c_adap->algo_data; rc = vendor_i2c_send_target_addr(pi2c, pmsg); if ((pmsg->flags & I2C_M_RD)) return vendor_i2c_readbytes(pi2c, pmsg->buf, pmsg->len); else return vendor_i2c_writebytes(pi2c, pmsg->buf, pmsg->len, is_last_msg); }
vendor_i2c_send_target_addr, vendor_i2c_readbytes, vendor_i2c_writebytes等依赖于具体厂商的实现。
3.4 用户层接口
此外,i2c-dev.c中定义了与应用层交互接口的ops,用户可以直接打开/dev/i2c-0或其它设备节点,使用文件描述符进行读写等操作,不必关心底层实现。static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, };
读写操作最终都是通过i2c-core提供的i2c_transfer函数实现的,i2c_transfer又是通过调用adap->algo->master_xfer实现的,此处不再进行展开。
4 设备驱动
i2c设备驱动,即i2c master对应的slave设备的驱动,在前面我们已经提到controller驱动中已经对slave设备i2c_client进行了注册,此处是对i2c_client设备对应的驱动i2c_driver进行注册。此例中的i2c_driver使用传感器的驱动bma2x2_driver,注册代码如下。
#define SENSOR_NAME "bma2x2" static const struct i2c_device_id bma2x2_id[] = { { SENSOR_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, bma2x2_id); static const struct of_device_id bma2x2_of_match[] = { { .compatible = "BOSCH,bma2x2", }, { } }; MODULE_DEVICE_TABLE(of, bma2x2_of_match); static struct i2c_driver bma2x2_driver = { .driver = { .owner = THIS_MODULE, .name = SENSOR_NAME, .of_match_table = bma2x2_of_match, .pm = &bma2x2_pm_ops }, .id_table = bma2x2_id, .probe = bma2x2_probe, .remove = bma2x2_remove, .shutdown = bma2x2_shutdown, }; static int __init BMA2X2_init(void) { return i2c_add_driver(&bma2x2_driver); } static void __exit BMA2X2_exit(void) { i2c_del_driver(&bma2x2_driver); } module_init(BMA2X2_init); module_exit(BMA2X2_exit);
驱动注册i2c_add_driver函数的调用过程中会调用dev->bus->probe,其中dev是通过遍历所有dev设备得到的。当i2c_client设备注册完成后,client->dev.bus = &i2c_bus_type,因此,此处调用的bus->probe就是i2c_bus_type.probe函数指针指向的函数。
i2c_add_driver i2c_register_driver(THIS_MODULE, driver) res = driver_register(&driver->driver); ret = bus_add_driver(drv); driver_attach(drv); bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); __driver_attach driver_probe_device(drv, dev); ret = really_probe(dev, drv); dev->bus->probe(dev);
i2c_bus_type在i2c-core.c中定义如下:
struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, .pm = &i2c_device_pm_ops, }; static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; if (!device_can_wakeup(&client->dev)) device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); status = of_clk_set_defaults(dev->of_node, false); if (status < 0) return status; status = dev_pm_domain_attach(&client->dev, true); if (status != -EPROBE_DEFER) { status = driver->probe(client, i2c_match_id(driver->id_table, client)); //driver->probe的调用依赖于i2c_match_id的结果 if (status) dev_pm_domain_detach(&client->dev, true); } return status; } static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { while (id->name[0]) { if (strcmp(client->name, id->name) == 0) return id; id++; } return NULL; }
i2c_match_id的实现就是在比对client->name和id->name。client->name在controller驱动中注册i2c_client设备的过程中有过赋值,它来自于dt中slave设备的compatible属性。id_name在设备驱动中定义,本例中就是宏定义SENSOR_NAME,即 “bma2x2”,与dt中slave中的compatible属性逗号后面的字符串一致。因此driver->probe即 bma2x2_probe会被调用。
5 总结
理解linux i2c框架就要理清如下关系:1. bus, device, device_driver的驱动模型。
2. controller和slave设备的关系。
3. i2c_client与i2c_driver的关系。
4. core层的价值。
相关文章推荐
- Linux I2C核心、总线与设备驱动
- Linux下I2C设备驱动开发和实现
- Linux I2C核心、总线与设备驱动
- Linux设备驱动简析—基于I2C的E2PROM驱动
- Linux设备驱动之I2C总线适配器驱动分析
- 转:Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux设备驱动之I2C架构分析
- Linux下I2C设备驱动开发和实现
- linux分类驱动对字符设备框架压力的卸载
- Linux下PCI设备驱动程序开发 --- linux 驱动框架(二)
- linux i2c设备驱动
- Linux I2C Input设备驱动代码的几点理解
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- ARM+LINUX字符型设备驱动编写框架
- Linux下I2C设备驱动开发和实现
- Linux下I2C设备驱动开发和实现
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动