Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform

/***********************************************************************************
 *                    
 *                           mdev,bus,device,driver,platform
 *
 *   声明:
 *       1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
 *          不对齐,从而影响阅读.
 *       2. 由于本人水平有限,很难阐述清楚bus device driver platform的关系
 *          所以强烈要求您详细参考本次提供的预热文章.
 *
 *
 *                                          2015-3-21 晴 深圳 尚观 Opt 曾剑锋
 **********************************************************************************/

                        \\\\\\\\\\\--*目录*--//////////
                        |  一. 预热文章:              
                        |  二. mdev 原理及配置:       
                        |  三. bus device driver接口: 
                        |  四. platform bus接口       
                        \\\\\\\\\\\\\\\////////////////

一. 预热文章:
    1. linux设备驱动模型(上):
        http://m.blog.csdn.net/blog/zhuzongwei1988/5785461
    2. [嵌入式Linux学习七步曲之第四篇 Linux内核移植]详解Linux2.6内核中
        基于platform机制的驱动模型:
        http://blog.csdn.net/sailor_8318/article/details/5267698
    3. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2.6.25平台
        下的I2C驱动架构分析:
        http://blog.csdn.net/sailor_8318/article/details/5905988
    4. [嵌入式Linux学习七步曲之第五篇 Linux内核移植]PowerPC+Linux2.6.25平台
        下的SPI驱动架构分析:
        http://blog.csdn.net/sailor_8318/article/details/5977733

二. mdev 原理及配置:
    1. 在/etc/init.d/rcS中最后执行命令:
        #采用设备模型进行创建设备节点的必须加上,热插拔处理
        echo "/sbin/mdev" > /proc/sys/kernel/hotplug
        mdev -s
    2. mdev扫描/sys/lock(块设备保存在/sys/block目录下,2.6.25版本以后,块设备也保存在
        /sys/class/block目录下.mdev扫描/sys/block是为了实现向后兼容和/sys/class两个
        目录下的dev属性文件,从该dev属性文件中获取设备编号(dev属性文件以"major:minor\n"
        形式保存设备编号),并以包含该dev属性文件的目录名称作为设备名device_name(即包含
        dev属性文件的目录为device_name,而/sys/class和/device_name之间的那部分目录称为
        subsystem,也就是每个dev属性文件所在的路径都可表示/sys/class/subsystem/device_name/dev),
    3. 并在/dev目录下创建相应的设备文件.例如:
        cat /sys/class/tty/tty0/dev会得到4:0,subsystem为tty,device_name为tty0.
    4. 系统运行起来以后,每次创建新的节点的时候都会调用mdev,并根据/etc/mdev.conf文件
        做相应的事,如果配置中没有对应的配置,那就按常规处理:
        cat > /etc/mdev.conf << EOF
        misc_dev            0:0     0600    =test/my_device
        event.*             0:0     0600    =input/
        mice                0:0     0600    =input/
        mouse0              0:0     0600    =input/
        dsp                 0:0     0600    =sound/
        sdb[0-9]            0:0     0644    * /sbin/auto_mount
        EOF
        配置解析:
        1. 格式: <device regex> <uid>:<gid> <octal permissins> [<@|$|*> <command>]
            1. @在创建设备节点后运行命令;
            2. $在删除设备节点前运行命令;
            3. *创建设备节点后和创建设干杯节点前都会运行命令;
        2. =input: 表示将mice放在/dev/input目录下;
        3. =test/my_device: 表示将misc_dev改名字为my_device,并放在/dev/test目录下;
    5. 按照上面的操作,可以实现U盘的自动挂载.

三. bus device driver接口:
    1. 总线注册:
        struct bus_type bus;
        bus_register(&bus);
    2. 总线注销:
        bus_unregister(&bus);
    3. 设备注册:
        struct device dev;
        device_register(&dev);
    4. 设备注销:
        device_unregister(&dev);
    5. 驱动注册:
        struct device_driver drv;
        driver_register(&drv);
    6. 驱动注销:
        driver_unregister(&drv);
    7. bus device接口实例Demo:
        ...
        //总线通过match函数决定总线匹配规则,返回0代表匹配失败
        int up_match(struct device *dev, struct device_driver *drv)
        {
            printk("try to match!\n");
        
            /** 
             * 通过名字匹配设备和驱动,init_name中的值会赋给kobj.name
             * 并且init_name中的值会变成NULL,所以如果要通过名字匹配
             * 设备驱动,需要比较的是dev->kobj.name和drv->name的值.
             * 如下方式是错误的:
             *     return !strcmp(dev->init_name, drv->name);
             */
            return !strcmp(dev->kobj.name, drv->name);
        }
        
        struct bus_type up_bus = {
            .name   = "niubi_bus",
            .match  = up_match,
        };
        EXPORT_SYMBOL(up_bus);
        
        void test_release(struct device *dev)
        {
        }
        
        struct device up_dev = {
            .init_name  = "bus_device",
            .release    = test_release,
        };
        EXPORT_SYMBOL(up_dev);
        
        int __init test_init(void)
        {
            int ret;
        
            ret = bus_register(&up_bus);
            if(ret)
            {
                printk("bus_register FAILED!\n");
                goto err0;
            }
        
            ret = device_register(&up_dev);
            if(ret)
            {
                printk("device_register FAILED!\n");
                goto err1;
            }
        
            return ret;
        
        err1:
            bus_unregister(&up_bus);
        err0:
            return ret;
        }
        
        void __exit test_exit(void)
        {
            bus_unregister(&up_bus);
        }
        ...
    
    8. device_driver接口实例Demo:
        ...
        extern struct bus_type up_bus;
        
        //dev指向匹配的设备结构
        static int up_probe(struct device *dev)
        {
            printk("Probe.\n");
        
            return 0;
        }
        
        static int up_remove(struct device *dev)
        {
            printk("Remove.\n");
        
            return 0;
        }
        
        struct device_driver drv = {
            .owner  = THIS_MODULE,
            .name   = "niubi_dev", //match的时候要用到
            .bus    = &up_bus,
            .probe  = up_probe,
            .remove = up_remove,
        };
        
        int __init test_init(void)
        {
            int ret;
            ret = driver_register(&drv);
            if(ret)
                printk("driver_register FAILED!\n");
        
            return ret;
        }
        
        void __exit test_exit(void)
        {
            driver_unregister(&drv);
        }
        ...

四. platform bus接口
    1. 注册平台设备:
        struct platform_device pdev;
        platform_device_register(&dev);
    2. 注销平台设备:
        platform_device_unregister(&dev);
    3. 注册平台驱动:
        struct platform_driver pdrv;
        platform_driver_register(&pdrv);
    4. 注销平台驱动:
        platform_driver_unregister(&pdrv);

    5. platform_device接口Demo:

        #include <linux/module.h>
        #include <linux/platform_device.h>
        
        //定义自己的平台数据结构
        struct my_platform_data {
            int w;
            int h;
            char name[20];
        };
        
        struct my_platform_data pdata = {
            .w      = 800,
            .h      = 480,
            .name   = "lcd_screen",
        };
        
        struct resource res[] = {
            [0] = {
                .start  = 0x10000000,
                .end    = 0x10000000 + SZ_128 - 1,
                .flags  = IORESOURCE_MEM,
            },
            [1] = {
                .start  = 0x20000000,
                .end    = 0x20000000 + SZ_128 - 1,
                .flags  = IORESOURCE_MEM,
            },
            [2] = {
                .start  = 0x30,
                .end    = 0x30,
                .flags  = IORESOURCE_IRQ,
            },
        };
        
        void test_release(struct device *dev)
        {
        }
        
        //设备的resource保存在设备结构里
        struct platform_device pdev = {
            .name   = "device_v3",
            .id     = -1,
            .dev    = {
                .release        = test_release,
                .platform_data  = &pdata,
            },
            .num_resources  = ARRAY_SIZE(res),
            .resource       = res,
        };
        
        int __init test_init(void)
        {
            int ret;
            ret = platform_device_register(&pdev);
            if(ret)
                printk("platform_device_register FAILED!\n");
        
            return ret;
        }
        
        void __exit test_exit(void)
        {
            platform_device_unregister(&pdev);
        }
        
        module_init(test_init);
        module_exit(test_exit);
        MODULE_LICENSE("GPL");

    6. platform_device接口Demo:

        #include <linux/module.h>
        #include <linux/platform_device.h>
        
        //定义自己的平台数据结构
        struct my_platform_data {
            int w;
            int h;
            char name[20];
        };
        
        static int up_probe(struct platform_device *pdev)
        {
            int i;
            struct my_platform_data *pdata = pdev->dev.platform_data;
        
            for(i = 0; i < pdev->num_resources; i++)
            {
                printk("start = %x, end = %x\n",
                       pdev->resource[i].start,
                       pdev->resource[i].end);
            }
            printk("==========================\n");
            printk("platform data.\n");
            printk("width = %d, height = %d\n%s\n",
                    pdata->w, pdata->h, pdata->name);
        
            return 0;
        }
        
        static int up_remove(struct platform_device *pdev)
        {
            printk("In %s func.\n", __func__);
        
            return 0;
        }
        
        //最后一个元素清0
        struct platform_device_id up_ids[] = {
            {"device_v1", 1},
            {"device_v2", 2},
            {"device_v3", 3},
            {"device_v4", 4},
            {"device_v5", 5},
            {},
        };
        
        //使用id_table和设备匹配
        struct platform_driver pdrv = {
            .probe  = up_probe,
            .remove = up_remove,
            .driver = {
                .name   = "xxxxxxx",
                .owner  = THIS_MODULE,
            },
            .id_table = up_ids,
        };
        
        int __init test_init(void)
        {
            int ret;
            ret = platform_driver_register(&pdrv);
            if(ret)
                printk("platform_driver_register FAILED!\n");
        
            return ret;
        }
        
        void __exit test_exit(void)
        {
            platform_driver_unregister(&pdrv);
        }
        
        module_init(test_init);
        module_exit(test_exit);
        MODULE_LICENSE("GPL");

 

上一篇:阿里云发布第三代神龙云服务器:号称整体算力全球最强


下一篇:阿里云MySQL云数据库和Redis6.0数据库使用体验