• 《LED总线驱动例程》


    1.LED总线驱动源码

    led_opr.h

    #ifndef _LED_OPR_H
    #define _LED_OPR_H
    
    struct led_operations {
        int (*init) (int which); /* 初始化LED, which-哪个LED */       
        int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
    };
    
    struct led_operations *get_board_led_opr(void);
    
    
    #endif

    led_source.h

    #ifndef _LED_RESOURCE_H
    #define _LED_RESOURCE_H
    
    /* GPIO3_0 */
    /* bit[31:16] = group */
    /* bit[15:0]  = which pin */
    #define GROUP(x) (x>>16)
    #define PIN(x)   (x&0xFFFF)
    #define GROUP_PIN(g,p) ((g<<16) | (p))
    
    
    #endif

    led_driver.c

    #include <linux/module.h>
    
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <linux/miscdevice.h>
    #include <linux/kernel.h>
    #include <linux/major.h>
    #include <linux/mutex.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/stat.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/tty.h>
    #include <linux/kmod.h>
    #include <linux/gfp.h>
    
    #include "led_opr.h"
    
    
    /* 1. 确定主设备号                                                                 */
    static int major = 0;
    static struct class *led_class;
    struct led_operations *p_led_opr;
    
    
    #define MIN(a, b) (a < b ? a : b)
    
    
    void led_class_create_device(int minor)
    {
        device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor); /* /dev/100ask_led0,1,... */
    }
    void led_class_destroy_device(int minor)
    {
        device_destroy(led_class, MKDEV(major, minor));
    }
    void register_led_operations(struct led_operations *opr)
    {
        p_led_opr = opr;
    }
    
    EXPORT_SYMBOL(led_class_create_device);
    EXPORT_SYMBOL(led_class_destroy_device);
    EXPORT_SYMBOL(register_led_operations);
    
    
    
    /* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
    static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
    {
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
        return 0;
    }
    
    /* write(fd, &val, 1); */
    static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
    {
        int err;
        char status;
        struct inode *inode = file_inode(file);
        int minor = iminor(inode);
        
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
        err = copy_from_user(&status, buf, 1);
    
        /* 根据次设备号和status控制LED */
        p_led_opr->ctl(minor, status);
        
        return 1;
    }
    
    static int led_drv_open (struct inode *node, struct file *file)
    {
        int minor = iminor(node);
        
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
        /* 根据次设备号初始化LED */
        p_led_opr->init(minor);
        
        return 0;
    }
    
    static int led_drv_close (struct inode *node, struct file *file)
    {
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
        return 0;
    }
    
    /* 2. 定义自己的file_operations结构体                                              */
    static struct file_operations led_drv = {
        .owner     = THIS_MODULE,
        .open    = led_drv_open,
        .read    = led_drv_read,
        .write   = led_drv_write,
        .release = led_drv_close,
    };
    
    /* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
    /* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
    static int __init led_init(void)
    {
        int err;
        
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
        major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */
    
    
        led_class = class_create(THIS_MODULE, "100ask_led_class");
        err = PTR_ERR(led_class);
        if (IS_ERR(led_class)) {
            printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
            unregister_chrdev(major, "led");
            return -1;
        }
    
        /* 本来函数是向底层获取结构体,但是由于EXPORT_SYMBOL(led_class_create_device);的关系,
            也就是leddrv这个驱动要先加载,然后再加载chip_demo_gpio。
            但是这个下面这个函数是在底层定义,也就是要先加载底层的驱动,然后再加载上层驱动。这样就会
            存在交叉加载。所以现在不用这个函数。
            EXPORT_SYMBOL(register_led_operations);
            通过这个函数,让底层主动向上层注册结构体。*/
    //    p_led_opr = get_board_led_opr();
    
        
        return 0;
    }
    
    /* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
    static void __exit led_exit(void)
    {
        printk("%s %s line %d
    ", __FILE__, __FUNCTION__, __LINE__);
    
        class_destroy(led_class);
        unregister_chrdev(major, "100ask_led");
    }
    
    
    /* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
    
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_LICENSE("GPL");

      驱动程序中向内核注册了file_operations结构体,结构体里面有open,read,write,close等函数。

      p_led_opr是led_operations结构体的指针变量。相关定义在led_opr.h中,主要有init()和ctl()两个函数指针。前者用来初始化具体的哪个LED,后者用来控制哪个LED的亮跟灭。这两个函数是实际与硬件相关控制有关的。

      在led_driver.c中主要是与驱动相关的通用程序,与硬件无关。

      通过register_led_operations()函数可以在chip_demo_gpio.c中调用,让底层主动向驱动注册这两个函数。

      EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。

       led_class_create_device()和led_class_destroy_device()函数,函数作用是用来创建设备节点的和销毁设备节点。本来是直接在驱动程序中创建的。但是因为现在使用总线的框架,驱动和硬件分离。程序一开始不知道要注册多少个设备节点,所以创建设备节点就放在当设备匹配后,调用probe的时候来创建。因为这个时候可以知道有多少个设备,就可以创建多少个设备节点。

    chip_demo_gpio.c

    #include <linux/module.h>
    
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <linux/miscdevice.h>
    #include <linux/kernel.h>
    #include <linux/major.h>
    #include <linux/mutex.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/stat.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/tty.h>
    #include <linux/kmod.h>
    #include <linux/gfp.h>
    #include <linux/platform_device.h>
    
    #include "led_opr.h"
    #include "leddrv.h"
    #include "led_resource.h"
    
    static int g_ledpins[100];
    static int g_ledcnt = 0;
    
    static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
    {   
        //printk("%s %s line %d, led %d
    ", __FILE__, __FUNCTION__, __LINE__, which);
        
        printk("init gpio: group %d, pin %d
    ", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
        switch(GROUP(g_ledpins[which]))
        {
            case 0:
            {
                printk("init pin of group 0 ...
    ");
                break;
            }
            case 1:
            {
                printk("init pin of group 1 ...
    ");
                break;
            }
            case 2:
            {
                printk("init pin of group 2 ...
    ");
                break;
            }
            case 3:
            {
                printk("init pin of group 3 ...
    ");
                break;
            }
        }
        
        return 0;
    }
    
    static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
    {
        //printk("%s %s line %d, led %d, %s
    ", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
        printk("set led %s: group %d, pin %d
    ", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    
        switch(GROUP(g_ledpins[which]))
        {
            case 0:
            {
                printk("set pin of group 0 ...
    ");
                break;
            }
            case 1:
            {
                printk("set pin of group 1 ...
    ");
                break;
            }
            case 2:
            {
                printk("set pin of group 2 ...
    ");
                break;
            }
            case 3:
            {
                printk("set pin of group 3 ...
    ");
                break;
            }
        }
    
        return 0;
    }
    
    static struct led_operations board_demo_led_opr = {
        .init = board_demo_led_init,
        .ctl  = board_demo_led_ctl,
    };
    static int chip_demo_gpio_probe(struct platform_device *pdev)
    {
        struct resource *res;
        int i = 0;
    
        while (1)
        {
            res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
            if (!res)
                break;
            
            g_ledpins[g_ledcnt] = res->start;
            led_class_create_device(g_ledcnt);
            g_ledcnt++;
        }
        return 0;
        
    }
    
    static int chip_demo_gpio_remove(struct platform_device *pdev)
    {
        struct resource *res;
        int i = 0;
    
        while (1)
        {
            res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
            if (!res)
                break;
            
            led_class_destroy_device(i);
            i++;
            g_ledcnt--;
        }
        return 0;
    }
    
    
    static struct platform_driver chip_demo_gpio_driver = {
        .probe      = chip_demo_gpio_probe,
        .remove     = chip_demo_gpio_remove,
        .driver     = {
            .name   = "100ask_led",
        },
    };
    
    static int __init chip_demo_gpio_drv_init(void)
    {
        int err;
        
        err = platform_driver_register(&chip_demo_gpio_driver); 
        register_led_operations(&board_demo_led_opr);
        
        return 0;
    }
    
    static void __exit lchip_demo_gpio_drv_exit(void)
    {
        platform_driver_unregister(&chip_demo_gpio_driver);
    }
    
    module_init(chip_demo_gpio_drv_init);
    module_exit(lchip_demo_gpio_drv_exit);
    
    MODULE_LICENSE("GPL");

      chip_demo_gpio_drv_init()函数向内核注册一个platform_driver结构体变量chip_demo_gpio_driver,里面有probe、remove、以及name的定义。其中probe就是当匹配成功后会被调用的函数。并且调用register_led_operations()函数,也就是led_driver.c中的函数。把init()和ctl()这两个函数向led_driver.c注册。(相当于底层每次更新完init()和ctl()会自动注册。这里运用了函数指针的便捷性)

       通过platform_get_resource()函数获取硬件资源。放在数组g_ledpins中。同时通过led_class_create_device()进行设备节点的注册。(为什么这里还要将device_create()再封装成led_class_create_device()函数。注意:这里led_driver.c和chip_demo_gpio.c属于两个ko。如果要在chip_demo_gpio.c直接调用device_create()还要再调用一次class_create()函数。但是class_create()函数属于通用函数,与硬件不相关)

    board_A_led.c

    #include <linux/module.h>
    
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <linux/miscdevice.h>
    #include <linux/kernel.h>
    #include <linux/major.h>
    #include <linux/mutex.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/stat.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/tty.h>
    #include <linux/kmod.h>
    #include <linux/gfp.h>
    #include <linux/platform_device.h>
    
    #include "led_resource.h"
    
    
    static void led_dev_release(struct device *dev)
    {
    }
    
    static struct resource resources[] = {
            {
                    .start = GROUP_PIN(3,1),
                    .flags = IORESOURCE_IRQ,
                    .name = "100ask_led_pin",
            },
            {
                    .start = GROUP_PIN(5,8),
                    .flags = IORESOURCE_IRQ,
                    .name = "100ask_led_pin",
            },
    };
    
    
    static struct platform_device board_A_led_dev = {
            .name = "100ask_led",
            .num_resources = ARRAY_SIZE(resources),
            .resource = resources,
            .dev = {
                    .release = led_dev_release,
             },
    };
    
    static int __init led_dev_init(void)
    {
        int err;
        
        err = platform_device_register(&board_A_led_dev);   
        
        return 0;
    }
    
    static void __exit led_dev_exit(void)
    {
        platform_device_unregister(&board_A_led_dev);
    }
    
    module_init(led_dev_init);
    module_exit(led_dev_exit);
    
    MODULE_LICENSE("GPL");

       board_A_led.c主要就是定义硬件资源。这里定义的是LED的引脚

    2.总结

      board_A_led.c和chip_demo_gpio.c中分别定义了platform_device和platform_driver_register。并向内核注册。当匹配成功后就会调用platform_driver_register结构体中的.probe。这里体现了总线驱动的概念。

      至于led_driver.c主要是做一些通过的操作。比如创建一个led_class类和注册file_operations结构体。并且定义open、close、write、read函数。其中调用了p_led_opr->ctl(minor, status);这样的操作。实际就是结构体以及函数指针的概念,让程序分离,耦合性减少。方便修改。

    1.在头文件中定义一个结构体,结构体成员为函数指针。

    2.在led_driver.c中定义一个结构体变量。

    3.通过变量去调用结构体成员。p_led_opr->ctl(minor, status)类似这样,这样就相当于直接调用一个函数。但是函数是个变量,未定。

    4.并且在led_driver.c中定义一个函数。

    5.该函数可以让其他.c文件调用,调用后可以让其他.c文件中定义的结构体变量赋值过来。

    6.而其他.c文件中定义了具体的函数

      

    led_opr.h
    
    1.
    struct led_operations { int (*init) (int which); /* 初始化LED, which-哪个LED */ int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */ }; led_driver.c 2.
    struct led_operations *p_led_opr;
    4.
    void register_led_operations(struct led_operations *opr) { p_led_opr =
    opr; } EXPORT_SYMBOL(register_led_operations);//声明后可以让其他ko调用该函数 static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) { int err; char status; struct inode *inode = file
    _inode(file);
    int minor = iminor(inode); printk("%s %s line %d ", __FILE__, __FUNCTION__, __LINE__); err = copy_from_user(&status, buf, 1); /* 根据次设备号和status控制LED */   3.
      p_led_opr
    ->ctl(minor, status);
    return 1; } //open、read、close就不贴出来了 chip_demo_gpio.c static int __init chip_demo_gpio_drv_init(void) { int err; err = platform_driver_register(&chip_demo_gpio_driver); 5.
      register_led_operations(
    &board_demo_led_opr); return 0; }
    5.
    static struct led_operations board_demo_led_opr = { .init = board_demo_led_init, .ctl = board_demo_led_ctl, };

    6. static int board_demo_led_init
    (int which) /* 初始化LED, which-哪个LED */ { //printk("%s %s line %d, led %d ", __FILE__, __FUNCTION__, __LINE__, which); printk("init gpio: group %d, pin %d ", GROUP(g_ledpins[which]), PIN(g_ledpins[which])); 略 return 0; } 6. static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */ { //printk("%s %s line %d, led %d, %s ", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off"); printk("set led %s: group %d, pin %d ", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which])); 略 return 0; }
  • 相关阅读:
    爬取虎牙小姐姐热舞 Python
    爬某音乐平台的付费音乐 Python 被标题党搞烦了的可以来看看
    爬取某站的文档 Python
    解决网页复制限制
    英文拼写检查
    记测评文章
    Standard Deviation (SD), Standard Error (SE),Confidence Interval (CI). 画barplot,置信区间
    conditional analysis 把发现的lead SNP作为协变量看LD内是否有其他显著SNP
    知网下载pdf格式
    MAC环境配置
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/12454713.html
Copyright © 2020-2023  润新知