linux-platform机制实现驱动层分离(详解)

https://www.cnblogs.com/lifexy/p/7569371.html

自己理解:

主要是把设备和驱动进行分离:

device:主要是硬件描述相关。地址,中断等等;

driver:主要是驱动硬件工作,连接硬件与上层应用的桥梁;

device与driver就通过某种机制进行连接起来。就是通过platform_bus_type,把驱动和设备联系起来。

platform_bus_type是bus type的一种,其定义如下:

struct bus_type platform_bus_type={

.name = "platform",

.dev_attrs = platfrom_dev_attrs,

.uevent = platfrom_uevent,

.match = platform_match,

.suspend = platform_suspend,

.suspend_late = platform_suspend_late,

.resume = platform_resume,

.resume_early = platform_resume_early,

}

只要有一方注册,就会调用platform_bus_type的.match匹配函数,来找对方,成功就调用driver驱动结构体里的.probe函数来使总线将设备和驱动联系起来

device:先声明(定义),再注册

driver:先声明(定义),在注册

platform_driver_register()函数,主要是注册到platforn_bus_type虚拟总线上,如下示例:

int platform_driver_register(struct platform_driver *drv) {

drv->driver.bus = &platform_bus_type; //(1)挂接到虚拟总线

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;

if (drv->suspend) drv->driver.suspend = platform_drv_suspend;

if (drv->resume) drv->driver.resume = platform_drv_resume;

return driver_register(&drv->driver); //(2) 注册到driver目录下 }

挂接到虚拟总线platform_bus_type上,然后会调用platform_bus_type下的platform_match匹配函数,来匹配device和driver的名字

若名字匹配成功,则调用driver的.probe成员函数,然后放到/sys/bus/platform/driver目录下,其中driver_register()函数就是用来创建dirver目录的

定义:

platform_device{

name  //设备名称,要与platform_driver的name一样,这样总线才能匹配成功

id //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1

dev //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)

num_resource //资源数量,

resource //资源结构体,保存设备的信息

}

resource{

start //起始资源,如果是地址的话,必须是物理地址

end //结束资源,如果是地址的话,必须是物理地址

name //资源名

flag //资源的标志,比如IORESOURCE_MEM,表示地址资源, IORESOURCE_IRQ表示中断引脚... ...

struct resource *parent, *sibling, *child; //资源拓扑指针父、兄、子,可以构成链表

}

完成定义后,进行实例填充,比如

led_resource[]={

[0] = {

.start = 0x56000050, //led的寄存器GPFCON起始地址

.end = 0x56000050 + 8 - 1, // led的寄存器GPFDAT结束地址

.flags = IORESOURCE_MEM, //表示地址资源

},

[1] = {

.start = 5, //表示GPF第几个引脚开始

.end = 5, //结束引脚

flags = IORESOURCE_IRQ, //表示中断资源

}

}

led_device={

}

梳理清晰以上信息,我们就可以编写代码把上面的信息给关联起来运用。

led_device.c设备代码的结构如下:

device_init()

device_exit()

module_init()

module_exit()

MODULE_LICENCE()

下面我们再看看driver:

同样需要先定义:驱动主要工作会包括匹配卸载,休眠唤醒;

platform_driver{

.probe

.remove

.suspend

.resume

.driver

}

定义清楚后就需要填充实体:

led_driver={

}

填充完成后就同样编码对上面的信息进行关联起来运用,代码结构如下:

led_driver.c:

driver_init()

driver_exit()

module_init()

module_exit()

MODULE_LICTENCE()

下面请看完整代码:

led_device.c

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

static struct resource led_resource[] = {               //资源数组
    [0] = {
        .start = 0x56000050,                     //led的寄存器GPFCON起始地址
        .end   = 0x56000050 + 8 - 1,     // led的寄存器GPFDAT结束地址
        .flags = IORESOURCE_MEM,      //表示地址资源
    },
    [1] = {
        .start =  5,                                   //表示GPF第几个引脚开始
        .end   = 5,                            //结束引脚
        .flags = IORESOURCE_IRQ,     //表示中断资源    
    } 
};

static void led_release(struct device * dev)       //释放函数
{}

static struct platform_device led_dev = {
    .name         = "myled",                    //对应的platform_driver驱动的名字
    .id       = -1,                              //表示只有一个设备
    .num_resources    = ARRAY_SIZE(led_resource), //资源数量,ARRAY_SIZE()函数:获取数量
    .resource     = led_resource,      //资源数组led_resource
    .dev = {
    .release = led_release,             //释放函数,必须向内核提供一个release函数, 、
                                       //否则卸载时,内核找不到该函数会报错
       },
};
static int led_dev_init(void)    //入口函数,注册dev设备
{
  platform_device_register(&led_dev);
  return 0;
}

static void led_dev_exit(void) //出口函数,注销dev设备
{
  platform_device_unregister(&led_dev); 
}
module_init(led_dev_init);   //修饰入口函数
module_exit(led_dev_exit);  //修饰出口函数
MODULE_LICENSE("GPL");   //声明函数

led_driver.c

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

/*函数声明*/
static  int  led_remove(struct platform_device *led_dev);
static  int led_probe(struct platform_device *led_dev);

struct platform_driver led_drv = {
       .probe           = led_probe,        //当与设备匹配,则调用该函数
       .remove         = led_remove,             //删除设备

       .driver            = {
              .name     = "myled",           //与设备名称一样
       }
};
static struct class *cls;                                      //类,用来注册,和注销
static volatile unsigned long *gpio_con;         //被file_operations的.open函数用
static volatile unsigned long *gpio_dat;          //被file_operations的.write函数用
static int pin;                                                 //LED位于的引脚值

static int led_open(struct inode *inode, struct file  *file)
 {
       *GPFcon&=~(0x03<<(LED_PIN*2));
       *GPFcon|=(0x01<<(LED_PIN*2));   
       return 0;
 } 

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
       int val=0;
       if(count!=1)
              return -EINAL;
       copy_from_user(&val,buf,count);      //从用户(应用层)拷贝数据       

       if(val)      //开灯
       {
       *GPFdat&=~(0x1<<LED_PIN);
       }
       else
       {
       *GPFdat |= (0x1<<LED_PIN);
       }   
       return 0 ;
}


static struct  file_operations led_fops= {
    .owner  =   THIS_MODULE,     //被使用时阻止模块被卸载
    .open   =   led_open,     
    .write   =   led_write,   
  };

 
static int led_probe(struct platform_device *pdev)
{
       struct resource      *res;
       printk("enter probe\n");

       /* 根据platform_device的资源进行ioremap */
       res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取寄存器地址
       gpio_con = ioremap(res->start, res->end - res->start + 1); //获取虚拟地址
       gpio_dat = gpio_con + 1;

       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);   //获取引脚值
       pin = res->start;

       /* 注册字符设备驱动程序 */
       major = register_chrdev(0, "myled", &led_fops);   //赋入file_operations结构体
       cls = class_create(THIS_MODULE, "myled");
       class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
       return 0;
}
static int led_remove(struct platform_device *pdev)
{
       /* 卸载字符设备驱动程序 */
       printk("enter remove\n");
       class_device_destroy(cls, MKDEV(major, 0));
       class_destroy(cls);
       unregister_chrdev(major, "myled");

       iounmap(gpio_con);       //注销虚拟地址
       return 0;
}
static int led_drv_init(void)           //入口函数,注册驱动
{
       platform_driver_register(&led_drv);
       return 0;
} 
static void led_drv_exit(void)       //出口函数,卸载驱动
{
       platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);     
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

猜你喜欢

转载自blog.csdn.net/haigand/article/details/89948234
今日推荐