1. I2C驅動框架
I2C設備驅動的層次分爲設備層、總線層。理解這兩個層次的重點是理解4個數據結構,分別爲i2c_driver,i2c_client、i2c_algorithm,i2c_adapter。
i2c_driver和i2c_client是設備層,i2c_algorithm和i2c_adapter是總線層。
- 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 */ //內嵌的device結構
- int nr;
- char name[48];
- struct completion dev_released;
- struct mutex userspace_clients_lock;
- struct list_head userspace_clients; //此總線上的設備鏈表
- };
- struct i2c_driver {
- unsigned int class;
- /* Notifies the driver that a new bus has appeared or is about to be
- * removed. You should avoid using this, it will be removed in a
- * near future.
- */
- int (*attach_adapter)(struct i2c_adapter *) __deprecated;
- int (*detach_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;
- };
- 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 i2c_driver *driver; /* and our access routines */ //設備的驅動
- struct device dev; /* the device structure */< //內嵌的device結構
- int irq; /* irq issued by device */
- struct list_head detected; //已經被發現的設備鏈表
- };
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 */ //內嵌的device結構
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients; //此總線上的設備鏈表
};
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this, it will be removed in a
* near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_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;
};
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 i2c_driver *driver; /* and our access routines */ //設備的驅動
struct device dev; /* the device structure */< //內嵌的device結構
int irq; /* irq issued by device */
struct list_head detected; //已經被發現的設備鏈表
};
2. 設備側驅動的註冊
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
- /* add the driver to the list of i2c drivers in the driver core */
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type; //i2c_driver的總線類型爲i2c
- /* When registration returns, the driver core
- * will have called probe() for all matching-but-unbound devices.
- */
- res = driver_register(&driver->driver); //註冊i2c_driver內嵌的driver
- if (res)
- return res;
- /* Drivers should switch to dev_pm_ops instead. */
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
- /* Walk the adapters that are already present */
- res = i2c_for_each_dev(driver, __process_new_driver); //掃描i2c總線掛載的i2c_client設備和i2c_adapte設備,不過只有adapter設備才能執行__process_new_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; //i2c_driver的總線類型爲i2c
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver); //註冊i2c_driver內嵌的driver
if (res)
return res;
/* Drivers should switch to dev_pm_ops instead. */
if (driver->suspend)
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
driver->driver.name);
if (driver->resume)
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
driver->driver.name);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
res = i2c_for_each_dev(driver, __process_new_driver); //掃描i2c總線掛載的i2c_client設備和i2c_adapte設備,不過只有adapter設備才能執行__process_new_driver
下面先分析下driver_register的執行流程:
driver_register
driver_find
bus_add_driver
driver_attach
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
__driver_attach
driver_match_device
drv->bus->match(dev, drv)
i2c_device_match
driver_probe_device
really_probe
dev->bus->probe(dev)
i2c_device_probe
driver->probe(client, i2c_match_id(driver->id_table, client))
再來分析下__process_new_driver
- static int __process_new_driver(struct device *dev, void *data)
- {
- if (dev->type != &i2c_adapter_type)
- {
- //printk("-----dev type is not i2c adapter type\n");
- return 0;
- }
- //printk("-----i2c do add adapter\n");
- return i2c_do_add_adapter(data, to_i2c_adapter(dev));
- }
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type)
{
//printk("-----dev type is not i2c adapter type\n");
return 0;
}
//printk("-----i2c do add adapter\n");
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
- static int i2c_do_add_adapter(struct i2c_driver *driver,
- struct i2c_adapter *adap)
- {
- /* Detect supported devices on that bus, and instantiate them */
- i2c_detect(adap, driver); //if (!driver->detect || !address_list),如果detect函數和address_list沒有定義就直接返回。
- /* Let legacy drivers scan this bus for matching devices */
- if (driver->attach_adapter) { //如果沒定義就不執行
- dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
- driver->driver.name);
- dev_warn(&adap->dev, "Please use another way to instantiate "
- "your i2c_client\n");
- /* We ignore the return code; if it fails, too bad */
- driver->attach_adapter(adap);
- }
- return 0;
- }
static int i2c_do_add_adapter(struct i2c_driver *driver,
struct i2c_adapter *adap)
{
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver); //if (!driver->detect || !address_list),如果detect函數和address_list沒有定義就直接返回。
/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) { //如果沒定義就不執行
dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
driver->driver.name);
dev_warn(&adap->dev, "Please use another way to instantiate "
"your i2c_client\n");
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}
3. 總線側驅動的註冊
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- if (!i2c) {
- dev_err(&pdev->dev, "no memory for state\n");
- return -ENOMEM;
- }
- strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
- i2c->adap.owner = THIS_MODULE;
- i2c->adap.algo = &s3c24xx_i2c_algorithm;
- i2c->adap.retries = 2;
- i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- i2c->tx_setup = 50;
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- /* find the clock and enable it */
- i2c->dev = &pdev->dev;
- i2c->clk = clk_get(&pdev->dev, "i2c");
- if (IS_ERR(i2c->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
- ret = -ENOENT;
- goto err_noclk;
- }
- dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
- clk_enable(i2c->clk);
- /* map the registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
- ret = -ENOENT;
- goto err_clk;
- }
- i2c->ioarea = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (i2c->ioarea == NULL) {
- dev_err(&pdev->dev, "cannot request IO\n");
- ret = -ENXIO;
- goto err_clk;
- }
- i2c->regs = ioremap(res->start, resource_size(res));
- if (i2c->regs == NULL) {
- dev_err(&pdev->dev, "cannot map IO\n");
- ret = -ENXIO;
- goto err_ioarea;
- }
- dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
- i2c->regs, i2c->ioarea, res);
- /* setup info block for the i2c core */
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- if (ret != 0)
- goto err_iomap;
- /* find the IRQ for this unit (note, this relies on the init call to
- * ensure no current IRQs pending
- */
- i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
- goto err_iomap;
- }
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), i2c);
- if (ret != 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- goto err_iomap;
- }
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
- goto err_irq;
- }
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
- i2c->adap.nr = pdata->bus_num;
- ret = i2c_add_numbered_adapter(&i2c->adap);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- goto err_cpufreq;
- }
- platform_set_drvdata(pdev, i2c);
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- clk_disable(i2c->clk);
- return 0;
- err_cpufreq:
- s3c24xx_i2c_deregister_cpufreq(i2c);
- err_irq:
- free_irq(i2c->irq, i2c);
- err_iomap:
- iounmap(i2c->regs);
- err_ioarea:
- release_resource(i2c->ioarea);
- kfree(i2c->ioarea);
- err_clk:
- clk_disable(i2c->clk);
- clk_put(i2c->clk);
- err_noclk:
- kfree(i2c);
- return ret;
- }
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
/* find the clock and enable it */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
i2c->regs = ioremap(res->start, resource_size(res));
if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_iomap;
}
ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
}
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
* the platform data, so if unset it will now default to always
* being bus 0.
*/
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
}
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
clk_disable(i2c->clk);
return 0;
err_cpufreq:
s3c24xx_i2c_deregister_cpufreq(i2c);
err_irq:
free_irq(i2c->irq, i2c);
err_iomap:
iounmap(i2c->regs);
err_ioarea:
release_resource(i2c->ioarea);
kfree(i2c->ioarea);
err_clk:
clk_disable(i2c->clk);
clk_put(i2c->clk);
err_noclk:
kfree(i2c);
return ret;
}
先來看下i2c_add_numbered_adapter的執行流程
i2c_add_numbered_adapter
i2c_register_adapter
device_register //註冊i2c_adapter內嵌的device
i2c_scan_static_board_info
list_for_each_entry(devinfo, &__i2c_board_list, list)
i2c_new_device
device_register //註冊i2c_device內嵌的device
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)
__process_new_adapter
i2c_do_add_adapter(to_i2c_driver(d), data)
所以i2c_add_numbered_adapter也走到了i2c_do_add_adapter。
初始化流程總結:
1. setup_arch時把靜態定義的struct i2c_board_info結構,使用i2c_register_board_info函數註冊到全局__i2c_board_list中。
2. 適配層 platform_driver的註冊,就是分配i2c_adapter結構,初始化,然後掃描__i2c_board_list中的鏈表,並且分配i2c_device結構。
3. 設備側i2c_driver的註冊,如果找到i2c總線上註冊的i2c_device結構,就觸發i2c_driver裏面的probe。
4. 不管是註冊i2c_adapter還是i2c_driver,最後都會觸發到i2c_do_add_adapter。
4. i2c-dev的分析
- static struct notifier_block i2cdev_notifier = {
- .notifier_call = i2cdev_notifier_call,
- };
- static int __init i2c_dev_init(void)
- {
- int res;
- printk(KERN_INFO "i2c /dev entries driver\n");
- res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
- if (res)
- goto out;
- i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
- if (IS_ERR(i2c_dev_class)) {
- res = PTR_ERR(i2c_dev_class);
- goto out_unreg_chrdev;
- }
- /* Keep track of adapters which will be added or removed later */
- res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
- if (res)
- goto out_unreg_class;
- /* Bind to already existing adapters right away */
- i2c_for_each_dev(NULL, i2cdev_attach_adapter);
- return 0;
- out_unreg_class:
- class_destroy(i2c_dev_class);
- out_unreg_chrdev:
- unregister_chrdev(I2C_MAJOR, "i2c");
- out:
- printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
- return res;
- }
- int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
- void *data)
- {
- struct device *dev = data;
- switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
- return i2cdev_attach_adapter(dev, NULL);
- case BUS_NOTIFY_DEL_DEVICE:
- return i2cdev_detach_adapter(dev, NULL);
- }
- return 0;
- }
static struct notifier_block i2cdev_notifier = {
.notifier_call = i2cdev_notifier_call,
};
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
return i2cdev_attach_adapter(dev, NULL);
case BUS_NOTIFY_DEL_DEVICE:
return i2cdev_detach_adapter(dev, NULL);
}
return 0;
}
所以每當有i2c_adapter要註冊時,都會觸發i2cdev_attach_adapter函數。
- static int i2cdev_attach_adapter(struct device *dev, void *dummy)
- {
- struct i2c_adapter *adap;
- struct i2c_dev *i2c_dev;
- int res;
- if (dev->type != &i2c_adapter_type)
- return 0;
- adap = to_i2c_adapter(dev);
- i2c_dev = get_free_i2c_dev(adap);
- if (IS_ERR(i2c_dev))
- return PTR_ERR(i2c_dev);
- /* register this i2c device with the driver core */
- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
- MKDEV(I2C_MAJOR, adap->nr), NULL,
- "i2c-%d", adap->nr);
- if (IS_ERR(i2c_dev->dev)) {
- res = PTR_ERR(i2c_dev->dev);
- goto error;
- }
- res = device_create_file(i2c_dev->dev, &dev_attr_name);
- if (res)
- goto error_destroy;
- pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
- adap->name, adap->nr);
- //printk("-------i2c-dev: adapter [%s] registered as minor %d\n",
- //adap->name, adap->nr);
- return 0;
- error_destroy:
- device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
- error:
- return_i2c_dev(i2c_dev);
- return res;
- }
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
res = device_create_file(i2c_dev->dev, &dev_attr_name);
if (res)
goto error_destroy;
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
//printk("-------i2c-dev: adapter [%s] registered as minor %d\n",
//adap->name, adap->nr);
return 0;
error_destroy:
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
return res;
}
總結:i2c驅動框架目前只是分析了初始化過程,i2c_client,i2c_driver,i2c_adapter的創建過程,它們之間的關係。具體的通信過程有時間再分析。