• Linux platform平台总线、平台设备、平台驱动


    平台总线(platform_bus)的需求来源?
    随着soc的升级,S3C2440->S3C6410->S5PV210->4412,以前的程序就得重新写一遍,做着大量的重复工作,
    人们为了提高效率,发现控制器的操作逻辑(方法)是一样的,只有寄存器地址不一样,如果将与硬件有关的
    代码(platform_device)和驱动代码(platform_driver)分开,升级soc后,因为驱动方式一样,
    只需要修改与硬件有关的代码就可以,实现一个驱动控制多个设备。

    平台(platform)总线是一种虚拟的总线,在 /sys/bus/platform 目录可以看到。
    平台总线三要素:平台总线、平台设备、平台驱动
    平台总线原则:先分离,后合并

    分离:
    将设备信息封装成 platform_device,将驱动信息封装成 platform_driver,并为各自起名称,
    然后将 platform_device 中的 struct device 和 platform_driver 中的 struct device_driver 分别注册到设备链表和驱动链表中。

    int platform_device_register(struct platform_device *pdev)

      return platform_device_add(pdev);

        ret = device_add(&pdev->dev);

    int platform_driver_register(struct platform_driver *drv)

      return driver_register(&drv->driver);

        ret = bus_add_driver(drv);


    合并:
    在系统每注册一个设备(驱动)时,平台总线会找与之匹配的驱动(设备),匹配原则是名称相同。

    装载(insmod)时设备和驱动没有顺序,卸载(rmmod)时必须先卸载设备文件,因为卸载设备会调用驱动中的 remove 函数。

    // 下面的“|”表示包含于上一个之中
    // 描述设备的信息
    struct platform_device {
        const char    * name;                    // 用于和platform_driver进行匹配的名字--自定义
        int        id;                      // 一般直接填-1,区分不同的控制组
        struct device    dev;                    // 父类
            |
            void    (*release)(struct device *dev);        // 设备卸载时调用的函数
            void    *platform_data;                // 匹配后传递的自定义数据
            ... ...
        u32        num_resources;                // 资源的个数
        struct resource    * resource;                 // 描述资源信息
            |    
            resource_size_t start;                // 起始位置
            resource_size_t end;                // 结束位置
            const char *name;                 // 自定义
            unsigned long flags;                 // 区分不同的资源,一般是 内存或者中断资源
            ... ...
     };
    // 描述设备的操作方法
    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 char        *name;                // 该名字可以用于匹配,但比id_table中的name优先级低
            // 此名称在 /sys/bus/platform/drivers/xxx
            ... ...
        const struct platform_device_id *id_table;
            |
            char name[PLATFORM_NAME_SIZE];                // 用于和platform_device的名字进行匹配,优先级高
    };
    // 平台总线
    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_devive

    int platform_device_register(struct platform_device *pdev);

    // 注销 platform_devive

    void platform_device_unregister(struct platform_device *pdev);

    // 注册 platform_driver

    int platform_driver_register(struct platform_driver *drv);

    // 注销 platform_driver

    void platform_driver_unregister(struct platform_driver *drv);

    // 批量注册pdev

    int platform_add_devices(struct platform_device **devs, int num);

    获取资源的接口:

    // 通过类型和编号获取资源

    // 参数1:pdev

    // 参数2:获取的资源类型

    // 参数3:获取的资源编号

    struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);

    // 通过类型和名称获取资源

    // 参数3:获取资源的名称

    struct resource * platform_get_resource_byname(struct platform_device * dev,unsigned int type,const char * name);

    // 通过编号获取中断资源

    int platform_get_irq(struct platform_device * dev,unsigned int num);

    // 通过名称获取中断资源

    int platform_get_irq_byname(struct platform_device * dev,const char * name);

    // 重点:资源编号一定是按照同种类型来排序的
    struct resource led_res[] = {
        [0] = { // 获取内存资源时,此内存资源的编号为0
            ... ...
            .flags = IORESOURCE_MEM,
        },
        [1] = { // 获取中断资源时,此中断资源的编号为0
            ... ...
            .flags = IORESOURCE_IRQ,
        },
        [2] = { // 获取内存资源时,此内存资源的编号为1
            ... ...
            .flags = IORESOURCE_MEM,
        },
    };

    plat_led_dev.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    
    #include "plat_led.h"
    
    #define GPL2_0        0x11000100
    #define GP_SIZE        8
    
    // 平台自定义数据,与硬件相关
    struct regled led_reg = {
        .ctl_clr = 0x0f,
        .ctl_set = 0x01,
        .dat_clr = 0x01,
        .dat_set = 0x01,
    }; 
    
    static struct resource led_res[] = {
        [0] = {
            .start = GPL2_0,
            .end = GPL2_0 + GP_SIZE - 1,
            .name = "led0",
            .flags = IORESOURCE_MEM,
        },
        [1] = {
            .start = 888,
            .end = 888,
            .name = "virt_irq",
            .flags = IORESOURCE_IRQ,
        },
    };
    
    void plat_led_release(struct device *dev)
    {
        // 为了卸载模块时不报错误
    }
    
    struct platform_device led_pdev = {
        .name = "plat_led",
        .id = -1,
        .dev = {
            .platform_data = &led_reg,
            .release = plat_led_release,
        },
        .num_resources = ARRAY_SIZE(led_res),
        .resource = led_res,
    };
    
    static int __init plat_led_dev_init(void)
    {
        platform_device_register(&led_pdev);
    
        return 0;
    }
    
    static void __exit plat_led_dev_exit(void)
    {
        platform_device_unregister(&led_pdev);
    }
    
    module_init(plat_led_dev_init);
    module_exit(plat_led_dev_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Aaron Lee");

    plat_led_drv.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/slab.h>
    #include <linux/platform_device.h>
    
    #include <asm/io.h>
    #include <asm/uaccess.h>
    
    #include "plat_led.h"
    
    struct samsung *platled;
    
    static int platled_open(struct inode *inode, struct file *fops)
    {
        writeb(readb(platled->reg_base+4) & (~platled->reg->dat_clr), platled->reg_base+4);
        
        return 0;
    }
    
    static int platled_close(struct inode *inode, struct file *fops)
    {
        return 0;
    }
    
    static ssize_t platled_write(struct file *fops, const char __user *buf, size_t size, loff_t *fpos)
    {
        int value;
    //暂时忽略返回值
        copy_from_user(&value, buf, size);
    
        if (value)
            writeb((readb(platled->reg_base+4) & (~platled->reg->dat_clr)) | platled->reg->dat_set, platled->reg_base+4);
        else
            writeb(readb(platled->reg_base+4) & (~platled->reg->dat_clr), platled->reg_base+4);
        
        return 0;
    }
    
    const struct file_operations platled_fops = {
        .open = platled_open,
        .release = platled_close,
        .write = platled_write,
    };
    
    static int led_register(void)
    {
        int ret;
    
        platled = kmalloc(sizeof(struct samsung), GFP_KERNEL);
        if (platled == NULL)
        {
            printk("kmalloc fail!
    ");
            return -ENOMEM;
        }
    
        platled->major = register_chrdev(0, "plat_led", &platled_fops);
        if (platled->major < 0)
        {
            printk("register_chrdev fail!
    ");
            ret = -EFAULT;
            goto chrdev_err;
        }
    
        platled->cls = class_create(THIS_MODULE, "plat_led");
        if (platled->cls < 0)
        {
            printk("class_create fail!
    ");
            ret = -EFAULT;
            goto class_err;
        }
    
        platled->dev = device_create(platled->cls, NULL, MKDEV(platled->major, 0), NULL, "plat_led");
        if (platled->dev < 0)
        {
            printk("device_create fail!
    ");
            ret = -EFAULT;
            goto device_err;
        }
    
        return 0;
    
    device_err:
        class_destroy(platled->cls);
    
    class_err:
        unregister_chrdev(platled->major, "plat_led");
    
    chrdev_err:
        kfree(platled);
    
        return ret;
    }
    
    static void led_unregister(void)
    {
        device_destroy(platled->cls, MKDEV(platled->major, 0));
        class_destroy(platled->cls);
        unregister_chrdev(platled->major, "plat_led");
        kfree(platled);
    }
    
    
    int plat_led_probe(struct platform_device *pdev)
    {
        int ret;
        struct resource *res;
    
        ret = led_register();
        if (ret < 0)
        {
            printk("plat_led_probe fail
    ");
            return -EFAULT;
        }
    
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL)
        {
            printk("platform_get_resource fail
    ");
            return -ENODEV;
        }
    // 获取平台自定义数据
        platled->reg = pdev->dev.platform_data;
            
        platled->reg_base = ioremap(res->start, resource_size(res));
        writel((readl(platled->reg_base) & (~platled->reg->ctl_clr)) | platled->reg->ctl_set, platled->reg_base);
    
        return 0;
    }
    
    int plat_led_remove(struct platform_device *pdev)
    {
        iounmap(platled->reg_base);
        led_unregister();
        
        return 0;
    }
    
    const struct platform_device_id led_id_table[]  = {
            {"plat_led", 0x1234}, //第二个整数是自定义
    };
    
    struct platform_driver led_pdrv = {
        .probe = plat_led_probe,
        .remove = plat_led_remove,
        .driver = {
            .name    = "red_led",
        },
        .id_table = led_id_table,// name用于匹配
    };
    
    static int __init plat_led_drv_init(void)
    {
        platform_driver_register(&led_pdrv);
    
        return 0;
    }
    
    static void __exit plat_led_drv_exit(void)
    {
        platform_driver_unregister(&led_pdrv);
    }
    
    module_init(plat_led_drv_init);
    module_exit(plat_led_drv_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Aaron Lee");

    plat_led.h

    #ifndef __PLAT_LED_H_
    #define __PLAT_LED_H_
    
    struct regled {
        unsigned long ctl_clr;
        unsigned long ctl_set;
        unsigned long dat_clr;
        unsigned long dat_set;
    };
    
    struct samsung {
        int major;
        struct class *cls;
        struct device *dev;
        struct regled *reg;
        void *reg_base;
    };
    
    #endif
    作者:AaronLee

    签名:永不言弃!

    转载请注明出处,谢谢!!!
  • 相关阅读:
    【HDU 4305】Lightning(生成树计数)
    【HDU 1150】Machine Schedule(二分图匹配)
    【HDU 2063】过山车(二分图匹配)
    透过Nim游戏浅谈博弈
    [SCOI2010]字符串
    [SCOI2010]传送带[三分]
    [SCOI2010]序列操作[分块or线段树]
    HDU 5306 Gorgeous Sequence[线段树区间最值操作]
    1455: 罗马游戏[左偏树or可并堆]
    Codevs 5914 [SXOI2016]最大值
  • 原文地址:https://www.cnblogs.com/wan0807/p/14292601.html
Copyright © 2020-2023  润新知