免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1062 | 回复: 0
打印 上一主题 下一主题

Linux Platform Device and Driver [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-11-10 15:24 |只看该作者 |倒序浏览

                               
       
       
       
       
Linux
Platform Device and Driver
作者:Dongas
日期:08-06-27
从Linux
2.6起引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver。
Linux中大部分的设备驱动,都可以使用这套机制,
设备用Platform_device表示,驱动用Platform_driver进行注册。
Linux
platform driver机制和传统的device
driver
机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform
device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。
Platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。
通过Platform机制开发发底层驱动的大致流程为:
定义
platform_device
à
注册
platform_device
à
定义
platform_driver
à注册
platform_driver。
首先要确认的就是设备的资源信息,例如设备的地址,中断号等。
在2.6内核中platform设备用结构体platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,
struct
platform_device {
const
char * name;
u32
id;
struct device    dev;
u32
num_resources;
struct
resource * resource;
};
struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;    /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent;
    struct kobject kobj;
    char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
    unsigned        uevent_suppress:1;
    const char        *init_name; /* initial name of the device */
    struct device_type    *type;
    struct semaphore    sem;    /* semaphore to synchronize calls to
                     * its driver.
                     */
    struct bus_type    *bus;        /* type of bus device is on */
    struct device_driver *driver;    /* which driver has allocated this
                       device */
    void        *driver_data;    /* data private to the driver */
    void        *platform_data;    /* Platform specific data, device
                       core doesn't touch it   ----------这里可以添加私有数据,作额外的补充,增加devie功能。*/
    struct dev_pm_info    power;
#ifdef CONFIG_NUMA
    int        numa_node;    /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */
    struct device_dma_parameters *dma_parms;
    struct list_head    dma_pools;    /* dma pools (if dma'ble) */
    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */
    struct dev_archdata    archdata;
    dev_t            devt;    /* dev_t, creates the sysfs "dev" */
    spinlock_t        devres_lock;
    struct list_head    devres_head;
    struct klist_node    knode_class;
    struct class        *class;
    struct attribute_group    **groups;    /* optional groups */
    void    (*release)(struct device *dev);
};
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,
struct
resource {
const
char *name;
unsigned
long start, end;
unsigned
long flags;
struct
resource *parent, *sibling, *child;
};
下面举s3c2410平台的i2c驱动作为例子来说明:
       
       
               
                       
                        /*
                        arch/arm/mach-s3c2410/devs.c */[color="#000000"]
/*
                        I2C */[color="#000000"]
[color="#0000ff"]static
                        [color="#0000ff"]struct
                        resource s3c_i2c_resource[color="#0000cc"][]
                        [color="#0000cc"]=
                        [color="#0000cc"]{[color="#000000"]
         [color="#0000cc"][[color="#000000"]0[color="#0000cc"]]
                        [color="#0000cc"]=
                        [color="#0000cc"]{[color="#000000"]
                   [color="#0000cc"].start
                        [color="#0000cc"]=
                        S3C24XX_PA_IIC[color="#0000cc"],[color="#000000"]
                   [color="#0000cc"].end
                        [color="#0000cc"]=
                        S3C24XX_PA_IIC [color="#0000cc"]+
                        S3C24XX_SZ_IIC [color="#0000cc"]-
                        1[color="#0000cc"],[color="#000000"]
                   [color="#0000cc"].flags
                        [color="#0000cc"]=
                        IORESOURCE_MEM[color="#0000cc"],[color="#000000"]
         [color="#0000cc"]},[color="#000000"]
         [color="#0000cc"][[color="#000000"]1[color="#0000cc"]]
                        [color="#0000cc"]=
                        [color="#0000cc"]{[color="#000000"]
                   [color="#0000cc"].start
                        [color="#0000cc"]=
                        IRQ_IIC[color="#0000cc"],
                        [color="#ff9900"]//S3C2410_IRQ(27)[color="#000000"]
                   [color="#0000cc"].end
                        [color="#0000cc"]=
                        IRQ_IIC[color="#0000cc"],[color="#000000"]
                   [color="#0000cc"].flags
                        [color="#0000cc"]=
                        IORESOURCE_IRQ[color="#0000cc"],[color="#000000"]
         [color="#0000cc"]}[color="#000000"]
[color="#0000cc"]};
               
       

这里定义了两组resource,它描述了一个I2C设备的资源,第1组描述了这个I2C设备所占用的总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个I2C设备的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
有了resource信息,就可以定义platform_device了:
       
       
               
                       
                        [color="#000000"]

                       
               
       
定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_driver_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要匹配内核中所以已注册的设备名。
s3c2410-i2c的platform_device是在系统启动时,在cpu.c里的s3c_arch_init()函数里进行注册的,这个函数申明为arch_initcall(s3c_arch_init);会在系统初始化阶段被调用。
arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考include/linux/init.h)
s3c_arch_init函数如下:
       
       
               
                       
                        /*
                        arch/arm/mach-3sc2410/cpu.c */[color="#000000"]
[color="#0000ff"]static
                        [color="#0000ff"]int
                        __init s3c_arch_init[color="#0000cc"]([color="#0000ff"]void[color="#0000cc"])[color="#000000"]
[color="#0000cc"]{[color="#000000"]
    [color="#0000ff"]int
                        ret[color="#0000cc"];[color="#000000"]
    ……
/*
                        [color="#ff9900"]这里[color="#ff9900"]board[color="#ff9900"]指针指向在[color="#ff9900"]mach-smdk2410.c[color="#ff9900"]里的定义的[color="#ff9900"]smdk2410_board[color="#ff9900"],里面包含了预先定义的I2C
                        Platform_device[color="#ff9900"]等.
                        */[color="#000000"]
    [color="#0000ff"]if
                        [color="#0000cc"]([color="#ff0000"]board
                        [color="#0000cc"]!=
                        [color="#ff0000"]NULL[color="#0000cc"])
                        [color="#0000cc"]{[color="#000000"]
        [color="#0000ff"]struct
                        platform_device [color="#0000cc"]**ptr
                        [color="#0000cc"]=
                        board[color="#0000cc"]->[color="#000000"]devices[color="#0000cc"];[color="#000000"]
        [color="#0000ff"]int
                        i[color="#0000cc"];[color="#000000"]
        [color="#0000ff"]for
                        [color="#0000cc"](i
                        [color="#0000cc"]=
                        0[color="#0000cc"];
                        i [color="#0000cc"]
                        board[color="#0000cc"]->[color="#000000"]devices_count[color="#0000cc"];
                        i[color="#0000cc"]++,
                        ptr[color="#0000cc"]++)
                        [color="#0000cc"]{
            ret
                        [color="#0000cc"]=
                        [color="#ff0000"]pla
                       
                        [color="#ff0000"]tform_device_register[color="#0000cc"](*[color="#ff0000"]ptr[color="#0000cc"]);[color="#ff0000"]    [color="#ff9900"]//[color="#ff9900"]在这里进行注册[color="#ff9900"]
[color="#ff0000"]
            [color="#0000ff"]if
                        [color="#0000cc"]([color="#ff0000"]ret[color="#0000cc"])
                        [color="#0000cc"]{[color="#ff0000"]
                printk[color="#0000cc"](KERN_ERR
                        "s3c24xx:
                        failed to add board device %s (%d) @%p\n"[color="#0000cc"],
                        [color="#0000cc"](*[color="#ff0000"]ptr[color="#0000cc"])->[color="#ff0000"]name[color="#0000cc"],
                       
ret[color="#0000cc"],
                        [color="#0000cc"]*[color="#ff0000"]ptr[color="#0000cc"]);[color="#ff0000"]
            [color="#0000cc"]}[color="#ff0000"]
        [color="#0000cc"]}[color="#ff0000"]
        /*
                        mask any error, we may not need all these board
        
                        * devices */
        ret
                        [color="#0000cc"]=
                        0[color="#0000cc"];[color="#ff0000"]
    [color="#0000cc"]}[color="#ff0000"]
    [color="#0000ff"]return
                        ret[color="#0000cc"];[color="#ff0000"]
[color="#0000cc"]}
                       

               
       
同时被注册还有很多其他平台的platform_device,详细查看arch/arm/mach-s3c2410/mach-smdk2410.c里的smdk2410_devices结构体。
驱动程序需要实现结构体struct
platform_driver,参考drivers/i2c/busses
       
       
               
                       
                        /* device
                        driver for platform bus bits */
                       
                        [color="#000000"]
[color="#0000ff"]static
                        [color="#0000ff"]struct
                        platform_driver s3c2410_i2c_driver [color="#0000cc"]=
                        [color="#0000cc"]{[color="#000000"]
         [color="#0000cc"].probe
                        [color="#0000cc"]=
                        s3c24xx_i2c_probe[color="#0000cc"],[color="#000000"]
         [color="#0000cc"].[color="#ff0000"]remove
                        [color="#0000cc"]=
                        s3c24xx_i2c_remove[color="#0000cc"],[color="#000000"]
         [color="#0000cc"].resume
                        [color="#0000cc"]=
                        s3c24xx_i2c_resume[color="#0000cc"],[color="#000000"]
         [color="#0000cc"].driver
                        [color="#0000cc"]=
                        [color="#0000cc"]{[color="#000000"]
                   [color="#0000cc"].owner
                        [color="#0000cc"]=
                        THIS_MODULE[color="#0000cc"],[color="#000000"]
                   [color="#0000cc"].name
                        [color="#0000cc"]=
                        [color="#ff00ff"]"s3c2410-i2c"[color="#0000cc"],[color="#000000"]
         [color="#0000cc"]},[color="#000000"]
[color="#0000cc"]};
               
       

在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是s3c_device_i2c结构中name元素和s3c2410_i2c_driver结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册成功,当注册成功时会调用platform_driver结构元素probe函数指针,这里就是s3c24xx_i2c_probe,当进入probe函数后,需要获取设备的资源信息,常用获取资源的函数主要是:
struct
resource * platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num);
根据参数type所指定类型,例如IORESOURCE_MEM,来获取指定的资源。
struct
int platform_get_irq(struct platform_device *dev, unsigned int num);
获取资源中的中断号。
下面举s3c24xx_i2c_probe函数分析,看看这些接口是怎么用的。
前面已经讲了,s3c2410_i2c_driver注册成功后会调用s3c24xx_i2c_probe执行,下面看代码:
       
       
               
                       
                        /*
                        drivers/i2c/busses/i2c-s3c2410.c */[color="#000000"]
[color="#0000ff"]static
                        [color="#0000ff"]int
                        s3c24xx_i2c_probe[color="#0000cc"]([color="#0000ff"]struct
                        platform_device [color="#0000cc"]*[color="#000000"]pdev[color="#0000cc"])[color="#000000"]
[color="#0000cc"]{[color="#000000"]
    [color="#0000ff"]struct
                        s3c24xx_i2c [color="#0000cc"]*i2c
                        [color="#0000cc"]=
                        [color="#0000cc"]&[color="#000000"]s3c24xx_i2c[color="#0000cc"];[color="#000000"]
    [color="#0000ff"]struct
                        resource [color="#0000cc"]*[color="#000000"]res[color="#0000cc"];[color="#000000"]
    [color="#0000ff"]int
                        ret[color="#0000cc"];[color="#000000"]

    /*
                        find the clock and enable it */[color="#000000"]

    i2c[color="#0000cc"]->dev
                        [color="#0000cc"]=
                        [color="#0000cc"]&[color="#000000"]pdev[color="#0000cc"]->[color="#000000"]dev[color="#0000cc"];[color="#000000"]
    i2c[color="#0000cc"]->clk
                        [color="#0000cc"]=
                        clk_get[color="#0000cc"](&[color="#000000"]pdev[color="#0000cc"]->[color="#000000"]dev[color="#0000cc"],
                        [color="#ff00ff"]"i2c"[color="#0000cc"]);[color="#000000"]
    [color="#0000ff"]if
                        [color="#0000cc"]([color="#000000"]IS_ERR[color="#0000cc"]([color="#000000"]i2c[color="#0000cc"]->[color="#000000"]clk[color="#0000cc"]))
                        [color="#0000cc"]{
   
                        dev_err[color="#0000cc"](&[color="#000000"]pdev[color="#0000cc"]->[color="#000000"]dev[color="#0000cc"],
                        "cannot
                        get clock\n"[color="#0000cc"]);
   
                        ret [color="#0000cc"]=
                        [color="#0000cc"]-[color="#000000"]ENOENT[color="#0000cc"];
   
                        [color="#0000ff"]goto
                        out[color="#0000cc"];[color="#000000"]
    [color="#0000cc"]}[color="#000000"]
    dev_dbg[color="#0000cc"](&[color="#000000"]pdev[color="#0000cc"]->[color="#000000"]dev[color="#0000cc"],
                        "clock
                        source %p\n"[color="#0000cc"],
                        i2c[color="#0000cc"]->[color="#000000"]clk[color="#0000cc"]);[color="#000000"]
    clk_enable[color="#0000cc"]([color="#000000"]i2c[color="#0000cc"]->[color="#000000"]clk[color="#0000cc"]);
                       
                        [color="#000000"]
    /*
                        map the registers */[color="#000000"]
    res
                        [color="#0000cc"]=
                        platform_get_resource[color="#0000cc"]([color="#ff0000"]pdev[color="#0000cc"],
                        IORESOURCE_MEM[color="#0000cc"],
                        0[color="#0000cc"]);
                        /*
                        [color="#ff9900"]获取设备的[color="#ff9900"]IO资源地址
                        [color="#ff9900"]*/[color="#000000"]
    [color="#0000ff"]if
                        [color="#0000cc"](res
                        [color="#0000cc"]==
                        NULL)
                        [color="#0000cc"]{
   
                        dev_err[color="#0000cc"](&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev[color="#0000cc"],
                        "cannot
                        find IO resource\n"[color="#0000cc"]);
   
                        ret [color="#0000cc"]=
                        [color="#0000cc"]-[color="#ff0000"]ENOENT[color="#0000cc"];
   
                        [color="#0000ff"]goto
                        out[color="#0000cc"];[color="#ff0000"]
    [color="#0000cc"]}[color="#ff0000"]
   
    i2c[color="#0000cc"]->ioarea
                        [color="#0000cc"]=
                        request_mem_region[color="#0000cc"]([color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]start[color="#0000cc"],
                        [color="#0000cc"]([color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]end[color="#0000cc"]-[color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]start[color="#0000cc"])+[color="#ff0000"]1[color="#0000cc"],
                        pdev[color="#0000cc"]->[color="#ff0000"]name[color="#0000cc"]);
                        /*
                        申请这块IO
                        Region */[color="#ff0000"]
   
    [color="#0000ff"]if
                        [color="#0000cc"]([color="#ff0000"]i2c[color="#0000cc"]->ioarea
                        [color="#0000cc"]==
                        NULL[color="#0000cc"])
                        [color="#0000cc"]{
   
                        dev_err[color="#0000cc"](&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev[color="#0000cc"],
                        "cannot
                        request IO\n"[color="#0000cc"]);
   
                        ret [color="#0000cc"]=
                        [color="#0000cc"]-[color="#ff0000"]ENXIO[color="#0000cc"];
   
                        [color="#0000ff"]goto
                        out[color="#0000cc"];[color="#ff0000"]
    [color="#0000cc"]}[color="#ff0000"]
   
    i2c[color="#0000cc"]->regs
                        [color="#0000cc"]=
                        ioremap[color="#0000cc"]([color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]start[color="#0000cc"],
                        [color="#0000cc"]([color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]end[color="#0000cc"]-[color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]start[color="#0000cc"])+[color="#ff0000"]1[color="#0000cc"]);
                        /*
                        映射至内核虚拟空间
                        [color="#ff9900"]*/[color="#ff0000"]
   
    [color="#0000ff"]if
                        [color="#0000cc"]([color="#ff0000"]i2c[color="#0000cc"]->regs
                        [color="#0000cc"]==
                        NULL[color="#0000cc"])
                        [color="#0000cc"]{
   
                        dev_err[color="#0000cc"](&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev[color="#0000cc"],
                        "cannot
                        map IO\n"[color="#0000cc"]);
   
                        ret [color="#0000cc"]=
                        [color="#0000cc"]-[color="#ff0000"]ENXIO[color="#0000cc"];
   
                        [color="#0000ff"]goto
                        out[color="#0000cc"];[color="#ff0000"]
    [color="#0000cc"]}[color="#ff0000"]
   
    dev_dbg[color="#0000cc"](&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev[color="#0000cc"],
                        "registers
                        %p (%p, %p)\n"[color="#0000cc"],
                        i2c[color="#0000cc"]->[color="#ff0000"]regs[color="#0000cc"],
                        i2c[color="#0000cc"]->[color="#ff0000"]ioarea[color="#0000cc"],
                        res[color="#0000cc"]);[color="#ff0000"]
   
    /*
                        setup info block for the i2c core */[color="#ff0000"]
    i2c[color="#0000cc"]->[color="#ff0000"]adap[color="#0000cc"].algo_data
                        [color="#0000cc"]=
                        i2c[color="#0000cc"];[color="#ff0000"]
    i2c[color="#0000cc"]->[color="#ff0000"]adap[color="#0000cc"].[color="#ff0000"]dev[color="#0000cc"].parent
                        [color="#0000cc"]=
                        [color="#0000cc"]&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev[color="#0000cc"];[color="#ff0000"]
   
    /*
                        initialise the i2c controller */
    ret
                        [color="#0000cc"]=
                        s3c24xx_i2c_init[color="#0000cc"]([color="#ff0000"]i2c[color="#0000cc"]);[color="#ff0000"]
    [color="#0000ff"]if
                        [color="#0000cc"](ret
                        [color="#0000cc"]!=
                        0[color="#0000cc"])
   
                        [color="#0000ff"]goto
                        out[color="#0000cc"];[color="#ff0000"]
    /*
                        find the IRQ for this unit (note, this relies on the init call to
                        ensure no current IRQs pending */
   
    res
                        [color="#0000cc"]=
                        platform_get_resource[color="#0000cc"]([color="#ff0000"]pdev[color="#0000cc"],
                        IORESOURCE_IRQ[color="#0000cc"],
                        0[color="#0000cc"]);
                        /*
                        获取设备[color="#ff9900"]IRQ中断号
                        [color="#ff9900"]*/[color="#ff0000"]
    [color="#0000ff"]if
                        [color="#0000cc"](res
                        [color="#0000cc"]==
                        NULL[color="#0000cc"])
                        [color="#0000cc"]{
   
                        dev_err[color="#0000cc"](&[color="#ff0000"]pdev[color="#0000cc"]->[color="#ff0000"]dev,:
                        #ff00ff">"cannot find IRQ\n");
   
                        ret = -ENOENT;
     [color="#0000ff"]goto
                        out;
    }
   
    ret
                        [color="#0000cc"]=
                        request_irq[color="#0000cc"]([color="#ff0000"]res[color="#0000cc"]->[color="#ff0000"]start[color="#0000cc"],
                        s3c24xx_i2c_irq[color="#0000cc"],
                        IRQF_DISABLED,
                        /*
                        申请IRQ
                        */
   
                        pdev->name, i2c);
   
    ……
    [color="#0000ff"]return
                        ret;
   
}
                       
                       
                       
               
       
[color="#000000"]小思考:
那什么情况可以使用platform
driver机制编写驱动呢?
我的理解是只要和内核本身运行依赖性不大的外围设备(换句话说只要不在内核运行所需的一个最小系统之内的设备),相对独立的,拥有各自独自的资源(addresses
and
IRQs),都可以用platform_driver实现。如:lcd,usb,uart等,都可以用platfrom_driver写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver机制,实际上内核实现也是这样的。
参考资料:
linux-2.6.24/Documentation/driver-model/platform.txt

《platform
_device和platform_driver注册过程》
http://blog.chinaunix.net/u2/60011/showart.php?id=1018999

[color="#800080"]http://www.eetop.cn/blog/html/45/11145-676.html
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94108/showart_2090877.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP