您的位置:首页 > 其它

平台设备驱动分析

2011-11-21 20:15 330 查看
 
通常在Linux中,把SOC系统中集成的独立外设单元(如:I2C、IIS、RTC、看门狗等)都被当作平台设备来处理。在Linux中用platform_device结构体来描述一个平台设备

 

平台设备驱动分析:

   使用内核linux-2.6.22

   分析工具:source insight

 

我们来分析s3c2410-lcd是如何进行平台设备驱动注册的。

 drivers/video/s3c2410fb.c

 

 首先我们找到s3c2410fb.c的入口函数:

  module_init(s3c2410fb_init);

  module_exit(s3c2410fb_cleanup);

 

 然后查找 s3c2410fb:

 int __devinit s3c2410fb_init(void)

 {

  return platform_driver_register(&s3c2410fb_driver);   //在入口函数中注册了&s3c2410fb_driver结构体

 }

 static void __exit s3c2410fb_cleanup(void)

 {

  platform_driver_unregister(&s3c2410fb_driver);

 }

 我们就跳到s3c2410fb_driver结构体:

 

 static struct platform_driver s3c2410fb_driver = {

 .probe  = s3c2410fb_probe,             /*这个函数很重要,它的作用就相当于我们普通字符设备驱动中的init函数*/

 .remove  = s3c2410fb_remove,        /*这个函数的作用就相当于我们普通字符设备驱动中的exit函数里所作的工作*/

 .suspend = s3c2410fb_suspend,

 .resume  = s3c2410fb_resume,

 .driver  = {

  .name = "s3c2410-lcd",     //名字很重要,设备驱动和设备只有在相同的名字情况下,才能够进行匹配。

  .owner = THIS_MODULE,

 },

};

 

 接下来分析.probe  = s3c2410fb_probe, 中的s3c2410fb_probe结构体

 

  static int __init s3c2410fb_probe(struct platform_device *pdev)

{

 struct s3c2410fb_info *info;

 struct fb_info    *fbinfo;

 struct s3c2410fb_hw *mregs;

 int ret;

 int irq;

 int i;

 u32 lcdcon1;

      mach_info = pdev->dev.platform_data;  //mach_info函数非常重要,他会和dev.platform_data中的dev进行匹配。

}

对于这个结构体里面的函数我就不一一分析了

 

我们构造的每一个平台设备结构体都会添加到platform.c(总线驱动中):

          drivers/base/platform.c

找到它的入口函数:

int __init platform_bus_init(void)

{

 int error;

 error = device_register(&platform_bus);

 if (error)

  return error;

 error =  bus_register(&platform_bus_type);

 if (error)

  device_unregister(&platform_bus);

 return error;

}

找到platform_bus_type结构体:

struct bus_type platform_bus_type = {

 .name  = "platform",

 .dev_attrs = platform_dev_attrs,

 .match  = platform_match,

 .uevent  = platform_uevent,

 .suspend = platform_suspend,

 .suspend_late = platform_suspend_late,

 .resume_early = platform_resume_early,

 .resume  = platform_resume,

};

那么我们的设备驱动&s3c2410fb_driver又是如何注册到总线驱动platform.c中的呢?

platform_driver_register(&s3c2410fb_driver);

在platform.c中:

int platform_driver_register(struct platform_driver *drv)

{

 drv->driver.bus = &platform_bus_type;  //将设备驱动加入到平台总线的驱动链表上面

 if (drv->probe)

  drv->driver.probe = platform_drv_probe;

 if (drv->remove)

  drv->driver.remove = platform_drv_remove;

 if (drv->shutdown)

  drv->driver.shutdown = platform_drv_shutdown;

 if (drv->suspend)

  drv->driver.suspend = platform_drv_suspend;

 if (drv->resume)

  drv->driver.resume = platform_drv_resume;

 return driver_register(&drv->driver);

}

drivers/base/driver.c

int driver_register(struct device_driver * drv)

{

 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);

 }

 klist_init(&drv->klist_devices, NULL, NULL);    //

 return bus_add_driver(drv);   //将本drv驱动注册登记到drv->bus所在的总线上

}

我们找到bus_add_driver结构体:

drivers/base/bus.c

int bus_add_driver(struct device_driver *drv)

{

 struct bus_type * bus = get_bus(drv->bus);

 int error = 0;

 

 drv->kobj.kset = &bus->drivers;

 if (drv->bus->drivers_autoprobe) {

  error = driver_attach(drv);  //在此去总线的设备链表上去搜索每一个设备,每搜索一个设备都会去调用__driver_attach;

  if (error)

   goto out_unregister;

 }

 klist_add_tail(&drv->knode_bus, &bus->klist_drivers);  //将自己挂载到驱动链表上面去

 module_add_driver(drv->owner, drv);

 error = driver_add_attrs(bus, drv);

 

}

driver_attach起的作用是什么?

drivers/base/dd.c

int driver_attach(struct device_driver * drv)

{

 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   //查找bus驱动中的每一个drv

}

来看看 __driver_attach函数的作用:

static int __driver_attach(struct device * dev, void * data)

{

 struct device_driver * drv = data;

 

 down(&dev->sem);

 if (!dev->driver)

  driver_probe_device(drv, dev);

 return 0;

}

int driver_attach(struct device_driver * drv)

{

 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

分析driver_probe_device结构体:

int driver_probe_device(struct device_driver * drv, struct device * dev)

{

 if (drv->bus->match && !drv->bus->match(dev, drv))     //platform_match

  goto done;

 ret = really_probe(dev, drv);    //名字相同,代表匹配成功,成功则调用次函数

done:

 return ret;

}

再来看看really_probe:

static int really_probe(struct device *dev, struct device_driver *drv)

{

 dev->driver = drv;

 if (driver_sysfs_add(dev)) {

 }

 if (dev->bus->probe) {

  ret = dev->bus->probe(dev);

  if (ret)

 } else if (drv->probe) {

  ret = drv->probe(dev);   //platform_drv_probe

  if (ret)

 }

}

 

drivers/base/platform.c

static int platform_match(struct device * dev, struct device_driver * drv)

{

 struct platform_device *pdev = container_of(dev, struct platform_device, dev);

 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}

static int platform_drv_probe(struct device *_dev)

{

 struct platform_driver *drv = to_platform_driver(_dev->driver);

 struct platform_device *dev = to_platform_device(_dev);

 return drv->probe(dev);             //s3c2410fb_probe

}

platform_device注册过程:arch/arm/mach-s3c2410/mach-smdk2410.c

static void __init smdk2410_init(void)

{

 platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));   //添加dev

 smdk_machine_init();

}

 

static struct platform_device *smdk2410_devices[] __initdata = {                 //各种设备

 &s3c_device_usb,

 &s3c_device_lcd,

 &s3c_device_wdt,

 &s3c_device_i2c,

 &s3c_device_iis,

};

drivers/base/platform.c

int platform_device_register(struct platform_device * pdev)

{

 device_initialize(&pdev->dev);

 return platform_device_add(pdev);

}

 

int platform_device_add(struct platform_device *pdev)

{

 if (!pdev->dev.parent)

  pdev->dev.parent = &platform_bus;

 pdev->dev.bus = &platform_bus_type;

 else

  strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

 for (i = 0; i < pdev->num_resources; i++) {

 }

 ret = device_add(&pdev->dev);

}

drivers/base/core.c

int device_add(struct device *dev)

{

 dev = get_device(dev);

 if (dev->bus)

 bus_attach_device(dev);  //开始匹配平台驱动,开始去找控制驱动(lcd控制器驱动)

 if (parent)

  klist_add_tail(&dev->knode_parent, &parent->klist_children);

 if (dev->class) {

  down(&dev->class->sem);

  list_add_tail(&dev->node, &dev->class->devices);

  list_for_each_entry(class_intf, &dev->class->interfaces, node)

   if (class_intf->add_dev)

    class_intf->add_dev(dev, class_intf);

  up(&dev->class->sem);

 }

void bus_attach_device(struct device * dev)

{

 struct bus_type *bus = dev->bus;

 int ret = 0;

 if (bus) {

  dev->is_registered = 1;

  if (bus->drivers_autoprobe)

   ret = device_attach(dev);

  if (ret >= 0)

   klist_add_tail(&dev->knode_bus, &bus->klist_devices);

 }

}

查看device_attach函数

int device_attach(struct device * dev)

{

 int ret = 0;

 down(&dev->sem);

 if (dev->driver) {

  ret = device_bind_driver(dev);

  if (ret == 0)

   ret = 1;

  else {

   dev->driver = NULL;

   ret = 0;

  }

 } else {

  ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

 }

 up(&dev->sem);

 return ret;

}

再看看__device_attach函数:

drivers/base/dd.c

static int __device_attach(struct device_driver * drv, void * data)

{

 struct device * dev = data;

 return driver_probe_device(drv, dev);   //也会调用总线的match函数来实现匹配过程,根据名字进行匹配

}

int driver_probe_device(struct device_driver * drv, struct device * dev)

{

 

 if (drv->bus->match && !drv->bus->match(dev, drv))

  goto done;

 ret = really_probe(dev, drv);

}

 

static int really_probe(struct device *dev, struct device_driver *drv)

{

 dev->driver = drv;

 if (dev->bus->probe) {

  ret = dev->bus->probe(dev);

  if (ret)

   goto probe_failed;

 } else if (drv->probe) {

  ret = drv->probe(dev);                                //platform_drv_probe最终会调用到我们的s3c2410fb_probe  if (ret)

 }

}

就分析到这了,后面有一些因为时间匆忙以后再做详细分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  平台 struct module c null class