总线设备驱动模型——platform篇

来源:互联网 发布:曾梵志 知乎 编辑:程序博客网 时间:2024/05/21 09:27

    Linux从2.6起加入了一套新的驱动管理和注册的机制。platform平台总线是一条虚拟的总线,设备用platform_device表示,驱动用platform_driver进行注册。与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性。

    下面来看看内核注册platform总线的过程。

platform总线struct bus_typeplatform_bus_type

struct bus_type platform_bus_type= {

    .name       = "platform",

    .dev_attrs   = platform_dev_attrs,

    .match       = platform_match,

    .uevent       = platform_uevent,

    .pm        = &platform_dev_pm_ops,

};

platform设备struct device platform_bus

struct deviceplatform_bus= {

    .init_name   = "platform",

};

平台总线初始化

其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为

1、定义一个platform_device,并注册;

2、定义一个platform_driver,并注册;

3、通过platform_match成员函数来确定platform_device与platform_driver如何匹配。

int __init platform_bus_init(void)

{

    int error;

 

    early_platform_cleanup();

 

    error = device_register(&platform_bus);   //注册platform设备

    if (error)

        return error;

    error = bus_register(&platform_bus_type); //注册platform总线

    if (error)

        device_unregister(&platform_bus);

    return error;

}

struct platform_device

struct platform_device {

    const char   * name;                 //设备名

    int        id;                        //设备ID

    struct device    dev;

    u32        num_resources;             //设备使用的资源的数目

    struct resource    * resource;        //设备使用的资源

 

    const struct platform_device_id    *id_entry;

 

    /* MFD cell pointer */

    struct mfd_cell *mfd_cell;

 

    /* arch specific additions */

    struct pdev_archdata    archdata;

};

    在struct resource结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型、flags可以用内存资源、IO资源、中断资源等。

struct resource {

    resource_size_t start;

    resource_size_t end;

    const char *name;

    unsigned long flags;

    struct resource *parent,*sibling,*child;

};

 

struct platform_device *platform_device_alloc(constchar *name,int id)

{

    struct platform_object*pa;

 

    pa = kzalloc(sizeof(struct platform_object)+ strlen(name), GFP_KERNEL);

    if (pa) {

        strcpy(pa->name, name);

        pa->pdev.name= pa->name;

        pa->pdev.id= id;

        device_initialize(&pa->pdev.dev);

        pa->pdev.dev.release= platform_device_release;

        arch_setup_pdev_archdata(&pa->pdev);

    }

 

    return pa ? &pa->pdev: NULL;

}

platform_device注册

    其最后也是调用device_add的,主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程。

int platform_device_register(struct platform_device*pdev)

{

    device_initialize(&pdev->dev);//dev初始化

    arch_setup_pdev_archdata(pdev);

    return platform_device_add(pdev);//加入到dev链表

}

 

int platform_device_add(struct platform_device*pdev)

{

    int i, ret= 0;

 

    if (!pdev)

        return -EINVAL;

 

    if (!pdev->dev.parent)

        pdev->dev.parent= &platform_bus;//父设备设置为platform_bus

 

    pdev->dev.bus= &platform_bus_type;//设备挂载在platforrm总线上

 

    if (pdev->id != -1)

        dev_set_name(&pdev->dev,"%s.%d", pdev->name, pdev->id);

    else

        dev_set_name(&pdev->dev,"%s", pdev->name);

 

    for (i = 0; i < pdev->num_resources; i++){    //完成资源的初始化

        struct resource*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;

            else if (resource_type(r)== IORESOURCE_IO)

                p = &ioport_resource;

        }

 

        if (p && insert_resource(p, r)){

            printk(KERN_ERR

             "%s: failed toclaim resource %d\n",

             dev_name(&pdev->dev), i);

            ret = -EBUSY;

            goto failed;

        }

    }

 

    pr_debug("Registeringplatform device '%s'. Parent at %s\n",

         dev_name(&pdev->dev), dev_name(pdev->dev.parent));

 

    ret = device_add(&pdev->dev);

    if (ret == 0)

        return ret;

 

 failed:

    while (--i >= 0) {

        struct resource*r =&pdev->resource[i];

        unsigned long type = resource_type(r);

 

        if (type == IORESOURCE_MEM|| type == IORESOURCE_IO)

            release_resource(r);

    }

 

    return ret;

}

struct platform_driver

    platform_driver包含了设备操作的功能函数,同时包含了device_driver结构。

struct platform_driver {

    int (*probe)(struct platform_device*);

    int (*remove)(struct platform_device*);

    void (*shutdown)(struct platform_device*);

    int (*suspend)(struct platform_device*, pm_message_t state);

    int (*resume)(struct platform_device*);

    struct device_driver driver;

    const struct platform_device_id *id_table;

};

platform_driver注册

int platform_driver_register(struct platform_driver*drv)

{

    drv->driver.bus= &platform_bus_type;//注册到总线上,drvdev匹配通过platform_bus_type注册的platform_match完成

    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;

 

    return driver_register(&drv->driver);

}

    其注册函数中比较重要的还是调用了driver_register,添加到platform总线链表,完成设备与驱动之间的匹配过程,其主要的过程在总线设备驱动模型的驱动篇已经有过分析。

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 = bus_add_driver(drv);

    if (ret)

        return ret;

    ret = driver_add_groups(drv, drv->groups);

    if (ret)

        bus_remove_driver(drv);

    return ret;

}

驱动和设备的匹配

    由以前可以看出,驱动和设备的匹配主要是调用bus的match函数来完成匹配。可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功

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

{

    struct platform_device*pdev = to_platform_device(dev);

    struct platform_driver*pdrv = to_platform_driver(drv);

 

    /* Attempt an OF style match first */

    if (of_driver_match_device(dev, drv))

        return 1;

 

    /* Then try to match against the idtable */

    if (pdrv->id_table)

        return platform_match_id(pdrv->id_table, pdev) !=NULL;

 

    /* fall-back to driver name match */

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

}

 

在platform的设备驱动的编写有两种方法:

1、在bsp版文件中实现定义,在文件中将platform_device被化为一个数组,最后通过platform_add_devices函数注册。对于2440来说位于arch\arm\mach-s3c2440\mach-smdk2440.c中定义

static struct platform_device *smdk2440_devices[] __initdata= {

    &s3c_device_usb,

    &s3c_device_lcd,

    &s3c_device_wdt,

    &s3c_device_i2c,

    &s3c_device_iis,

};

如果我们要实现一个设备的添加,那么只需要加入一个struct platform_device的数组,然后只需要编写对应的platform_driver驱动程序就可以了。从这种方法可以看出,存在一个很明显的缺点,如果要改写驱动,就要重新的编译内核。

 

2、第二种方法只需要单独编写一个内核模块加载到内核中。

例子:

device文件:platform_dev.c

struct platform_device *my_led_dev;

 

static int __initplatform_dev_init(void)

{

    int ret;

     

 //分配一个 platform_device结构体

    my_led_dev =platform_device_alloc("platform_led",-1);

     

    ret = platform_device_add(my_led_dev);//将自定义的设备添加到内核设备架构中

     

    if(ret)

        platform_device_put(my_led_dev);//销毁platform设备结构

     

    return ret;

}

 

static void __exitplatform_dev_exit(void)

{

    platform_device_unregister(my_led_dev);//注销platform_device

}

 

module_init(platform_dev_init);

module_exit(platform_dev_exit);

 

MODULE_AUTHOR("Sola");

MODULE_LICENSE("GPL");


drv文件:platform_drv.c

static int s3c6410_led_open(struct inode*inode,struct file *file)

{

   unsigned tmp;

          tmp = readl(S3C64XX_GPMCON);

     tmp = (tmp & ~(0xFFFF))|(0x1111U);

          writel(tmp, S3C64XX_GPMCON);

 

     return 0;

}

 

 

static int s3c6410_led_close(struct inode*inode,struct file *file)

{

 

    return 0;

}

 

 

static int s3c6410_led_read(struct file*filp,char __user *buff, size_t count, loff_t*offp)

{

     printk("#########read######\n");

     return count;

}

 

static int s3c6410_led_write (struct file*filp,const char __user *buf, size_t count,loff_t*f_pos)

{

     char wbuf[10];

     unsigned tmp;

 

     copy_from_user(wbuf,buf,count);

  if(wbuf[0]==1)//1号灯亮

     switch(wbuf[1])

     {

         case 0: //off

             tmp = readl(S3C64XX_GPMDAT);

                           tmp |=(0x1U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         case 1: //on

             tmp = readl(S3C64XX_GPMDAT);

                           tmp &=~(0x1U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         default :

             break;

     }

 

  if(wbuf[0]==2)//2号灯亮

     switch(wbuf[1])

     {

         case 0: //off

             tmp = readl(S3C64XX_GPMDAT);

                           tmp |=(0x2U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         case 1: //on

             tmp = readl(S3C64XX_GPMDAT);

                           tmp &=~(0x2U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         default :

             break;

     }

 

  if(wbuf[0]==3)//3号灯亮

     switch(wbuf[1])

     {

         case 0: //off

             tmp = readl(S3C64XX_GPMDAT);

                           tmp |=(0x4U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         case 1: //on

             tmp = readl(S3C64XX_GPMDAT);

                           tmp &=~(0x4U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         default :

             break;

     }

 

  if(wbuf[0]==4)//4号灯亮

     switch(wbuf[1])

     {

         case 0: //off

             tmp = readl(S3C64XX_GPMDAT);

                           tmp |=(0x8U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         case 1: //on

             tmp = readl(S3C64XX_GPMDAT);

                           tmp &=~(0x8U);

                           writel(tmp, S3C64XX_GPMDAT);

             break;

         default :

             break;

     }

     return count;

}

 

 

static struct file_operationsled_fops = {

    .owner = THIS_MODULE,

    .open = s3c6410_led_open,

    .release = s3c6410_led_close,

    .read = s3c6410_led_read,

 .write = s3c6410_led_write,

};

 

 

static int my_plat_probe(struct platform_device*dev)

{

    int rc;

 printk("Test platform_led dev\n");

 //注册设备

 rc = register_chrdev(LED_MAJOR,"platform_led",&led_fops);

  if (rc <0)

     {

         printk ("register %schar dev error\n","led");

         return -1;

     }

     printk ("ok!\n");

     return 0;

}

 

static int my_plat_remove(struct platform_device*dev)

{

    printk("my platfromdevice has removed.\n");

    return 0;

}

 

structplatform_driver my_led_drv= {

    .probe = my_plat_probe,

    .remove = my_plat_remove,

    .driver = {

        .owner = THIS_MODULE,

        .name = "platform_led",

    },

};

 

static int __initplatform_drv_init(void)

{

    int ret;

 

    ret = platform_driver_register(&my_led_drv);

     

    return ret;

}

 

static void __exitplatform_drv_exit(void)

{

    platform_driver_unregister(&my_led_drv);

}

 

module_init(platform_drv_init);

module_exit(platform_drv_exit);

 

MODULE_LICENSE("GPL");

0 0
原创粉丝点击