zoukankan      html  css  js  c++  java
  • 三种方式注册一个字符设备(按键驱动完整程序及测试程序)【转载】

    原文地址:http://blog.163.com/hefeng330467115@126/blog/static/78205842201072932412573/

    1. register_chrdev注册指定主次设备号的设备,手动在/dev下创建该设备的设备节点

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/irq.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>

    #define DEVICE_NAME     "buttons"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
    #define BUTTON_MAJOR    232         /* 主设备号 */

    struct button_irq_desc {
        int irq;
        unsigned long flags;
        char *name;
    };

    /* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
    static struct button_irq_desc button_irqs [] = {
        {IRQ_EINT1, IRQF_TRIGGER_FALLING, "KEY1"}, /* K1 */
        {IRQ_EINT4, IRQF_TRIGGER_FALLING, "KEY2"}, /* K2 */
        {IRQ_EINT2,  IRQF_TRIGGER_FALLING, "KEY3"}, /* K3 */
        {IRQ_EINT0,  IRQF_TRIGGER_FALLING, "KEY4"}, /* K4 */
    };

    /* 按键被按下的次数(准确地说,是发生中断的次数) */
    static volatile int press_cnt [] = {0, 0, 0, 0};

    /* 等待队列:
     * 当没有按键被按下时,如果有进程调用s3c24xx_buttons_read函数,
     * 它将休眠
     */

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;


    static irqreturn_t buttons_interrupt(int irq, void *dev_id)
    {
        volatile int *press_cnt = (volatile int *)dev_id;
       
        *press_cnt = *press_cnt + 1; /* 按键计数加1 */
        ev_press = 1;                /* 表示中断发生了 */
        wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
       
        return IRQ_RETVAL(IRQ_HANDLED);
    }
    /* 应用程序对设备文件/dev/buttons执行open(...)时,
     * 就会调用s3c24xx_buttons_open函数
     */

    static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
    {
        int i;
        int err;
       
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
            // 注册中断处理函数
            err = request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags,
                              button_irqs[i].name, (void *)&press_cnt[i]);
            if (err)
                break;
        }

        if (err) {
            // 释放已经注册的中断
            i--;
            for (; i >= 0; i--)
                free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
            return -EBUSY;
        }
       
        return 0;
    }


    /* 应用程序对设备文件/dev/buttons执行close(...)时,
     * 就会调用s3c24xx_buttons_close函数
     */

    static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
    {
        int i;
       
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
            // 释放已经注册的中断
            free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
        }

        return 0;
    }
    /* 应用程序对设备文件/dev/buttons执行read(...)时,
     * 就会调用s3c24xx_buttons_read函数
     */

    static int s3c24xx_buttons_read(struct file *filp, char __user *buff,
                                             size_t count, loff_t *offp)
    {
        unsigned long err;
       
        /* 如果ev_press等于0,休眠 */
        wait_event_interruptible(button_waitq, ev_press);

        /* 执行到这里时,ev_press等于1,将它清0 */
        ev_press = 0;

        /* 将按键状态复制给用户,并清0 */
        err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count));
        memset((void *)press_cnt, 0, sizeof(press_cnt));

        return err ? -EFAULT : 0;
    }

    /* 这个结构是字符设备驱动程序的核心
     * 当应用程序操作设备文件时所调用的open、read、write等函数,
     * 最终会调用这个结构中的对应函数
     */

    static struct file_operations s3c24xx_buttons_fops = {
        .owner   =   THIS_MODULE,    /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */
        .open    =   s3c24xx_buttons_open,
        .release =   s3c24xx_buttons_close,
        .read    =   s3c24xx_buttons_read,
    };

    /*
     * 执行“insmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */

    static int __init s3c24xx_buttons_init(void)
    {
        int ret;

        /* 注册字符设备驱动程序
         * 参数为主设备号、设备名字、file_operations结构;
         * 这样,主设备号就和具体的file_operations结构联系起来了,
         * 操作主设备为BUTTON_MAJOR的设备文件时,就会调用s3c24xx_buttons_fops中的相关成员函数
         * BUTTON_MAJOR可以设为0,表示由内核自动分配主设备号
         */
        ret = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &s3c24xx_buttons_fops);
        if (ret < 0) {
          printk(DEVICE_NAME " can't register major number ");
          return ret;
        }
       
        printk(DEVICE_NAME " initialized ");
        return 0;
    }

    /*
     * 执行”rmmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */
    static void __exit s3c24xx_buttons_exit(void)
    {

        /* 卸载驱动程序 */
        unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);
    }

    /* 这两行指定驱动程序的初始化函数和卸载函数 */
    module_init(s3c24xx_buttons_init);
    module_exit(s3c24xx_buttons_exit);

    /* 描述驱动程序的一些信息,不是必须的 */
    MODULE_AUTHOR("http://www.100ask.net");            

    MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver");  

    MODULE_LICENSE("GPL");                               

    2. register_chrdev_region 注册一个指定主次设备号的设备,后利用class类在/dev/目录下自动创建一个该设备的节点

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/irq.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <linux/cdev.h>
    #include <linux/device.h>


    #define DEVICE_NAME     "buttons"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
    #define BUTTON_MAJOR    233       /* 主设备号 */
    #define BUTTON_MINOR     0     //次设备号
    #define NUMBER_OF_DEVICES  1   //要注册设备的个数
    struct cdev cdev;   //定义一个cdev结构的设备
    dev_t dev = 0;

    struct button_irq_desc {
        int irq;
        unsigned long flags;
        char *name;
    };

    /* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
    static struct button_irq_desc button_irqs [] = {
        {IRQ_EINT1, IRQF_TRIGGER_FALLING, "KEY1"}, /* K1 */
        {IRQ_EINT4, IRQF_TRIGGER_FALLING, "KEY2"}, /* K2 */
        {IRQ_EINT2,  IRQF_TRIGGER_FALLING, "KEY3"}, /* K3 */
        {IRQ_EINT0,  IRQF_TRIGGER_FALLING, "KEY4"}, /* K4 */
    };

    /* 按键被按下的次数(准确地说,是发生中断的次数) */
    static volatile int press_cnt [] = {0, 0, 0, 0};

    /* 等待队列:
     * 当没有按键被按下时,如果有进程调用s3c24xx_buttons_read函数,
     * 它将休眠
     */

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;
    static irqreturn_t buttons_interrupt(int irq, void *dev_id)
    {
        volatile int *press_cnt = (volatile int *)dev_id;
        *press_cnt = *press_cnt + 1; 
        ev_press = 1;             
        wake_up_interruptible(&button_waitq);   
        return IRQ_RETVAL(IRQ_HANDLED);
    }
    /* 应用程序对设备文件/dev/buttons执行open(...)时,
     * 就会调用s3c24xx_buttons_open函数
     */

    static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
    {
        int i;
        int err;
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
                 err = request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags,
                              button_irqs[i].name, (void *)&press_cnt[i]);
            if (err)
                break;
        }

        if (err) {
            i--;
            for (; i >= 0; i--)
                free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
            return -EBUSY;
        }    
        return 0;
    }
    /* 应用程序对设备文件/dev/buttons执行close(...)时,
     * 就会调用s3c24xx_buttons_close函数
     */

    static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
    {
        int i;   
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
            free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
        }

        return 0;
    }
    /* 应用程序对设备文件/dev/buttons执行read(...)时,
     * 就会调用s3c24xx_buttons_read函数
     */

    static int s3c24xx_buttons_read(struct file *filp, char __user *buff,
                                             size_t count, loff_t *offp)
    {
        unsigned long err;
        /* 如果ev_press等于0,休眠 */
        wait_event_interruptible(button_waitq, ev_press);

        /* 执行到这里时,ev_press等于1,将它清0 */
        ev_press = 0;

        /* 将按键状态复制给用户,并清0 */
        err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count));
        memset((void *)press_cnt, 0, sizeof(press_cnt));

        return err ? -EFAULT : 0;
    }

    /* 这个结构是字符设备驱动程序的核心
     * 当应用程序操作设备文件时所调用的open、read、write等函数,
     * 最终会调用这个结构中的对应函数
     */

    static struct file_operations s3c24xx_buttons_fops = {
        .owner   =   THIS_MODULE,    /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */
        .open    =   s3c24xx_buttons_open,
        .release =   s3c24xx_buttons_close,
        .read    =   s3c24xx_buttons_read,
    };

    /*向内核添加设备的函数*/
    static void char_reg_setup_cdev (void)   
     
      {
         int error;
         dev_t devno = MKDEV (BUTTON_MAJOR,BUTTON_MINOR);    //将已有的主次设备号转换为一个 dev_t, 
    /*初始化设备cdev同时建立和_fops结构的联系*/
          cdev_init (&cdev, &s3c24xx_buttons_fops);
          cdev.owner = THIS_MODULE;
          cdev.ops = &s3c24xx_buttons_fops;  
    /*添加设备   如果是动态申请的设备号,就用 
          cdev_add(&cdev,MKDEV(mem_major, 0), MEMDEV_NR_DEVS);                                  
        向内核添加设备,第一个参数是设备,第二个参数是设备号,
        第三个参数是要注册的次设备数目,mem_major在动态申请时候保存起来了。
    */
           error = cdev_add (&cdev, devno , 1);
           if (error)
           printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
    }
    struct class *my_class; //定义一个类,为在/dev目录下自动创建设备节点

    /*
     * 执行“insmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */

    static int __init s3c24xx_buttons_init(void)
    {
        int result;
    /*1. 注册获取字符设备号*/
       dev = MKDEV (BUTTON_MAJOR,BUTTON_MINOR)
       result = register_chrdev_region (dev, NUMBER_OF_DEVICES, DEVICE_NAME);
       if (result<0)
        {
         printk (KERN_WARNING "hello: can't get major number %d ", BUTTON_MAJOR);
         return result;
        }
    /*2. 利用注册的设备号向内核添加设备*/
       char_reg_setup_cdev ();
    /*3. 在/sysfs下创建一个类 */
     my_class = class_create(THIS_MODULE, "my_class");
     if(IS_ERR(my_class))
     {
       printk("Err: failed in creating class. ");
       return -1;
      }
     /*4. 在sysfs中注册你的驱动,且利用已经创建好的类在/dev/目录下创建一个该设备的节点 */
      device_create( my_class, NULL, MKDEV(BUTTON_MAJOR, 0), DEVICE_NAME);
       printk (KERN_INFO "Registered character driver ");
      return 0;
    }

    /*
     * 执行”rmmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */

    static void __exit s3c24xx_buttons_exit(void)
    {
        /* 卸载驱动程序 */

     dev_t devno = MKDEV(BUTTON_MAJOR,BUTTON_MINOR);
     cdev_del (&cdev); //删除设备结构
     device_destroy(my_class, MKDEV(BUTTON_MAJOR, 0)); //删除 /dev目录下的节点
     class_destroy(my_class); //删除创建的class
     unregister_chrdev_region (devno, NUMBER_OF_DEVICES); //注销字符设备
     printk (KERN_INFO "char driver cleaned up ");
    }

    /* 这两行指定驱动程序的初始化函数和卸载函数 */
    module_init(s3c24xx_buttons_init);
    module_exit(s3c24xx_buttons_exit);

    /* 描述驱动程序的一些信息,不是必须的 */
    MODULE_AUTHOR("http://www.100ask.net");           

    MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver");  

    MODULE_LICENSE("GPL");                            

    3. register_chrdev_region 注册一个系统自动分配主次设备号的设备,后利用class类在/dev/目录下自动创建一个该设备的节点

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/irq.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <linux/cdev.h>
    #include <linux/device.h>


    #define DEVICE_NAME     "buttons"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */

    int button_major;         /* 主设备号 */
    int button_minor=0;     //此设备号
    int number_of_devices=1;   //要注册设备的个数
    struct cdev cdev;   //定义一个cdev结构的设备
    dev_t dev = 0;

    struct button_irq_desc {
        int irq;
        unsigned long flags;
        char *name;
    };

    /* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
    static struct button_irq_desc button_irqs [] = {
        {IRQ_EINT1, IRQF_TRIGGER_FALLING, "KEY1"}, /* K1 */
        {IRQ_EINT4, IRQF_TRIGGER_FALLING, "KEY2"}, /* K2 */
        {IRQ_EINT2,  IRQF_TRIGGER_FALLING, "KEY3"}, /* K3 */
        {IRQ_EINT0,  IRQF_TRIGGER_FALLING, "KEY4"}, /* K4 */
    };

    /* 按键被按下的次数(准确地说,是发生中断的次数) */
    static volatile int press_cnt [] = {0, 0, 0, 0};

    /* 等待队列:
     * 当没有按键被按下时,如果有进程调用s3c24xx_buttons_read函数,
     * 它将休眠
     */

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
    static volatile int ev_press = 0;
    static irqreturn_t buttons_interrupt(int irq, void *dev_id)
    {
        volatile int *press_cnt = (volatile int *)dev_id;
       
        *press_cnt = *press_cnt + 1; 
        ev_press = 1;             
        wake_up_interruptible(&button_waitq);  
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }
    /* 应用程序对设备文件/dev/buttons执行open(...)时,
     * 就会调用s3c24xx_buttons_open函数
     */

    static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
    {
        int i;
        int err; 
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
             err = request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags,
                              button_irqs[i].name, (void *)&press_cnt[i]);
            if (err)
                break;
        }

        if (err) {
            i--;
            for (; i >= 0; i--)
                free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
            return -EBUSY;
        }
       
        return 0;
    }
    /* 应用程序对设备文件/dev/buttons执行close(...)时,
     * 就会调用s3c24xx_buttons_close函数
     */

    static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
    {
        int i;
        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
              free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
        }

        return 0;
    }
    /* 应用程序对设备文件/dev/buttons执行read(...)时,
     * 就会调用s3c24xx_buttons_read函数
     */

    static int s3c24xx_buttons_read(struct file *filp, char __user *buff,
                                             size_t count, loff_t *offp)
    {
        unsigned long err;
        wait_event_interruptible(button_waitq, ev_press);
        ev_press = 0;
        err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count));
        memset((void *)press_cnt, 0, sizeof(press_cnt));

        return err ? -EFAULT : 0;
    }

    /* 这个结构是字符设备驱动程序的核心
     * 当应用程序操作设备文件时所调用的open、read、write等函数,
     * 最终会调用这个结构中的对应函数
     */

    static struct file_operations s3c24xx_buttons_fops = {
        .owner   =   THIS_MODULE,   

        .open    =   s3c24xx_buttons_open,
        .release =   s3c24xx_buttons_close,
        .read    =   s3c24xx_buttons_read,
    };

    /*向内核添加设备的函数*/
    static void char_reg_setup_cdev (void)   
        {
           int error;
        dev_t devno = MKDEV (button_major,button_minor);  //将已有的主次设备号转换为一个 dev_t,      
     /*初始化设备cdev同时建立和_fops结构的联系*/
        cdev_init (&cdev, &s3c24xx_buttons_fops);
           cdev.owner = THIS_MODULE;
           cdev.ops = &s3c24xx_buttons_fops;
       
    /*添加设备   如果是动态申请的设备号,就用
    cdev_add(&cdev,MKDEV(mem_major, 0), MEMDEV_NR_DEVS);                                  
        向内核添加设备,第一个参数是设备,第二个参数是设备号,
        第三个参数是要注册的次设备数目,mem_major在动态申请时候保存起来了。
    */

        //error = cdev_add(&cdev,MKDEV(button_major, 0), number_of_devices)
        error = cdev_add (&cdev, devno ,number_of_devices);
           if (error)
               printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
    }
    struct class *my_class;
    //定义一个类,为在/dev目录下自动创建设备节点

    /*
     * 执行“insmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */

    static int __init s3c24xx_buttons_init(void)
    {

        int result;
    /*1. 注册获取字符设备号动态分配*/
      result=alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);//动态获得主设备号
      if (result<0)
      {
        printk (KERN_WARNING "hello: can't auto get major number ");
        return result;
       }
      button_major=MAJOR(dev);
      
    /*2. 利用注册的设备号向内核添加设备*/
       char_reg_setup_cdev ();
    /*3. 在/sysfs下创建一个类 */
     my_class = class_create(THIS_MODULE, "my_class");
     if(IS_ERR(my_class))
     {
       printk("Err: failed in creating class. ");
       return -1;
      }
     /*4. 在sysfs中注册你的驱动,且利用已经创建好的类在/dev/目录下创建一个该设备的节点 */
      device_create( my_class, NULL, MKDEV(button_major, button_minor), DEVICE_NAME);
       printk (KERN_INFO "Registered character driver ");
      return 0;
    }

    /*
     * 执行”rmmod s3c24xx_buttons.ko”命令时就会调用这个函数
     */

    static void __exit s3c24xx_buttons_exit(void)
    {

        /* 卸载驱动程序 */

     dev_t devno = MKDEV(button_major,button_minor);
     cdev_del (&cdev); //删除设备结构
     device_destroy(my_class, MKDEV(button_major,button_minor)); //删除 /dev目录下的节点
     class_destroy(my_class); //删除创建的class
     unregister_chrdev_region (devno, number_of_devices);  //注销字符设备
     printk (KERN_INFO "char driver cleaned up ");
    }

    /* 这两行指定驱动程序的初始化函数和卸载函数 */
    module_init(s3c24xx_buttons_init);
    module_exit(s3c24xx_buttons_exit);

    /* 描述驱动程序的一些信息,不是必须的 */
    MODULE_AUTHOR("http://www.100ask.net");            

    MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver");  

    MODULE_LICENSE("GPL");   

    4.测试程序             

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>

    int main(int argc, char **argv)
    {
        int i;
        int ret;
        int fd;
        int press_cnt[4];
       
        fd = open("/dev/buttons", 0);  // 打开设备
        if (fd < 0) {
            printf("Can't open /dev/buttons ");
            return -1;
        }

        // 这是个无限循环,进程有可能在read函数中休眠,当有按键被按下时,它才返回
        while (1) {
            // 读出按键被按下的次数
            ret = read(fd, press_cnt, sizeof(press_cnt));
            if (ret < 0) {
                printf("read err! ");
                continue;
            }

            for (i = 0; i < sizeof(press_cnt)/sizeof(press_cnt[0]); i++) {
                // 如果被按下的次数不为0,打印出来
                if (press_cnt[i])
                    printf("K%d has been pressed %d times! ", i+1, press_cnt[i]);
            }
        }
       
        close(fd);
        return 0;   
    }

  • 相关阅读:
    从程序员到项目经理(十一):每个人都是管理者
    从程序员到项目经理(十):程序员加油站 --要执着但不要固执
    CentOS 7安装KVM并创建虚拟机
    kvm安装及简单使用
    pg_ctl -- 启动、停止、重启 PostgreSQL
    Rabbitmq java
    Rabbitmq安装
    Zabbix通过SNMP监控HP Gen10服务器的硬件
    PostgreSQL忘记密码
    zabbix自动发现与监控内存和CPU使用率最高的进程
  • 原文地址:https://www.cnblogs.com/DawaTech/p/4835608.html
Copyright © 2011-2022 走看看