精华内容
下载资源
问答
  • Linux Platform驱动模型

    2019-05-28 17:53:13
    Linux Platform驱动模型(一) _设备信息:https://www.cnblogs.com/xiaojiang1025/p/6367061.html Linux Platform驱动模型(二) _驱动方法:...Linux Platform驱动模型(三) _platform+cdev:h...

    Linux Platform驱动模型(一) _设备信息:https://www.cnblogs.com/xiaojiang1025/p/6367061.html
    Linux Platform驱动模型(二) _驱动方法:https://www.cnblogs.com/xiaojiang1025/p/6367910.html
    Linux Platform驱动模型(三) _platform+cdev:https://www.cnblogs.com/xiaojiang1025/p/6369065.html

    展开全文
  • platform驱动详解

    千次阅读 2018-01-04 11:21:55
    platform总线是虚拟的平台总线,是linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。...platform驱动工作流程: 1. 系统开机内核初始化阶段,初始化platform总线; 2. platform_device初始化调用

    platform总线是虚拟的平台总线,是linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。
    总线将设备和驱动绑定,系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
    platform驱动工作流程:
    1. 系统开机内核初始化阶段,初始化platform总线;
    2. platform_device初始化调用platform_add_devices(一次注册多个设备)或者platform_device_register(一次注册单个设备),这部分一般在arch/arm/mach配置文件中,在上电开机的时候完成platform_device初始化
    3. platform_driver的初始化调用platform_driver_register或者driver_register,该过程一般在驱动程序的init函数中

    platform总线的初始化

    platform总线的初始化是在内核的初始化阶段完成的
    整个流程是kernel_init() –> do_basic_setup() –> driver_init() –> platform_bus_init()
    下面分析platform_bus_init() 函数

    struct device platform_bus = {  /*定义一个名为platform的总线设备*/
        .init_name  = "platform",   /*其他的platform设备都是它的子设备*/
    };
    
    struct bus_type platform_bus_type = {
        .name       = "platform",
        .dev_attrs  = platform_dev_attrs,
        .match      = platform_match,
        .uevent     = platform_uevent,
        .pm     = &platform_dev_pm_ops,
    };
    EXPORT_SYMBOL_GPL(platform_bus_type);
    
    int __init platform_bus_init(void)
    {
        int error;
    
        early_platform_cleanup();
        /* platform总线也是设备,对应的sysfs节点为/sys/devices/platform */
        error = device_register(&platform_bus);
        if (error)
            return error;
        error =  bus_register(&platform_bus_type);//注册平台类型的bus,将出现sys文  件系统在bus目录下,创建一个platform的目录,以及相关属性文件 
        if (error)
            device_unregister(&platform_bus);
        return error;
    }

    注册platform_device

    platform_device的注册一般放在arch/arm/mach配置文件中,在开机时被初始化,所以当注册platform_driver时会在platform总线上查找匹配的设备

    struct platform_device {
     const char * name;     /* 设备名 */
     u32 id;        //当有多个设备用来 区分 ,只有一个设备,id=-1
     struct device dev;
      struct resource    *  resource ;      /* 设备所使用各类资源数量 */
     struct resource * resource; /* 资源 */
     };

    这里有个重要的成员resource???该结构存入了资源信息

    struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
    };

    下面举 s3c6410 平台的 i2c 驱动作为例子来说明platform_device的注册过程:

    static struct platform_device *smdk6410_devices [] __initdata = {
    #ifdef CONFIG_SMDK6410_SD_CH0
        &s3c_device_hsmmc0,
    #endif
    #ifdef CONFIG_SMDK6410_SD_CH1
        &s3c_device_hsmmc1,
    #endif
        &s3c_device_i2c0 ,
        &s3c_device_i2c1,
        &s3c_device_fb,
        &s3c_device_usb,
        &s3c_device_usb_hsotg,
        &smdk6410_lcd_powerdev,
        &smdk6410_smsc911x,
    };

    把一个或几个设备资源放在一起,便于集中管理,其中IIC设备 platform_device如下:

    struct platform_device  s3c_device_i2c0  = {
        .name          = "s3c2410-i2c",
    #ifdef CONFIG_S3C_DEV_I2C1
        .id          = 0,
    #else
        .id          = -1,
    #endif
        .num_resources      = ARRAY_SIZE(s3c_i2c_resource ),
        .resource      = s3c_i2c_resource,
    };
    
    具体resource如下:
    static struct resource  s3c_i2c_resource [] = {
        [0] = {
            .start = S3C_PA_IIC,
            .end   = S3C_PA_IIC + SZ_4K - 1,
            .flags = IORESOURCE_MEM,
        },
        [1] = {
            .start = IRQ_IIC,
            .end   = IRQ_IIC,
            .flags = IORESOURCE_IRQ,
        },
    };

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

    定义好了platform_device 结构体后就可以调用函数 platform_add_devices向系统中添加该设备了,之后可以调用 platform_driver_register() 进行设备注册。
    s3c6410-i2cplatform_device是在系统启动时,在mach-smdk6410.c里的smdk6410_machine_init()函数里进行注册的,这个函数申明为arch_initcall的函数调用,arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考imach-smdk6410.c)

    static void __init smdk6410_machine_init(void)
    {
        s3c_i2c0_set_platdata(NULL);
        s3c_i2c1_set_platdata(NULL);
        s3c_fb_set_platdata(&smdk6410_lcd_pdata);
        gpio_request(S3C64XX_GPN(5), "LCD power");
        gpio_request(S3C64XX_GPF(13), "LCD power");
        gpio_request(S3C64XX_GPF(15), "LCD power");
        i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
        i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
        platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
        //添加多设备
    }
    
    下面分析一下platform_add_devices是如何注册platform_device的:
    int platform_add_devices(struct platform_device **devs, int num)
    {
        int i, ret = 0;
        for (i = 0; i < num; i++) {
             ret = platform_device_register(devs[i]);
            if (ret) {
                while (--i >= 0)
                    platform_device_unregister(devs[i]);
                break;
            }
        }
        return ret;
    }
    
    int platform_device_register(struct platform_device *pdev)
    {
        device_initialize(&pdev->dev);
        return platform_device_add(pdev);
    }
    
    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;
        pdev->dev.bus = &platform_bus_type;
        if (pdev->id != -1)
             //如果有id 表示有多个同类设备用 pdev->name和 pdev->id标识该设备
      dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
        else
            dev_set_name(&pdev->dev, "%s", pdev->name);
     //否则,只用 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; //   作为 IOMEM 资源分配  
                else if (resource_type(r) == IORESOURCE_IO)
                     p = &ioport_resource; //   作为 IO PORT资源分配  
            }
             if (p && insert_resource(p, r)) { // 将新的 resource 插入内核 resource tree
                printk(KERN_ERR
                       "%s: failed to claim resource %d\n",
                       dev_name(&pdev->dev), i);
                ret = -EBUSY;
                goto failed;
            }
        }
        pr_debug("Registering platform 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;
    }

    platform_driver注册

    platform_driver结构体初始化,以i2c为例

    static struct platform_driver s3c24xx_i2c_driver = {
        .probe        = s3c24xx_i2c_probe,
        .remove        = s3c24xx_i2c_remove,
        .suspend_late    = s3c24xx_i2c_suspend_late,
        .resume        = s3c24xx_i2c_resume,
        .id_table    = s3c24xx_driver_ids,
        .driver        = {
            .owner    = THIS_MODULE,
            .name    = "s3c-i2c",
        },
    };

    在驱动程序的初始化函数中调用platform_driver_add()注册platform_driver,中线的匹配函数(platrorm_mach)会匹配总线上已经存在的设备的名称(name字段),当设备与驱动匹配成功,调用驱动程序中的probe函数。

    展开全文
  • linux下的platform驱动

    2015-06-04 16:35:55
    linux下的platform驱动,包含有mini2440和linux3.5下的4412
  • Linux设备模型及Platform驱动 目标 理解Linux设备模型的内容和意义 掌握符合设备模型的驱动编写方法 掌握Platform设备驱动编写方法 第一章:Linux设备模型 第二章:Linux设备模型简单例程 第三章:Platform...
  • Linux platform驱动架构的解析与应用.pdf
  • 基于嵌入式Linux的platform驱动设计与应用.pdf
  • 上一篇博文《platform设备驱动框架搭建分析》主要是根据内核源码来分析platform驱动模型工作的原理,在实际的驱动开发中如何使用Linux的这么一种模型来管理这种类型的设备呢?把tq2440开发板上的LED1当做是平台设备...

    上一篇博文《platform设备驱动框架搭建分析》主要是根据内核源码来分析platform驱动模型工作的原理,在实际的驱动开发中如何使用Linux的这么一种模型来管理这种类型的设备呢?把tq2440开发板上的LED1当做是平台设备注册到Linux系统中,让系统可以用这种platform驱动来管理他。

    ①总线层:代码不用我们自己去写,内核已经提供了

    ②设备层:向platform总线层注册硬件相关的资源,一般是寄存器地址、内存空间、中断号(序号的一种代表)等等

    led_dev.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] = { //GPBCON
            .start = 0x56000010,
            .end   = 0x56000050 + 8 - 1,
            .flags = IORESOURCE_MEM, //一般地址类的操作就用这个flags
        },
        [1] = { //中断号
            .start = 5,
            .end   = 5,
            .flags = IORESOURCE_IRQ, //在这个例子中和中断并没有半毛钱的关系,纯粹就是表示一种序号的意思
        }
    
    };
    
    static void led_release(struct device * dev)
    {
    }
    
    /* 构造platform_device结构体 */
    static struct platform_device led_dev = {
        .name         = "myled",
        .id       = -1,
        .num_resources    = ARRAY_SIZE(led_resource),
        .resource     = led_resource,
        .dev = { 
        	.release = led_release, 
    	},
    };
    
    /* 向platform_bus注册一个设置好的platform_device */
    static int led_dev_init(void)
    {
    	platform_device_register(&led_dev);
    	return 0;
    }
    
    static void led_dev_exit(void)
    {
    	platform_device_unregister(&led_dev);
    }
    
    module_init(led_dev_init);
    module_exit(led_dev_exit);
    
    MODULE_LICENSE("GPL");

    ③驱动层:从总线那里获取需要的硬件资源,根据要求用它来完成一些硬件相关的操作,然后把驱动注册到总线,同时还需要完成probe成员函数--这是驱动和设备匹配之后的核心工作,这工作一般是向用户空间提供API,即实现并填充file_operation结构体成员函数

    led_drv.c

    #include <linux/module.h>
    #include <linux/version.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/interrupt.h>
    #include <linux/irq.h>
    #include <linux/sched.h>
    #include <linux/pm.h>
    #include <linux/sysctl.h>
    #include <linux/proc_fs.h>
    #include <linux/delay.h>
    #include <linux/platform_device.h>
    #include <linux/input.h>
    #include <linux/irq.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    static int major;
    static struct class *cls;
    static struct device *led_drv_device;
    
    /* 在platform_get_resource()和ioremap()之前,下面这变量没有任何实际意义 */
    static volatile unsigned long *gpio_con;
    static volatile unsigned long *gpio_dat;
    static int pin;
    
    static int led_open(struct inode *inode, struct file *file)
    {
    	/* 配置为输出 */
    	*gpio_con &= ~(0x3<<(pin*2));
    	*gpio_con |= (0x1<<(pin*2));
    	return 0;	
    }
    
    static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
    	int val;
    	copy_from_user(&val, buf, count);
    	if (val == 1)
    	{
    		// 点灯
    		*gpio_dat &= ~(1<<pin);
    	}
    	else
    	{
    		// 灭灯
    		*gpio_dat |= (1<<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;
    	/* 根据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);
    	cls = class_create(THIS_MODULE, "myled");
    	led_drv_device = device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    	return 0;
    }
    
    static int led_remove(struct platform_device *pdev)
    {
    	device_unregister(led_drv_device);
    	class_destroy(cls);
    	unregister_chrdev(major, "myled");
    	iounmap(gpio_con);
    	return 0;
    }
    
    struct platform_driver led_drv = {
    	.probe		= led_probe,
    	.remove		= led_remove,
    	.driver		= {
    		.name	= "myled",
    	}
    };
    
    
    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");

    小结:写一个platform驱动需要我们做哪些事情?
    ①一个xxx_drv.c文件:

    驱动初始化和注销函数:
    xxx_drv_init()

    platform_driver_register(struct platform_driver *drv);

    xxx_drv_exit()

    platform_driver_unregister(struct platform_driver *drv);

    驱动初始化原材料:
    struct platform_driver xxx_drv = {

    .probe = xxx_probe,

    .remove = xxx_remove,

    .driver = {

    .name = "xxx",

    }

    };
    实现原材料的成员函数:
    xxx_probe(struct platform_device *pdev)
    {

    //工作:

    //1.根据需要向总线获取对应设备资源,

    //2.然后拿着这资源该干嘛就干嘛去,有一点是通常都要做的:给应用层提供接口xxx_read() xxx_write() xxx_open() xxx_close()

    }
    xxx_remove(struct platform_device *pdev)
    {

    //把刚才probe函数里边注册申请的空间释放掉,动作刚好相反

    }

    ②一个xxx_dev.c文件:
    设备初始化和注销函数:
    xxx_dev_init()

    platform_device_register(struct platform_device *pdev);

    xxx_dev_exit()

    platform_device_unregister(struct platform_device *pdev);

    设备初始化原材料:
    static struct platform_device xxx_dev = {

    .name         = "xxx",  //重构name成员

    .id       = -1,

    .num_resources    = ARRAY_SIZE(xxx_resource),

    .resource     = xxx_resource,

    .dev = { 

    .release = xxx_release,  //这个函数是要自己重定义的不然加载驱动模块时会出错,哪怕这个函数里边什么都不做都好。

    },

    };
    定义并构造xxx_resource资源:有如下成员
    struct resource {

    resource_size_t start;

    resource_size_t end;

    const char *name;

    unsigned long flags; //只是一种代表

    struct resource *parent, *sibling, *child;

    };








    展开全文
  • platform驱动框架是利用总线(bus_type)+驱动(driver)+设备(device)模型建立的驱动框架。这个模型中总线只有一条,而驱动和设备可以有多个,驱动和设备全都挂接到总线上,总线完成驱动和设备的匹配工作,一旦驱动和...

    介绍

    platform驱动框架是利用总线(bus_type)+驱动(driver)+设备(device)模型建立的驱动框架。这个模型中总线只有一条,而驱动和设备可以有多个,驱动和设备全都挂接到总线上,总线完成驱动和设备的匹配工作,一旦驱动和设备相匹配,则会执行驱动中的probe函数将驱动注册到系统。同时驱动还可以调用设备中写好的资源(resource)来区别不同的设备,该驱动框架的好处是:
    1. 设备可热插拔。
    2. 多个设备可共用同一驱动。
    下面给出platfom驱动框架的使用模板并且分析它在内核中的实现。

    platform驱动框架代码模板

    1. platform_driver

    该驱动注册进系统后, 如果platform_device注册, 则执行probe()函数, 如果platform_device注销, 则执行remove()函数。设备和驱动的匹配只使用了名字进行匹配。

    struct xxx_dev {
        ...
    };
    
    // platform_drvier和platform_device匹配时会调用此函数
    static int xxx_probe(struct platform_device *pdev)
    {
        struct xxx_dev *my_dev;
        // devm_xxx的函数会自动回收内存
        my_dev = devm_kzalloc(&pdev->dev, sizeof(*gl), GFP_KERNEL);
        ...
        platform_set_drvdata(pdev, my_dev);
    }
    
    // platform_device从系统移除时会调用此函数
    static int xxx_remove(struct platform_device *pdev)
    {
        struct xxx_dev *my_dev = platform_get_drvdata(pdev);
        ...
    }
    
    static struct platform_driver xxx_driver = {
        .driver = {
            .name = "xxx",              // device_driver中只定义了名字
            .owner = THIS_MODULE,
        },
        .probe = xxx_probe,
        .remove = xxx_remove,
    };
    // 注册platform driver到platform bus
    module_platform_driver(xxx_driver);

    2. platform_device

    这部分代码使用模块的形式完成platform_device的注册和注销

    static struct platform_device *xxx_pdev;
    
    static int __init xxx_dev_init(void)
    {
        int ret;
        // 注意此名字和platform_driver中的名字一致
        xxx_pdev = platform_device_alloc("xxx", -1);
        if (!xxx_pdev)
            return -ENOMEM;
        // 注册platform_device到系统
        ret = platform_device_add(xxx_pdev);
        if (ret) {
            platform_device_put(xxx_pdev);
            return ret;
        }
    
        return 0;
    }
    module_init(xxx_dev_init);
    
    static void __exit xxx_dev_exit(void)
    {
        // 从系统中注销platform_device
        platform_device_unregister(xxx_pdev);
    }
    module_exit(xxx_dev_exit);

    将这两个驱动编译后分别插入系统,由于设备和驱动的名称一致,系统会自动将其相互匹配,从而执行probe函数。

    platform_driver的注册

    1. module_platform_driver宏

    首先分析platform_driver的注册过程。模板中仅仅使用了一个宏就完成了注册,该宏就是module_platform_driver,其中包含了module_initmodule_exit宏,展开后实际上是使用platform_driver_register()platform_driver_unregister()函数对驱动进行注册和注销。

    // linux/include/linux/platform_device.h :
    #define module_platform_driver(__platform_driver) \
        module_driver(__platform_driver, platform_driver_register, \
                platform_driver_unregister)
    
    // linux/include/linux/device.h :
    #define module_driver(__driver, __register, __unregister, ...) \
    static int __init __driver##_init(void) \
    { \
        return __register(&(__driver) , ##__VA_ARGS__); \
    } \
    module_init(__driver##_init); \
    static void __exit __driver##_exit(void) \
    { \
        __unregister(&(__driver) , ##__VA_ARGS__); \
    } \
    module_exit(__driver##_exit);

    2. platform_driver_register函数

    从上面的分析可以看到,系统实际上是调用了platform_driver_register()函数进行注册,下面给出该函数的调用过程。这里只是给出了关键的函数调用,即便如此该调用过程仍然很复杂,我们一条一条来分析。

    --- include --- linux --- platform_device.h --- platform_driver_register(drv) --- __platform_driver_register(drv,THIS_MODULE)
     |
     |- drivers --- base --- platform.c --- __platform_driver_register(    --- drv->driver.bus = &platform_bus_type;
                 |                   struct platform_driver *drv,  |- drv->driver.probe = platform_drv_probe
                 |                   struct module *owner)         |- drv->driver.remove = platform_drv_remove
                 |                                                 |- driver_register(&drv->driver)
                 |
                 |- driver.c --- driver_register(             --- driver_find(drv->name, drv->bus)
                 |                 struct device_driver *drv)  |  //查找总线中是否已注册驱动, 若已注册直接退出
                 |                                             |- bus_add_driver(drv)
                 |- bus.c --- bus_add_driver(              --- struct driver_private *priv
                           |    struct device_driver *drv)  |- priv = kzalloc(sizeof(*priv), GFP_KERNEL)
                           |                                |- bus = bus_get(drv->bus)
                           |                                |- priv->driver = drv
                           |                                |- drv->p = priv
                           |                                |- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)
                           |                                |  //driver添加到bus
                           |                                |- driver_attach(drv)
                           |                                |  //尝试绑定driver和device
                           |                                |- driver_create_file(drv, &driver_attr_uevent)
                           |                                |  //在sysfs中创建文件
                           |- bus_for_each_dev(       --- while ((dev = next_device(&i)) && !error)
                           |   struct bus_type *bus,  |      error = fn(dev, data)
                           |   struct device *start,  |  //遍历总线上的设备,执行fn函数
                           |   void *data,
                           |   int (*fn)(struct device *, void *))
                           |
                           |- dd.c --- driver_attach(             --- bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
                                    |    struct device_driver *drv)
                                    |- __driver_attach(      --- struct device_driver *drv = data
                                    |    struct device *dev,  |- driver_match_device(drv, dev)
                                    |    void *data)          |  //查看driver和device是否匹配,若不匹配直接退出 
                                    |                         |  //这里调用的是bus_type->match函数
                                    |                         |- driver_probe_device(drv, dev)
                                    |- driver_probe_device(       --- really_probe(dev, drv)
                                    |    struct device_driver *drv, 
                                    |    struct device *dev)
                                    |- really_probe(               --- driver_sysfs_add(dev))//添加device到sysfs
                                    |    struct device *dev,        |- if(dev->bus->probe)
                                    |    struct device_driver *drv) |      dev->bus->probe(dev);
                                    |                               |  else if(drv->probe)
                                    |                               |      drv->probe(dev);
                                    |                               |- driver_bound(dev)//设备和驱动绑定
                                    |- driver_bound(         --- klist_add_tail(&dev->p->knode_driver,//把device添加到driver
                                         struct device *dev)  |                 &dev->driver->p->klist_devices);

    platform_driver其实是device_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;
    };
    • 首先从platform_driver_register()函数开始,可以追到__platform_driver_register()函数,该函数中关键的一点是将驱动的总线类型设置为platform_bus_type,通过driver_register(&drv->driver)device_driver注册进系统。
    • 再看driver_register()函数,该函数首先调用driver_find()函数在总线上搜索是否有同名的驱动,如果有说明该驱动已经注册,直接退出。如果没有接下来调用bus_add_driver()函数将驱动添加到总线。
    • 再进入bus_add_driver()函数,该函数中首先申请了一个priv结构,然后将drv->p = priv,最后通过klist_add_tail()函数priv添加bus->p的链表当中从而完成了驱动添加到总线的操作。接下来执行driver_attach()函数尝试绑定总线上的驱动和设备。
    • 接下来分析driver_attach()函数,将函数中的bus_for_each_dev()函数展开,可以看成该函数遍历总线上的所有设备dev,然后执行__driver_attach(dev, drv)
    • 然后是__driver_attach()函数,该函数首先通过driver_match_device()函数判断驱动和设备是否匹配,若不匹配直接退出. 注意, 该函数的实现实际上就我们常说的bus->match函数:
    static inline int driver_match_device(struct device_driver *drv,
                          struct device *dev)
    {
        return drv->bus->match ? drv->bus->match(dev, drv) : 1;
    }

    bus->match函数实际上绑定的是 platform_match()函数, 该函数的实现如下:

    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);
    
        /* When driver_override is set, only bind to the matching driver */
        if (pdev->driver_override)
            return !strcmp(pdev->driver_override, drv->name);
    
        /* Attempt an OF style match first */
        // 匹配上之后直接返回,不进行下一步匹配
        if (of_driver_match_device(dev, drv))
            return 1;
    
        /* Then try ACPI style match */
        if (acpi_driver_match_device(dev, drv))
            return 1;
    
        /* Then try to match against the id table */
        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);
    }

    从该函数可以看到先进行设备树匹配, 最后进行名字匹配.

    • 接着分析__driver_attach()函数, 如果设备和驱动匹配, 则执行driver_probe_device()函数,该函数可以追到really_probe()函数。
    • really_probe()函数中可以看到执行了我们自己定义的probe函数,然后通过driver_bound()函数绑定设备和驱动。
    • driver_bound()函数中,和将驱动绑定到总线类似,同样调用了klist_add_tail()函数将设备加入了driver->p链表的尾部。

    platform_device的注册

    了解了platform_driver的注册过程,分析platform_device的注册过程就很简单了,因为有很多地方都是相似的。platform_device的注册过程主要包括两个函数:

    // 申请一个platform_device结构
    platform_device_alloc()
    // 向系统添加设备
    platform_device_add()

    1. platform_device_alloc()函数的调用过程

    struct platform_object {
        struct platform_device pdev;
        char name[];
    };
    
    drivers --- base --- platform.c --- platform_device_alloc( --- struct platform_object *pa
                                          const char *name,     |- pa = kzalloc(sizeof(*pa)+strlen(name)+1, GFP_KERNEL)
                                          int id)               |- strcpy(pa->name, name)
                                                                |- pa->pdev.name = pa->name
                                                                |- pa->pdev.id = id
                                                                |- device_initialize(&pa->pdev.dev)

    之前提到platform_driverdriver的子类,这里的情况和其类似,platform_device也是device的子类:

    struct platform_device {
        const char  * name;
        int     id;
        struct device   dev;
        u32     num_resources;
        struct resource * resource;
        struct platform_device_id   *id_entry;
        struct pdev_archdata    archdata;
    };

    这里重要一点就是调用device_initialize()函数给platform_device.dev进行了初始化

    2. platform_device_add()函数的调用过程

    drivers --- base --- platform.c --- platform_device_add(            --- pdev->dev.bus = &platform_bus_type
                      |                   struct platform_device *pdev)  |- dev_set_name(&pdev->dev, "%s", pdev->name)
                      |                                                  |- device_add(&pdev->dev)
                      |
                      |- core.c --- device_add(           --- kobject_add(&dev->kobj, dev->kobj.parent, NULL)
                      |               struct device *dev)  |- device_create_file(dev, &dev_attr_uevent)
                      |                                    |  //sysfs中创建文件
                      |                                    |- bus_add_device(dev)
                      |                                    |  //添加设备到总线
                      |                                    |- bus_probe_device(dev)
                      |                                    |  //执行probe函数
                      |
                      |- bus.c --- bus_add_device(       --- struct bus_type *bus = bus_get(dev->bus)
                      |         |    struct device *dev)  |- klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices)
                      |         |                            //添加设备到总线
                      |         |- bus_probe_device(     --- struct bus_type *bus = dev->bus
                      |         |    struct device *dev)  |- device_attach(dev)
                      |         |- bus_for_each_drv(               --- while ((drv = next_driver(&i)) && !error)
                      |              struct bus_type *bus,          |-     error = fn(drv, data);
                      |              struct device_driver *start,   |  //遍历总线上的设备,执行fn
                      |              void *data, 
                      |              int (*fn)(struct device_driver *, void *))
                      |
                      |- dd.c --- device_attach(        --- if (dev->driver) 
                               |    struct device *dev)  |      device_bind_driver(dev);
                               |                         |  else
                               |                         |      bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
                               |
                               |- __device_attach(             --- if (!driver_match_device(drv, dev))
                                    struct device_driver *drv,  |      return 0;
                                    void *data)                 |  return driver_probe_device(drv, dev);
                                                                |  //这里的两个函数和注册驱动时的两个函数一致
    
    • 申请到的platform_device由这个函数添加到系统,该函数进入后,首先是将pdev->dev.bus设置为platform_bus_type,这意味着该设备会挂载到platform_bus上。然后执行device_add()函数向系统添加设备。
    • device_add()函数进入后,使用bus_add_device()函数向总线添加设备,然后使用bus_probe_device()函数调用我们的probe函数。
    • bus_add_device()函数中,也是调用了klist_add_tail()将设备添加到系统链表当中,完成设备和总线绑定的操作。
    • bus_probe_device()函数中,最终调用device_attach()函数尝试绑定同一总线上的驱动和设备。
    • device_attach()函数中,首先判断dev->driver是否已经指定了驱动,否则在总线中搜索所有驱动,找到和设备匹配的那一个,遍历总线驱动使用的是bus_for_each_drv()函数,可以看到最终调用的是__device_attach()来尝试绑定设备和驱动。
    • 以上程序流程和驱动的注册如出一辙,接下来进入__device_attach()调用的就是和驱动中完全相同的函数了:使用driver_match_device()判断设备和驱动是否匹配,然后调用driver_probe_device()调用我们的probe函数。
    展开全文
  • platform驱动看驱动模型

    千次阅读 2010-12-06 22:27:00
    platform驱动看驱动模型
  • ASoC Platform驱动代码框架图

    千次阅读 2016-09-09 08:39:43
     在更深入地阅读了工程中 Audio 部分的驱动代码之后,整理出了一个 ASoC Platform 驱动代码的框架图。类似的 ASoC Machine驱动代码框架图在《ASoC Machine 驱动代码框架图》中,ASoC Codec驱动代码框架图在《ASoC ...
  • 我们编写代码时根据相关要求把设备和驱动挂在上面,完成platform驱动的编写了  2、编写驱动:我们需要编写两个文件,一个是平台设备文件,一个是平台驱动文件。 (1)平台设备文件 首先,编写描述设备的结构体 ...
  • platform驱动学习一之led实例

    千次阅读 2017-04-03 21:51:35
    前几天学习了platform驱动,学习过程中参考了网上的好多篇博客,总算是对platform驱动有了一定的了解,写个博客总结一下!下面我将对platform驱动代码及驱动整个流程进行分析,加之我的理解! 学习platform驱动之前...
  • 需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并
  • linux platform 驱动模型分析

    千次阅读 2013-01-28 15:16:51
    linux platform 驱动模型分析 一. 概述  platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,...
  • Linux Platform驱动模型(二) _驱动方法

    千次阅读 2017-02-08 11:53:32
    在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platform_driver对象? 如何将驱动...
  • platform驱动学习

    2016-08-03 00:57:29
    platform实现的是一种总线设备驱动模型,所谓的总线是虚拟的总线,挂在这个虚拟总线上的设备和驱动可以被内核以platform的机制探测到并使用probe进行初始化驱动platform的概念和字符设备驱动,块设备驱动,网卡...
  • 配套博文https://blog.csdn.net/szm1234/article/details/113705050
  • platform驱动简介

    千次阅读 2013-10-24 21:52:29
    目的:说白了就是为了将设备与驱动分离,通过platform总线进行连接 废话不多说: 相关结构介绍: 1.platform设备 结构体structplatform_device{ const charname;/*设备名*/ u32 id;/*设备id*/ struct device ...
  • platform驱动模型使用总结

    千次阅读 2018-07-21 18:13:17
    platform平台总线驱动的编写主要分为两个部分: 一个是platform_device部分,主要是提供设备本身对驱动处理所要求的参数。 另一个是platform_driver部分,主要是利用platform_device这边传递过来的参数提供对硬件...
  • platform驱动管理机制

    千次阅读 2011-02-14 15:58:00
    ============================================ 作者:yuanlulu ... 版权没有,但是转载请保留此段声明 ============================================ ...platform驱动管理机制 Linux2.6内核引入了一套新的驱动
  • Linux下面一个简单的虚拟platform驱动

    千次阅读 2016-03-17 14:21:59
     * Linux下面一个简单的虚拟platform驱动  */ 在Linux之中,约定如果设备不属于任何的总线,则可以把它注册为虚拟的platform设备。 下面就简单来学习一下一个简单的platform设备是怎么创建出来的。 ...
  •  platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,...
  • 那么我们编写platform模型驱动时,需要完成两个工作:1:实现platform驱动 2:实现platform设备,然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很...
  • Platform驱动程序

    2013-01-18 01:47:50
    Platform 驱动与传统的设备驱动模型相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。   平台设备描述 平台设
  • Linux 设备模型 --- Platform 驱动程序

    千次阅读 2013-05-04 11:27:16
    Platform 总线 : Platform 总线是 Linux2.6 内核加入的一种虚拟总线. Platform 机制的本身使用并不复杂,...Platform 驱动与 传统的设备驱动模型相比,优势在于 Platform 机制 将设备本身的资源注册进内核,由内

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,312
精华内容 32,124
关键字:

platform驱动