zoukankan      html  css  js  c++  java
  • 字符设备驱动(1)驱动代码完整源码:charButtons.c

    内核版本:Linux3.0.8

    开发板:基于三星S5PV210处理器的Tiny210开发板

    驱动名称:charButtons.c

    驱动描述:按键触发中断,中断处理程序执行相应的简单LED点亮操作

    方案1注册字符设备使用新的接口实现(需要好几个函数来实现。貌似更复杂)

    方案2注册字符设备使用老的接口实现(貌似老接口更简单)

    /*****************************************************************************
    简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
    ******************************************************************************/
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/wait.h>
    #include <linux/poll.h>
    #include <linux/sched.h>
    #include <linux/irq.h>
    #include <asm/irq.h>
    #include <linux/interrupt.h>
    
    
    #include <mach/map.h>
    #include <mach/gpio.h>
    #include <mach/regs-gpio.h>
    #include <plat/gpio-cfg.h>
    
    #include <linux/slab.h>
    
    #define DEVICE_NAME        "buttons"
    
    struct button_desc {
        int gpio;
        int number;
        char *name;    
    };
    
    struct led_desc {
        int gpio;
        int number;
        char *name;    
    };
    
    static struct button_desc buttons[] = {
        { S5PV210_GPH2(0), 0, "KEY0" },
        { S5PV210_GPH2(1), 1, "KEY1" },
        { S5PV210_GPH2(2), 2, "KEY2" },
        { S5PV210_GPH2(3), 3, "KEY3" },
        { S5PV210_GPH3(0), 4, "KEY4" },
        { S5PV210_GPH3(1), 5, "KEY5" },
        { S5PV210_GPH3(2), 6, "KEY6" },
        { S5PV210_GPH3(3), 7, "KEY7" },
    };
    
    static struct led_desc leds[] = {
        {S5PV210_GPJ2(0),1,"LED1"},
        {S5PV210_GPJ2(1),2,"LED2"},    
        {S5PV210_GPJ2(2),3,"LED3"},
        {S5PV210_GPJ2(3),4,"LED4"},
    };
    
    #define OK            (0)
    #define ERROR         (-1)
    struct gpio_chip *chip;
    struct cdev *gDev;
    struct file_operations *gFile;
    dev_t  devNum;
    unsigned int subDevNum = 1;//要申请的次设备号个数
    int reg_major  =  234;    
    int reg_minor =   0;
    
    
    static irqreturn_t button_interrupt(int irq, void *dev_id)
    {
        struct button_desc *bdata = (struct button_desc *)dev_id;
    
        int down;
        unsigned tmp;
        tmp = gpio_get_value(bdata->gpio);
    
        /* active low */
        down = !tmp;
        printk("KEY %d: %08x
    ", bdata->number, down);
            
        if(bdata->number < 4)
        {
            gpio_set_value(leds[bdata->number].gpio,0);
            printk("LED %d: On 
    ",leds[bdata->number].number);
        }
        else
        {
            gpio_set_value(leds[(bdata->number) - 4].gpio,1);
            printk("LED %d: OFF 
    ",leds[(bdata->number)-4].number);
        }
        
        return IRQ_HANDLED;
    }
    
    int butsOpen(struct inode *p, struct file *f)
    {
        
            int irq;
        int i;
        int err = 0;
        printk(KERN_EMERG"butsOpen
    ");
        for (i = 0; i < ARRAY_SIZE(buttons); i++) {
            if (!buttons[i].gpio)
                continue;
    
            irq = gpio_to_irq(buttons[i].gpio);
            // irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"
            //irq = IRQ_EINT(24)+i =168+i  "S5PV210_GPH3(i)"
            err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, 
                    buttons[i].name, (void *)&buttons[i]);
            if (err)
                break;
        }
        
        for(i = 0; i<ARRAY_SIZE(leds);i++)
        {
            if(!leds[i].gpio)
                continue;
            gpio_direction_output(leds[i].gpio,1);
        }
        
        if (err) {
            i--;
            for (; i >= 0; i--) {
                if (!buttons[i].gpio)
                    continue;
    
                irq = gpio_to_irq(buttons[i].gpio);
                disable_irq(irq);
                free_irq(irq, (void *)&buttons[i]);
            }
    
            return -EBUSY;
        }
        return 0;
    }
    
    
    
    
    int charDrvInit(void)
    {
        
        devNum = MKDEV(reg_major, reg_minor);
    
        printk(KERN_EMERG"devNum is %d
    ", devNum);
        if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME))
        {
            printk(KERN_EMERG"register_chrdev_region ok
    ");
        }
        else
        {
            printk(KERN_EMERG"register_chrdev_region error
    ");
            return ERROR;
        }
        /*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,"test"))
        {
            printk(KERN_EMERG"register_chrdev_region ok
    ");
        }
        else
        {
            printk(KERN_EMERG"register_chrdev_region error
    ");
            return ERROR;
        }*/
    
        gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
        gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
        
        gFile->open = butsOpen;
    //注册设备函数到file_operations结构体gFile
    
       //gDev->owner = THIS_MODULE;
        gFile->owner = THIS_MODULE;
        cdev_init(gDev, gFile);
    //在cdev结构体中添加指针指向file_operations结构体gFile
        cdev_add(gDev, devNum, 3);
    //建立设备号与cdev结构体联系
        printk(KERN_EMERG"button driver initial done...
    ");
        return 0;
    }
    
    void __exit charDrvExit(void)
    {
        int i,irq;
        cdev_del(gDev);
        unregister_chrdev_region(devNum, subDevNum);
        for (i = 0; i < ARRAY_SIZE(buttons); i++) {
                if (!buttons[i].gpio)
                    continue;
    
                irq = gpio_to_irq(buttons[i].gpio);
                disable_irq(irq);
                free_irq(irq, (void *)&buttons[i]);
            }
        
        return;
    }
    module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始
    module_exit(charDrvExit);//执行rmmod时,结束
    MODULE_LICENSE("GPL");
    代码实现方案1
    /*****************************************************************************
    简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
    ******************************************************************************/
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <mach/gpio.h>
    #include <linux/irq.h>
    #include <linux/kdev_t.h>
    #include <linux/interrupt.h>
    #include <linux/init.h>
    
    #define DEVICE_NAME        "leds"
    
    struct button_desc {
        int gpio;
        int number;
        char *name;    
    };
    
    struct led_desc {
        int gpio;
        int number;
        char *name;    
    };
    
    static struct button_desc buttons[] = {
        { S5PV210_GPH2(0), 0, "KEY0" },
        { S5PV210_GPH2(1), 1, "KEY1" },
        { S5PV210_GPH2(2), 2, "KEY2" },
        { S5PV210_GPH2(3), 3, "KEY3" },
        { S5PV210_GPH3(0), 4, "KEY4" },
        { S5PV210_GPH3(1), 5, "KEY5" },
        { S5PV210_GPH3(2), 6, "KEY6" },
        { S5PV210_GPH3(3), 7, "KEY7" },
    };
    
    static struct led_desc leds[] = {
        {S5PV210_GPJ2(0),1,"LED1"},
        {S5PV210_GPJ2(1),2,"LED2"},    
        {S5PV210_GPJ2(2),3,"LED3"},
        {S5PV210_GPJ2(3),4,"LED4"},
    };
    
    #define OK            (0)
    #define ERROR         (-1)
    dev_t  devNum;
    unsigned int subDevNum = 1;//要申请的次设备号个数
    int reg_major  =  234;    
    int reg_minor =   0;
    
    
    static irqreturn_t button_interrupt(int irq, void *dev_id)
    {
        struct button_desc *bdata = (struct button_desc *)dev_id;
    
        int down;
        unsigned tmp;
        tmp = gpio_get_value(bdata->gpio);
    
        /* active low */
        down = !tmp;
        printk("KEY %d: %08x
    ", bdata->number, down);
            
        if(bdata->number < 4)
        {
            gpio_set_value(leds[bdata->number].gpio,0);
            printk("LED %d: On 
    ",leds[bdata->number].number);
        }
        else
        {
            gpio_set_value(leds[(bdata->number) - 4].gpio,1);
            printk("LED %d: OFF 
    ",leds[(bdata->number)-4].number);
        }
        
        return IRQ_HANDLED;
    }
    
    int butsOpen(struct inode *p, struct file *f)
    {
        
            int irq;
        int i;
        int err = 0;
        printk(KERN_EMERG"butsOpen
    ");
        for (i = 0; i < ARRAY_SIZE(buttons); i++) {
            if (!buttons[i].gpio)
                continue;
    
            irq = gpio_to_irq(buttons[i].gpio);
            // irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"
            //irq = IRQ_EINT(24)+i =168+i  "S5PV210_GPH3(i)"
            err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, 
                    buttons[i].name, (void *)&buttons[i]);
            if (err)
                break;
        }
        
        for(i = 0; i<ARRAY_SIZE(leds);i++)
        {
            if(!leds[i].gpio)
                continue;
            gpio_direction_output(leds[i].gpio,1);
        }
        
        if (err) {
            i--;
            for (; i >= 0; i--) {
                if (!buttons[i].gpio)
                    continue;
    
                irq = gpio_to_irq(buttons[i].gpio);
                disable_irq(irq);
                free_irq(irq, (void *)&buttons[i]);
            }
    
            return -EBUSY;
        }
        return 0;
    }
    
    static const struct file_operations gFile = 
    {
        .owner = THIS_MODULE,
        .open  =  butsOpen,    
    };
    
    
    int charDrvInit(void)
    {
        
        devNum = MKDEV(reg_major, reg_minor);
        printk(KERN_EMERG"devNum is %d
    ", devNum);
       
        if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile))
        {
            printk(KERN_EMERG "register_chrdev_region ok
    ");
        }
        else
        {
            printk(KERN_EMERG"register_chrdev_region error
    ");
            return ERROR;
        }
        
        printk(KERN_EMERG "button driver initial done...
    ");
        return 0;
    }
    
    void __exit charDrvExit(void)
    {
        int i,irq;
        unregister_chrdev(reg_major, DEVICE_NAME);
        for (i = 0; i < ARRAY_SIZE(buttons); i++) {
                if (!buttons[i].gpio)
                    continue;
    
                irq = gpio_to_irq(buttons[i].gpio);
                disable_irq(irq);
                free_irq(irq, (void *)&buttons[i]);
            }
        
        return;
    }
    module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始
    module_exit(charDrvExit);//执行rmmod时,结束
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("LiuB");
    代码实现方案2

    函数修饰符

    __init,本质上是一个宏定义,在内核源代码中定义:#define __init __section(.init.text) __cold notrace
    作用就是,将被它修饰的函数放入.init.text段中去。所以所有的内核模块的__init修饰的函数被放在一起。内核启动时统一加载,加载完后统一释放以节省内存。

    __exit,同上:#define __exit __section(.exit.text) __exitused __cold notrace

    printk()函数:内核封装出来的打印函数。格式:
    printk( 打印级别 “打印信息”) //printk的打印级别是用来控制printk打印的信息是否在终端显示

    #define KERN_EMERG    "<0>"    /* system is unusable            */
    #define KERN_ALERT    "<1>"    /* action must be taken immediately    */
    #define KERN_CRIT    "<2>"    /* critical conditions            */
    #define KERN_ERR    "<3>"    /* error conditions            */
    #define KERN_WARNING    "<4>"    /* warning conditions            */
    #define KERN_NOTICE    "<5>"    /* normal but significant condition    */
    #define KERN_INFO    "<6>"    /* informational            */
    #define KERN_DEBUG    "<7>"    /* debug-level messages            */
    
    /* Use the default kernel loglevel */
    #define KERN_DEFAULT    "<d>"

     如果定义了模块,即这一驱动被动态编译为模块时

    驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h)

    /* Each module must use one module_init(). */
    #define module_init(initfn)    
    static inline initcall_t __inittest(void)    { return initfn; }    
    int init_module(void) __attribute__((alias(#initfn)));
    
    typedef int (*initcall_t)(void);
    //static inline initcall_t __inittest(void)    { return initfn; }
    //inittest这个函数返回值为一个initcall_t类型的initfn
    //而initcall_t是一个函数指针,返回值为整型,输入参数为空
    module_init()
    /* This is only required if you want to be unloadable. */
    #define module_exit(exitfn)    
    static inline exitcall_t __exittest(void)    { return exitfn; }    
    void cleanup_module(void) __attribute__((alias(#exitfn)));
    
    typedef void (*exitcall_t)(void);
    module_exit()

    如果未定义模块,这一驱动被静态编译进内核

    驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h)

    /**
     * module_init() - driver initialization entry point
     * @x: function to be run at kernel boot time or module insertion
     * 
     * module_init() will either be called during do_initcalls() (if
     * builtin) or at module insertion time (if a module).  There can only
     * be one per module.
     */
    #define module_init(x)    __initcall(x);
    #define __initcall(fn) device_initcall(fn)
    #define device_initcall(fn)    __define_initcall("6",fn,6)
    /* initcalls are now grouped by functionality into separate 
     * subsections. Ordering inside the subsections is determined
     * by link order. 
     * For backwards compatibility, initcall() puts the call in 
     * the device init subsection.
     *
     * The `id' arg to __define_initcall() is needed so that multiple initcalls
     * can point at the same handler without causing duplicate-symbol build errors.
     */
    
    #define __define_initcall(level,fn,id) 
        static initcall_t __initcall_##fn##id __used 
        __attribute__((__section__(".initcall" level ".init"))) = fn
    module_init(x)

    通过这些段代码,我们能够看出最终的结果是将我们使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所有使用module_init修饰的函数指针都被链接在这个段里,内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化

  • 相关阅读:
    pyc文件是什么【转载】
    Linux下的python等操作【转载】
    P1012 拼数 字符串
    P1309 瑞士轮 排序选择 时间限制 归并排序
    商业竞争 三分+背包
    老虎ji 剪枝模拟
    交通灯 并查集
    三色抽卡游戏 博弈论nim
    质数串 乱搞
    自动驾驶系统 bfs
  • 原文地址:https://www.cnblogs.com/embeded-linux/p/11094229.html
Copyright © 2011-2022 走看看