linux platform 驱动模型分析

linux platform 驱动模型分析

一. 概述

platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,如s3c2440,它包括处理器内核(arm920t)和系统的外设(lcd接口,nandflash接口等)。linux在引入了platform机制之后,内核假设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。二. platform 总线 1. 在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分析platform总线注册函数[cpp]view plaincopy

    int__initplatform_bus_init(void){interror;early_platform_cleanup();error=device_register(&platform_bus);//总线也是设备,所以也要进行设备的注册if(error)returnerror;error=bus_register(&platform_bus_type);//注册platform_bus_type总线到内核if(error)device_unregister(&platform_bus);returnerror;}

这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。 2. platform_bus_type 总线结构与设备结构 (1) platform总线 设备结构[cpp]view plaincopy

    structdeviceplatform_bus={.init_name="platform",};

platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。 (2) platform总线 总线结构[cpp]view plaincopy

    structbus_typeplatform_bus_type={.name="platform",.dev_attrs=platform_dev_attrs,.match=platform_match,.uevent=platform_uevent,.pm=&platform_dev_pm_ops,};

platform_dev_attrs 设备属性 platform_match match函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配工作。 platform_uevent 热插拔操作函数三. platform 设备 1. platform_device 结构[cpp]view plaincopy

    structplatform_device{constchar*name;intid;structdevicedev;u32num_resources;structresource*resource;structplatform_device_id*id_entry;/*archspecificadditions*/structpdev_archdataarchdata;};

(1)platform_device结构体中有一个struct resource结构,是设备占用系统的资源,定义在ioport.h中,如下[cpp]view plaincopy

    structresource{resource_size_tstart;resource_size_tend;constchar*name;unsignedlongflags;structresource*parent,*sibling,*child;};

(2) num_resources 占用系统资源的数目,一般设备都占用两种资源,io内存和中断信号线。这个为两种资源的总和。 2. 设备注册函数 platform_device_register[cpp]view plaincopy

    intplatform_device_register(structplatform_device*pdev){device_initialize(&pdev->dev);returnplatform_device_add(pdev);}

这个函数首先初始化了platform_device的device结构,然后调用platform_device_add,这个是注册函数的关键,下面分析platform_device_add:[cpp]view plaincopy

    intplatform_device_add(structplatform_device*pdev){inti,ret=0;if(!pdev)return-EINVAL;if(!pdev->dev.parent)pdev->dev.parent=&platform_bus;//可以看出,platform设备的父设备一般都是platform_bus,所以注册后的platform设备都出现在/sys/devices/platform_bus下pdev->dev.bus=&platform_bus_type;//挂到platform总线上if(pdev->id!=-1)dev_set_name(&pdev->dev,"%s.%d",pdev->name,pdev->id);elsedev_set_name(&pdev->dev,"%s",pdev->name);//设置设备名字,这个名字与/sys/devices/platform_bus下的名字对应for(i=0;i<pdev->num_resources;i++){//下面操作设备所占用的系统资源structresource*p,*r=&pdev->resource[i];if(r->name==NULL)r->name=dev_name(&pdev->dev);p=r->parent;if(!p){if(resource_type(r)==IORESOURCE_MEM)p=&iomem_resource;elseif(resource_type(r)==IORESOURCE_IO)p=&ioport_resource;}if(p&&insert_resource(p,r)){printk(KERN_ERR"%s:failedtoclaimresource%d\n",dev_name(&pdev->dev),i);ret=-EBUSY;gotofailed;}}//上面主要是遍历设备所占用的资源,找到对应的父资源,如果没有定义,那么根据资源的类型,分别赋予iomem_resource和ioport_resource,然后调用insert_resource插入资源。//这样系统的资源就形成了一个树形的数据结构,便于系统的管理pr_debug("Registeringplatformdevice’%s’.Parentat%s\n",dev_name(&pdev->dev),dev_name(pdev->dev.parent));ret=device_add(&pdev->dev);//注册到设备模型中if(ret==0)returnret;failed:while(–i>=0){structresource*r=&pdev->resource[i];unsignedlongtype=resource_type(r);if(type==IORESOURCE_MEM||type==IORESOURCE_IO)release_resource(r);}returnret;}

3. mini2440内核注册platform设备过程 因为一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初始化代码来完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函数中调用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))来完成注册。这个函数完成mini2440的所有platform设备的注册: (1) platform_add_devices函数是platform_device_register的简单封装,它向内核注册一组platform设备 (2) mini2440_devices是一个platform_device指针数组,定义如下:[cpp]view plaincopy

    staticstructplatform_device*mini2440_devices[]__initdata={&s3c_device_usb,&s3c_device_rtc,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&mini2440_device_eth,&s3c24xx_uda134x,&s3c_device_nand,&s3c_device_sdi,&s3c_device_usbgadget,};

这个就是mini2440的所有外设资源了,每个外设的具体定义在/arch/arm/plat-s3c24xx/devs.c,下面以s3c_device_lcd为例说明,其他的类似。s3c_device_lcd在devs.c中它定义为:[cpp]view plaincopy

    structplatform_devices3c_device_lcd={.name="s3c2410-lcd",.id=-1,.num_resources=ARRAY_SIZE(s3c_lcd_resource),.resource=s3c_lcd_resource,.dev={.dma_mask=&s3c_device_lcd_dmamask,.coherent_dma_mask=0xffffffffUL}};

可以看出,它占用的资源s3c_lcd_resource,定义如下:[cpp]view plaincopy

    staticstructresources3c_lcd_resource[]={[0]={.start=S3C24XX_PA_LCD,.end=S3C24XX_PA_LCD+S3C24XX_SZ_LCD-1,.flags=IORESOURCE_MEM,},[1]={.start=IRQ_LCD,.end=IRQ_LCD,.flags=IORESOURCE_IRQ,}};

这是一个数组,有两个元素,说明lcd占用了系统两个资源,一个资源类型是IORESOURCE_MEM代表io内存,起使地址S3C24XX_PA_LCD,这个是LCDCON1寄存器的地址。另外一个资源是中断信号线。四. platform设备驱动 如果要将所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个platform_driver,然后调用platform_driver_register进行注册。 1. 基本数据机构platform_driver[cpp]view plaincopy

    structplatform_driver{int(*probe)(structplatform_device*);int(*remove)(structplatform_device*);void(*shutdown)(structplatform_device*);int(*suspend)(structplatform_device*,pm_message_tstate);int(*resume)(structplatform_device*);structdevice_driverdriver;structplatform_device_id*id_table;};

这是platform驱动基本的数据结构,在驱动程序中我们要做的就是声明一个这样的结构并初始化。下面是lcd驱动程序对它的初始化:[cpp]view plaincopy

    staticstructplatform_drivers3c2412fb_driver={.probe=s3c2412fb_probe,.remove=s3c2410fb_remove,.suspend=s3c2410fb_suspend,.resume=s3c2410fb_resume,.driver={.name="s3c2412-lcd",.owner=THIS_MODULE,},};

上面几个函数是我们要实现的,它将赋值给device_driver中的相关成员,probe函数是用来查询特定设备是够真正存在的函数。当设备从系统删除的时候调用remove函数。 2. 注册函数platform_driver_register[cpp]view plaincopy

    intplatform_driver_register(structplatform_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;returndriver_register(&drv->driver);}

这个函数首先使驱动属于platform_bus_type总线,将platform_driver结构中的定义的probe,remove,shutdown赋值给device_driver结构中的相应成员,以供linux设备模型核心调用,然后调用driver_regster将设备驱动注册到linux设备模型核心中。五. 各环节的整合 前面提到mini2440板级初始化程序将它所有的platform设备注册到了linux设备模型核心中,在/sys/devices/platform目录中都有相应的目录表示。platform驱动则是由各个驱动程序模块分别注册到系统中的。但是他们是如何联系起来的呢,这就跟linux设备模型核心有关系了。在ldd3中的linux设备模型的各环节的整合中有详细的论述。这里简要说明一下platform实现的方法。每当注册一个platform驱动的时候就会调用driver_register,这个函数的调用会遍历设备驱动所属总线上的所有设备,并对每个设备调用总线的match函数。platform驱动是属于platform_bus_type总线,所以调用platform_match函数。这个函数实现如下:[cpp]view plaincopy

    staticintplatform_match(structdevice*dev,structdevice_driver*drv){structplatform_device*pdev=to_platform_device(dev);structplatform_driver*pdrv=to_platform_driver(drv);/*matchagainsttheidtablefirst*/if(pdrv->id_table)returnplatform_match_id(pdrv->id_table,pdev)!=NULL;/*fall-backtodrivernamematch*/return(strcmp(pdev->name,drv->name)==0);}

这个函数将device结构转换为platform_devcie结构,将device_driver结构转换为platform_driver结构,并调用platform_match_id对设备与驱动相关信息进行比较。如果没有比较成功会返回0,以便进行下一个设备的比较,如果比较成功就会返回1,并且将device结构中的driver指针指向这个驱动。然后调用device_driver中的probe函数,在lcd驱动中就是s3c2412fb_probe。这个函数是我们要编写的函数。这个函数检测驱动的状态,并且测试能否真正驱动设备,并且做一些初始化工作。

用最多的梦面对未来

linux platform 驱动模型分析

相关文章:

你感兴趣的文章:

标签云: