Bootstrap

驱动——platform驱动总线三种匹配方式

三种platform驱动匹配方式代码案例以及现象

方式一:通过设置名字进行匹配

相关API简介:

1、platform_device的API

①分配对象

struct platform_device {

        const  char *name;//用于进行匹配的名字

        int  id;//总线号 PLATFORM_DEVID_AUTO(自动分配总线号)

        struct  devicedev;//父类

        u32     num_resources;//表示设备信息的个数

        struct resource*resource;//描述硬件设备信息的结构体

};

struct device {

        void    (*release)(struct device *dev); //释放device的资源

}

struct resource {

        resource_size_t start;//资源的起始数值 

        resource_size_t end;//资源的结束值

        unsigned long flags;//资源的类型 // IORESOURCE_IO|IORESOURCE_MEM|IORESOURCE_IRQ };

②对象初始化

1>定义一个relese函数

void pdev_release(struct device *dev) { }

2>对设备信息进行填充

struct resource res[]={

                [0]={

                        .start=0x12345678,

                        .end=0x12345678+49,

                        .flags= IORESOURCE_MEM,

                },

                [1]={

                        .start=71,

                        .end=71,

                        .flags= IORESOURCE_IRQ,

                },

};

3>给对象赋值

struct platform_device pdev={

                        .name="aaaaa",

                        .id=PLATFORM_DEVID_AUTO,

                        .dev={

                                .release=pdev_release,

                        },

                        .resource=res,

                        .num_resources=ARRAY_SIZE(res),

};

③将对象注册进内核

int platform_device_register(struct platform_device *pdev)

参数:platform_device对象指针

④注销对象

 int platform_device_unregister(struct platform_device *pdev)

 2、platform_driver的API

①对象结构体

struct platform_driver {
                //匹配成功后执行probe函数
                int (*probe)(struct platform_device *);
                 //设备和驱动分离的时候执行remove函数
                int (*remove)(struct platform_device *);
                 //父类,进行匹配选项的设置
                struct device_driver driver;
                 //通过id_table的方式匹配设备文件
                const struct platform_device_id *id_table;
};

struct device_driver {
                const char  *name;//设置名字匹配
                struct of_device_id*of_match_table;//通过设备树进行匹配
        };

②对象的初始化

//定义一个probe函数
    int pdrv_probe(struct platform_device *pdev)
    {      
    }
    int pdrv_remove(struct platform_device *pdev)
    {      
    }
    //对象初始化
    struct platform_driver pdrv={
        .probe=pdrv_probe,
        .remove=pdrv_remove,  
        .driver={
            .name="aaaaa",        
        },  
    };

③对象的注册

 #define platform_driver_register(drv)   __platform_driver_register(drv, THIS_MODULE)

④对象的注销

void platform_driver_unregister(struct platform_driver *drv)

3、代码实现

——device

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

//对设备信息进行填充
  struct resource res[]={
      [0]={
         .start=0x12345678,
         .end=0x12345678+49,
         .flags= IORESOURCE_MEM,     
      },
      [1]={
          .start=71,
         .end=71,
         .flags= IORESOURCE_IRQ,      
      },
  };
  
//定义一个relese函数
void pdev_release(struct device *dev)
{
    printk("%s:%d\n",__func__,__LINE__);
}

  //给对象赋值
  struct platform_device pdev={
      .name="aaaaa",
      .id=PLATFORM_DEVID_AUTO,
      .dev={
          .release=pdev_release,      
      },
      .resource=res,
      .num_resources=ARRAY_SIZE(res),        
  };

static int __init mycdev_init(void)
{
    //注册device
    return platform_device_register(&pdev);
}
static void __exit mycdev_exit(void)
{
    //注销device
    platform_device_unregister(&pdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

——driver

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/mod_devicetable.h>
struct resource *res;
int irqno;
//对象的初始化
int pdrv_probe(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__); 
    res=platform_get_resource(pdev,IORESOURCE_MEM,0);
        if(res==NULL)
        {
            printk("获取MEM资源失败\n");
            return ENODATA;
        }
    //获取中断类型的资源
    irqno=platform_get_irq(pdev,0);
        if(irqno<0)
        {
            printk("获取中断资源失败\n");
            return ENODATA;
        }

        printk("addr:%#llx ,irqno:%d\n",res->start,irqno);
    return 0;
}

int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__);
    return 0;
} 
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="aaaaa",
    },
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

4、测试现象

 方式二:通过id_table方式匹配

        在驱动编写的时候,有时候需要一个驱动适配多款硬件,任何一个硬件的设备信息和这个驱动匹配成功后都可以执行这个驱动里的probe函数,所以为了解决这个情景,linux内核设计了id_table存取设备的名字列表,驱动端选择id_table方式通过platform_device_id来实现:

#include<linux/mod_devicetable.h>
struct platform_device_id {
    char name[PLATFORM_NAME_SIZE];//匹配的名字
    kernel_ulong_t driver_data;//设备驱动的私有数据
};

 代码实现—

——device

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

//对设备信息进行填充
  struct resource res[]={
      [0]={
         .start=0x12345678,
         .end=0x12345678+49,
         .flags= IORESOURCE_MEM,     
      },
      [1]={
          .start=71,
         .end=71,
         .flags= IORESOURCE_IRQ,      
      },
  };
  
//定义一个relese函数
void pdev_release(struct device *dev)
{
    printk("%s:%d\n",__func__,__LINE__);
}

  //给对象赋值
  struct platform_device pdev={
      .name="hello1",
      .id=PLATFORM_DEVID_AUTO,
      .dev={
          .release=pdev_release,      
      },
      .resource=res,
      .num_resources=ARRAY_SIZE(res),        
  };

static int __init mycdev_init(void)
{
    //注册device
    return platform_device_register(&pdev);
}
static void __exit mycdev_exit(void)
{
    //注销device
    platform_device_unregister(&pdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

——driver

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/mod_devicetable.h>
struct resource *res;
int irqno;
//对象的初始化
int pdrv_probe(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__); 
    res=platform_get_resource(pdev,IORESOURCE_MEM,0);
        if(res==NULL)
        {
            printk("获取MEM资源失败\n");
            return ENODATA;
        }
    //获取中断类型的资源
    irqno=platform_get_irq(pdev,0);
        if(irqno<0)
        {
            printk("获取中断资源失败\n");
            return ENODATA;
        }

        printk("addr:%#llx ,irqno:%d\n",res->start,irqno);
    return 0;
}

int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__);
    return 0;
} 
struct platform_device_id idtable[]={
    {"hello1",0},
    {"hello2",1},
    {"hello3",2},
    {},
};
//热插拔宏
MODULE_DEVICE_TABLE(platform,idtable);
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="aaaaa",
    },
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

测试现象 

 

方式三:通过设备树进行匹配

        在linux内核版本3.10之后,要求所有的设备信息都放在设备树中,所以在platform驱动被使用的时候不再写platform_device了。而是将platform_device中的设备信息直接放在设备树中即可

 1、添加设备节点信息

   myplatform{
       compatible="hqyj,platform";
       reg=<0x12345678 0x14>;
       interrupt-parent = <&gpiof>;
       interrupts = <9 0>;
       myled1 = <&gpioe 10 0>;
   };                              

2、将匹配方式设置为设备树匹配 

 //构建compatible表
    struct of_device_id oftable[]=
    {
        {.compatible="hqyj,platform",},
        {}
    };
    //热插拔宏
    MODULE_DEVICE_TABLE(of,oftable);  
    //对象初始化
    struct platform_driver pdrv={       
        .probe=pdrv_probe,
        .remove=pdrv_remove,  
        .driver={
            .name="aaaaa", //设置名字匹配
            .of_match_table=oftable,//设备树匹配
        }, 

3、解析设备树节点信息获取GPIO编号

4、代码实现

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/mod_devicetable.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
/*   myplatform{
       compatible="hqyj,platform";
       reg=<0x12345678 0x14>;
       interrupt-parent = <&gpiof>;
       interrupts = <9 0>;
       myled1 = <&gpioe 10 0>;
   };                              
*/
struct resource *res;
int irqno;
struct gpio_desc *gpiono;
//对象的初始化
int pdrv_probe(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__);
    res = platform_get_resource(pdev,IORESOURCE_MEM,0);
    if(res==NULL)
    {
        printk("paltform get resource error\n");
        return ENODATA;
    }
    irqno = platform_get_irq(pdev,0);
    if(irqno<0)
    {
        printk("paltform get irq error\n");
        return ENODATA;
    }
    printk("addr:%#x,irqno:%d\n",res->start,irqno);
    //解析设备树节点信息获取GPIO编号
    gpiono=gpiod_get_from_of_node(pdev->dev.of_node,"myled1",0,GPIOD_OUT_HIGH,NULL);
    if(IS_ERR(gpiono))
    {
        printk("gpiod get from of node error\n");
        return PTR_ERR(gpiono);
    }
    printk("gpiod get from of node success\n");
    //点灯
    gpiod_set_value(gpiono,1);
    return 0;
}

int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%d\n",__func__,__LINE__);
    gpiod_set_value(gpiono, 0);
    gpiod_put(gpiono);
    return 0;
} 
//构建compatible表
struct of_device_id oftable[]=
{
    {.compatible="hqyj,platform",},
    {}
};
//热插拔宏
MODULE_DEVICE_TABLE(of,oftable);
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="aaaaa",
        .of_match_table=oftable,//设备树匹配
    },
    
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

5、 测试现象

;