platform_device与platform_driver

platform_device与platform_driver,两者的工作顺序是先定义platform_device -> 注册 platform_device->,再定义 platform_driver-> 注册platform_driver。

 (1)platform_device设备的注册过程必须在相应设备驱动加载之前被调用,因为驱动注册时需要匹配内核中所以已注册的设备名。platform_device 是在系统启动时在init.c 里的s3c_arch_init() 函数里进行注册的。这个函数申明为arch_initcall(s3c_arch_init); 会在系统初始化阶段被调用。arch_initcall 的优先级高于module_init,所以会在Platform 驱动注册之前调用。现在内核中不是采用arch_initcall(s3c_arch_init) 注册platform_device 结构体而是通过.init_machine成员将其保存在arch_initcall(customize_machine)等待调用(在mach-smdk6410.c中定义的MACHINE_START到MACHINE_END);其实质是一样的均放在.initcall3.init等待调用。之后再定义结构体struct platform_driver,在驱动初始化函数中调用函数platform_driver_register() 注册 platform_driver。详细过程描述如下:

      Linux从2.6版本开始引入了platform这个概念,在开发底层驱动程序时,首先要确认的就是设备的资源信息,在2.6内核中将每个设备的资源用结构platform_device来描述,该结构体定义在kernel/include/linux/platform_device.h中,

  1. struct platform_device  
  2.   
  3. {  
  4.       const char * name;  
  5.       u32  id;  
  6.       struct device dev;  
  7.       u32  num_resources;  
  8.       struct resource * resource;  
  9. };  


该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel/include/linux/ioport.h中,
比如:

  1. struct resource   
  2.   
  3. {  
  4.        const char *name;  
  5.        unsigned long start, end;  
  6.        unsigned long flags;  
  7.        struct resource *parent, *sibling, *child;  
  8. };  

实例如:

  1. static struct resource s3c_usb_resource[] = {  
  2.  [0] = {  
  3.        .start = S3C_PA_USBHOST,  
  4.        .end   = S3C_PA_USBHOST + S3C_SZ_USBHOST - 1,  
  5.        .flags = IORESOURCE_MEM,  
  6.      },  
  7.  [1] = {  
  8.        .start = IRQ_UHOST,  
  9.        .end   = IRQ_UHOST,  
  10.        .flags = IORESOURCE_IRQ,  
  11.      }  
  12. };  


以上是6410的USB  HOST分配的资源信息。第1组描述了这个usb host设备所占用的总线地址范围,起始地址和大小由硬件决定,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息;第2组描述了这个usb host设备的中断号,也由硬件设定,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。

      有了resource信息,就可以定义platform_device了:

  1. struct platform_device s3c_device_usb = {  
  2.          .name    = "s3c2410-ohci",  //s3c6410-usb  
  3.          .id    = -1,  
  4.          .num_resources   = ARRAY_SIZE(s3c_usb_resource),  
  5.          .resource   = s3c_usb_resource,  
  6.          .dev              = {  
  7.                  .dma_mask = &s3c_device_usb_dmamask,  
  8.                  .coherent_dma_mask = 0xffffffffUL  
  9.              }  
  10. };  


有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了。系统中的设备资源都可以采用这种方式列举在一起,然后成一个指针数组,如:

static struct platform_device *smdk6410_devices[] __initdata = {

......

 &s3c_device_usbgadget,
 &s3c_device_usb,  //jeff add.

......

}

然后在6410的初始化函数smdk6410_machine_init()中执行:

platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));将所有的device添加进系统。platform_add_devices的好处在于它是一次性的执行多个platform_device_register。

(2) 至于驱动程序需要实现结构体struct platform_driver,也定义在kernel/include/linux/platform_device.h中:

  1. struct platform_driver {  
  2.       int (*probe)(struct platform_device *);  
  3.       int (*remove)(struct platform_device *);  
  4.       void (*shutdown)(struct platform_device *);  
  5.       int (*suspend)(struct platform_device *, pm_message_t state);  
  6.       int (*suspend_late)(struct platform_device *, pm_message_t state);  
  7.       int (*resume_early)(struct platform_device *);  
  8.       int (*resume)(struct platform_device *);  
  9.       struct pm_ext_ops *pm;  
  10.       struct device_driver driver;  
  11. };  


则该处的USB HOST实现是:

  1. static struct platform_driver ohci_hcd_s3c2410_driver = {  
  2.      .probe  = ohci_hcd_s3c2410_drv_probe,  
  3.      .remove  = ohci_hcd_s3c2410_drv_remove,  
  4.      .shutdown = usb_hcd_platform_shutdown,  
  5.      /*.suspend = ohci_hcd_s3c2410_drv_suspend, */  
  6.      /*.resume = ohci_hcd_s3c2410_drv_resume, */  
  7.      .driver  = {  
  8.           .owner = THIS_MODULE,  
  9.           .name = "s3c2410-ohci",  
  10.         },  
  11. };  


      在驱动初始化(ohci-hcd.c的1124行)函数中调用函数platform_driver_register()注册该platform_driver,需要注意的是s3c_device_usb结构中name元素和ohci_hcd_s3c2410_driver 结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的platform_device中元素的name和当前注册的platform_driver的driver.name进行比较,只有找到具备相同名称的platform_device存在后,platform_driver才能注册成功。当注册成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_s3c2410_drv_probe开始探测加载。platform driver中的函数都是以platform device作为参数进入。

(3)为什么两个name的名字必须匹配才能实现device和driver的绑定?(1)在内核初始化时kernel_init()->do_basic_setup()->driver_init()->platform_bus_init()初始化platform_bus(虚拟总线);(2)设备注册的时候platform_device_register()->platform_device_add()->(pdev->dev.bus = &platform_bus_type)把设备挂在虚拟的platform bus下;(3)驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device(),判断drv->bus->match()是否存在并且是否执行成功,此时通过指针执行platform_match,比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行的相应设备的platform_driver->probe(platform_device),注意platform_drv_probe的_dev参数是由bus_for_each_dev的next_device获得)开始真正的探测加载,如果probe成功则绑定该设备到该驱动。

      当进入probe函数后,需要获取设备的资源信息,根据参数type所指定类型,例如IORESOURCE_MEM,来分别获取指定的资源。
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);当然,也可以固定资源类型,如获取资源中的中断号:struct int platform_get_irq(struct platform_device *dev, unsigned int num);

      probe函数一般完成硬件设备使能,struct resource的获取以及虚拟地址的动态映射和具体类型设备的注册(因为平台设备只是一种虚拟的设备类型);remove函数完成硬件设备的关闭,struct resource以及虚拟地址的动态映射的释放和具体类型设备的注销。只要和内核本身运行依赖性不大的外围设备 ( 换句话说只要不在内核运行所需的一个最小系统之内的设备 ), 相对独立的拥有各自独自的资源 (addresses and IRQs) ,都可以用platform_driver 实现。如:lcd,usb,uart 等,都可以用platfrom_driver 写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver 机制,实际上内核实现也是这样的。


展开阅读全文

关于驱动结构体 platform_driver 和 file_operations

04-15

static struct platform_driver hello_driver = rn.probe = hello_probe,rn.remove = hello_remove,rn.driver = rn.name = HELLO_DEV_NAME,rn.owner = THIS_MODULE,rn,rn;rnrnrnstatic struct file_operations hello_fops = rn.owner = THIS_MODULE,rn.open = hello_open, //外部测试调用open打开设备文件时触发rn.release = hello_close, //外部调用close时触发rn.read = hello_read, //外部 write 时触发 rn.write = hello_write, //外部 read 时触发rnrn;rnrn请问上面的怎么用?什么时候触发呢?rnrn下面是两个LED灯的驱动程序 一个用platform_driver 另一个用file_operations 不知有什么区别? 请帮忙解释下 谢谢了。rnrn#include rn#include rn#include rn#include rnrn#include rn#include rn#include rnrn/* our context */rnrnstruct s3c24xx_gpio_led rn struct led_classdev cdev;rn struct s3c24xx_led_platdata *pdata;rn;rnrnstatic inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev)rnrn return platform_get_drvdata(dev);rnrnrnstatic inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev)rnrn return container_of(led_cdev, struct s3c24xx_gpio_led, cdev);rnrnrnstatic void s3c24xx_led_set(struct led_classdev *led_cdev,rn enum led_brightness value)rnrn struct s3c24xx_gpio_led *led = to_gpio(led_cdev);rn struct s3c24xx_led_platdata *pd = led->pdata;rnrn /* there will be a short delay between setting the output andrn * going from output to input when using tristate. */rnrn s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^rn (pd->flags & S3C24XX_LEDF_ACTLOW));rnrn if (pd->flags & S3C24XX_LEDF_TRISTATE)rn s3c2410_gpio_cfgpin(pd->gpio,rn value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);rnrnrnrnstatic int s3c24xx_led_remove(struct platform_device *dev)rnrn struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);rnrn led_classdev_unregister(&led->cdev);rn kfree(led);rnrn return 0;rnrnrnstatic int s3c24xx_led_probe(struct platform_device *dev)rnrn struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;rn struct s3c24xx_gpio_led *led;rn int ret;rnrn led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);rn if (led == NULL) rn dev_err(&dev->dev, "No memory for device\n");rn return -ENOMEM;rn rnrn platform_set_drvdata(dev, led);rnrn led->cdev.brightness_set = s3c24xx_led_set;rn led->cdev.default_trigger = pdata->def_trigger;rn led->cdev.name = pdata->name;rnrn led->pdata = pdata;rnrn /* no point in having a pull-up if we are always driving */rnrn if (pdata->flags & S3C24XX_LEDF_TRISTATE) rn s3c2410_gpio_setpin(pdata->gpio, 0);rn s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);rn else rn s3c2410_gpio_pullup(pdata->gpio, 0);rn s3c2410_gpio_setpin(pdata->gpio, 0);rn s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);rn rnrn /* register our new led device */rnrn ret = led_classdev_register(&dev->dev, &led->cdev);rn if (ret < 0) rn dev_err(&dev->dev, "led_classdev_register failed\n");rn goto exit_err1;rn rnrn return 0;rnrn exit_err1:rn kfree(led);rn return ret;rnrnrnrn#ifdef CONFIG_PMrnstatic int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state)rnrn struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);rnrn led_classdev_suspend(&led->cdev);rn return 0;rnrnrnstatic int s3c24xx_led_resume(struct platform_device *dev)rnrn struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);rnrn led_classdev_resume(&led->cdev);rn return 0;rnrn#elsern#define s3c24xx_led_suspend NULLrn#define s3c24xx_led_resume NULLrn#endifrnrnstatic struct platform_driver s3c24xx_led_driver = rn .probe = s3c24xx_led_probe,rn .remove = s3c24xx_led_remove,rn .suspend = s3c24xx_led_suspend,rn .resume = s3c24xx_led_resume,rn .driver = rn .name = "s3c24xx_led",rn .owner = THIS_MODULE,rn ,rn;rnrnstatic int __init s3c24xx_led_init(void)rnrn return platform_driver_register(&s3c24xx_led_driver);rnrnrnstatic void __exit s3c24xx_led_exit(void)rnrn platform_driver_unregister(&s3c24xx_led_driver);rnrnrnmodule_init(s3c24xx_led_init);rnmodule_exit(s3c24xx_led_exit);rnrnMODULE_AUTHOR("Ben Dooks ");rnMODULE_DESCRIPTION("S3C24XX LED driver");rnMODULE_LICENSE("GPL");rnMODULE_ALIAS("platform:s3c24xx_led");rnrnrnrnrn另一个如下:rnrn #include rn #include rn #include rnrn #define DEVICE_NAME "leds" rnrn static unsigned long led_table [] = rn S3C2410_GPB5, rnrn S3C2410_GPB6, rn S3C2410_GPB7, rn S3C2410_GPB8, rn; rnrnstatic unsigned int led_cfg_table [] = rn S3C2410_GPB5_OUTP, rnrn S3C2410_GPB6_OUTP, rn S3C2410_GPB7_OUTP, rn S3C2410_GPB8_OUTP, rn; rnrnstatic int sbc2440_leds_ioctl( rn struct inode *inode, rn struct file *file, rn unsigned int cmd, rn unsigned long arg) rn rn switch(cmd) rn case 0: rn case 1: rn if (arg > 4) rn return -EINVAL; rn rn s3c2410_gpio_setpin(led_table[arg], !cmd); rn return 0; rn default: rn return -EINVAL; rn rn rnrnstatic struct file_operations dev_fops = rn .owner = THIS_MODULE, rn .ioctl = sbc2440_leds_ioctl, rn; rnrnstatic struct miscdevice misc = rn .minor = MISC_DYNAMIC_MINOR, rn .name = DEVICE_NAME, rn .fops = &dev_fops, rn; rnrnstatic int __init dev_init(void) rn rn int ret; rnrn int i; rnrn for (i = 0; i < 4; i++) rn s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); rn s3c2410_gpio_setpin(led_table[i], 0); rn rnrn ret = misc_register(&misc); rnrn printk (DEVICE_NAME"\tinitialized\n"); rnrn return ret; rn rnrnstatic void __exit dev_exit(void) rn rn misc_deregister(&misc); rn rnrnmodule_init(dev_init); rnmodule_exit(dev_exit); rnMODULE_LICENSE("GPL"); rnMODULE_AUTHOR("FriendlyARM Inc."); rnrnrnrnrnrnrnrnrn 论坛

没有更多推荐了,返回首页