生活随笔
收集整理的這篇文章主要介紹了
linux powerpc i2c驱动 之 i2c设备层的注册过程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Linux下i2c驅動的加載過程,分為i2c設備層、i2c?adapter層與i2c核心層
i2c設備驅動層也就是我們為特定i2c設備編寫的驅動,下面是我自己理解的i2c驅動的注冊過程
在我們寫的i2c設備驅動中,我們會調用i2c_add_driver()開始i2c設備驅動的注冊,該函數調用i2c_register_driver完成所有注冊操作
?
?
[plain] view plaincopyprint?
static?inline?int?i2c_add_driver(struct?i2c_driver?*driver)??{??return?i2c_register_driver(THIS_MODULE,?driver);??}??
i2c_register_driver會調用driver_register() 來將設備驅動添加到總線的設備驅動鏈表中:
[plain] view plaincopyprint?
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;??driver->driver.owner?=?owner;??driver->driver.bus?=?&i2c_bus_type;??/*?When?registration?returns,?the?driver?core???*?will?have?called?probe()?for?all?matching-but-unbound?devices.???*/??res?=?<strong>driver_register</strong>(&driver->driver);??if?(res)??return?res;??pr_debug("i2c-core:?driver?[%s]?registered\n",?driver->driver.name);??INIT_LIST_HEAD(&driver->clients);??/*?Walk?the?adapters?that?are?already?present?*/??mutex_lock(&core_lock);??bus_for_each_dev(&i2c_bus_type,?NULL,?driver,?__attach_adapter);??mutex_unlock(&core_lock);??return?0;??}??
在driver_register中,通過driver_find來判斷驅動是否已經注冊,然后會調用
bus_add_drive
將設備驅動添加到總線上
[plain] view plaincopyprint?
int?driver_register(struct?device_driver?*drv)??{??int?ret;??struct?device_driver?*other;??BUG_ON(!drv->bus->p);??if?((drv->bus->probe?&&?drv->probe)?||??????(drv->bus->remove?&&?drv->remove)?||??????(drv->bus->shutdown?&&?drv->shutdown))??printk(KERN_WARNING?"Driver?'%s'?needs?updating?-?please?use?"??"bus_type?methods\n",?drv->name);??other?=?driver_find(drv->name,?drv->bus);??if?(other)?{??put_driver(other);??printk(KERN_ERR?"Error:?Driver?'%s'?is?already?registered,?"??"aborting...\n",?drv->name);??return?-EBUSY;??}??ret?=?<strong>bus_add_driver</strong>(drv);??if?(ret)??return?ret;??ret?=?driver_add_groups(drv,?drv->groups);??if?(ret)??bus_remove_driver(drv);??return?ret;??}??
在bus_add_driver中初始化priv->klist_devices的值,并將priv賦值給drv->p
通過調用klist_add_tail(&priv->knode_bus,?&bus->p->klist_drivers)將驅動信息保存到總線結構中,在此之前將調用driver_attach()
[plain] view plaincopyprint?
int?bus_add_driver(struct?device_driver?*drv)??{??struct?bus_type?*bus;??struct?driver_private?*priv;??int?error?=?0;??bus?=?bus_get(drv->bus);??if?(!bus)??return?-EINVAL;??pr_debug("bus:?'%s':?add?driver?%s\n",?bus->name,?drv->name);??priv?=?kzalloc(sizeof(*priv),?GFP_KERNEL);??if?(!priv)?{??error?=?-ENOMEM;??goto?out_put_bus;??}??<strong>klist_init</strong>(&priv->klist_devices,?NULL,?NULL);??priv->driver?=?drv;??drv->p?=?priv;??priv->kobj.kset?=?bus->p->drivers_kset;??error?=?kobject_init_and_add(&priv->kobj,?&driver_ktype,?NULL,???????"%s",?drv->name);??if?(error)??goto?out_unregister;??if?(drv->bus->p->drivers_autoprobe)?{??error?=?<strong>driver_attach</strong>(drv);??if?(error)??goto?out_unregister;??}??<strong>klist_add_tail</strong>(&priv->knode_bus,?&bus->p->klist_drivers);??module_add_driver(drv->owner,?drv);??error?=?driver_create_file(drv,?&driver_attr_uevent);??if?(error)?{??printk(KERN_ERR?"%s:?uevent?attr?(%s)?failed\n",??__func__,?drv->name);??}??error?=?driver_add_attrs(bus,?drv);??if?(error)?{??/*?How?the?hell?do?we?get?out?of?this?pickle??Give?up?*/??printk(KERN_ERR?"%s:?driver_add_attrs(%s)?failed\n",??__func__,?drv->name);??}??if?(!drv->suppress_bind_attrs)?{??error?=?add_bind_files(drv);??if?(error)?{??/*?Ditto?*/??printk(KERN_ERR?"%s:?add_bind_files(%s)?failed\n",??__func__,?drv->name);??}??}??kobject_uevent(&priv->kobj,?KOBJ_ADD);??return?0;??out_unregister:??kfree(drv->p);??drv->p?=?NULL;??kobject_put(&priv->kobj);??out_put_bus:??bus_put(bus);??return?error;??}??
在driver_attach中,通過調用bus_for_each_dev,遍歷在總線上掛載的所有設備,并對每個設備(dev)調用__driver_attach()
[plain] view plaincopyprint?
int?driver_attach(struct?device_driver?*drv)??{??return?bus_for_each_dev(drv->bus,?NULL,?drv,?<strong>__driver_attach</strong>);??}??
在__driver_attach里會調用driver_match_device()來判斷dev與driv的id是否相同,在i2c驅動里就會調用i2c_bus_type->match程序進行判斷,
當id相同時,將會調用driver_probe_device()
[plain] view plaincopyprint?
static?int?__driver_attach(struct?device?*dev,?void?*data)??{??struct?device_driver?*drv?=?data;??/*???*?Lock?device?and?try?to?bind?to?it.?We?drop?the?error???*?here?and?always?return?0,?because?we?need?to?keep?trying???*?to?bind?to?devices?and?some?drivers?will?return?an?error???*?simply?if?it?didn't?support?the?device.???*???*?driver_probe_device()?will?spit?a?warning?if?there???*?is?an?error.???*/??if?(!<strong>driver_match_device</strong>(drv,?dev))??return?0;??if?(dev->parent)?/*?Needed?for?USB?*/??down(&dev->parent->sem);??down(&dev->sem);??if?(!dev->driver)??<strong>driver_probe_device</strong>(drv,?dev);??up(&dev->sem);??if?(dev->parent)??up(&dev->parent->sem);??return?0;??}??
在driver_probe_device(),首先會調用device_is_registered()判斷dev是否注冊,若沒注冊則返回;若已經注冊,則調用really_probe
[plain] view plaincopyprint?
int?driver_probe_device(struct?device_driver?*drv,?struct?device?*dev)??{??int?ret?=?0;??if?(!device_is_registered(dev))??return?-ENODEV;??pr_debug("bus:?'%s':?%s:?matched?device?%s?with?driver?%s\n",???drv->bus->name,?__func__,?dev_name(dev),?drv->name);??pm_runtime_get_noresume(dev);??pm_runtime_barrier(dev);??ret?=?<strong>really_probe</strong>(dev,?drv);??pm_runtime_put_sync(dev);??return?ret;??}??
在really_probe()里,首先將drv賦值給dev->driver,然后會調用總線的probe函數,在i2c驅動里,
此時將會調用i2c總線的probe函數:i2c_device_probe
[plain] view plaincopyprint?
static?int?really_probe(struct?device?*dev,?struct?device_driver?*drv)??{??int?ret?=?0;??atomic_inc(&probe_count);??pr_debug("bus:?'%s':?%s:?probing?driver?%s?with?device?%s\n",???drv->bus->name,?__func__,?drv->name,?dev_name(dev));??WARN_ON(!list_empty(&dev->devres_head));??dev->driver?=?drv;??if?(driver_sysfs_add(dev))?{??printk(KERN_ERR?"%s:?driver_sysfs_add(%s)?failed\n",??__func__,?dev_name(dev));??goto?probe_failed;??}??if?(dev->bus->probe)???{??//此處調用i2c總線的probe函數??ret?=?dev->bus->probe(dev);??if?(ret)??goto?probe_failed;??}???else?if?(drv->probe)???{??ret?=?drv->probe(dev);??if?(ret)??goto?probe_failed;??}??driver_bound(dev);??ret?=?1;??pr_debug("bus:?'%s':?%s:?bound?device?%s?to?driver?%s\n",?drv->bus->name,?__func__,?dev_name(dev),?drv->name);??goto?done;??probe_failed:??devres_release_all(dev);??driver_sysfs_remove(dev);??dev->driver?=?NULL;??if?(ret?!=?-ENODEV?&&?ret?!=?-ENXIO)???{??/*?driver?matched?but?the?probe?failed?*/??printk(KERN_WARNING???????"%s:?probe?of?%s?failed?with?error?%d\n",???????drv->name,?dev_name(dev),?ret);??}??/*?*?Ignore?errors?returned?by?->probe?so?that?the?next?driver?can?try?*?its?luck.?*/??ret?=?0;??done:??atomic_dec(&probe_count);??wake_up(&probe_waitqueue);??return?ret;??}??
在i2c_device_probe()里,會根據to_i2c_driver(dev->driver)獲取i2c驅動,也就是我們編寫的具體的i2c設備驅動的結構體i2c_driver,即
[plain] view plaincopyprint?
static?struct?i2c_driver?XXX_driver?=?{??.driver?=?{??.name?=?"XXXX_name",??.owner?=?THIS_MODULE,??},??.probe?=?XXX_probe,??.remove?=?XXX_remove,??.id_table?=?XXX_id,??};??
這樣就調用了我們驅動的probe()了,這就是我們在驅動里調用i2c_add_driver(),通過driver_register()的一系列調用,最后執行我們所寫的probe()
[plain] view plaincopyprint?
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;??client->driver?=?driver;??if?(!device_can_wakeup(&client->dev))??device_init_wakeup(&client->dev,??client->flags?&?I2C_CLIENT_WAKE);??dev_dbg(dev,?"probe\n");??status?=?<strong>driver->probe</strong>(client,?i2c_match_id(driver->id_table,?client));//執行我們寫的probe函數??if?(status)??client->driver?=?NULL;??return?status;??}??
總結
以上是生活随笔為你收集整理的linux powerpc i2c驱动 之 i2c设备层的注册过程的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。