zoukankan      html  css  js  c++  java
  • 驱动学习3:简单驱动自动创建设备节点

    本文参考:

    https://www.cnblogs.com/lifexy/p/7506277.html

    http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/


    驱动学习2中每次都要手工创建设备节点,大家肯定也会觉得这样做太麻烦了。

    可以使用自动创建设备节点,Linux有udev、mdev的机制,而我们的ARM开发板上移植的busybox有mdev机制,然后mdev机制会通过class类来找到相应类的驱动设备来自动创建设备节点 (前提需要有mdev)

     接下来使用insmod自动创建设备节点, rmmod自动注销设备节点

    (1)首先创建一个class设备类,class是一个设备的高级视图,它抽象出低级的实现细节,然后在class类下,创建一个class_device,即类下面创建类的设备:(在C语言中class就是个结构体)

    同时设置了类名、设备节点名

    #define  DEVICE_NAME "hello"    ///< The device will appear at /dev/ebbchar using this value
    #define  CLASS_NAME  "ebb"        ///< The device class -- this is a character device driver

    (2)在hello_init入口函数、hello_exit出口处修改:

    static struct class*  ebbcharClass  = NULL; ///< The device-driver class struct pointer
    static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer
    static int    majorNumber;                  ///< Stores the device number -- determined automatically
    
    //register_chrdev作用:在VFS虚拟文件系统中找到字符设备,然后通过主设备号找到内核数组里对应位置,最后将设备名字和fops结构体填进去
    static int hello_init(void)
    {
        /*如果设置major为0,表示由内核动态分配主设备号,函数的返回值是主设备号*/
        majorNumber =register_chrdev (0, DEVICE_NAME, &hello_fops); 
    
       //创建类,它会在sys/class目录下创建CLASS_NAME这个类
       ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
       if (IS_ERR(ebbcharClass)){                // Check for error and clean up if there is
          unregister_chrdev(majorNumber, DEVICE_NAME);
          printk(KERN_ALERT "Failed to register device class
    ");
          return PTR_ERR(ebbcharClass);          // Correct way to return an error on a pointer
       }
       printk(KERN_INFO "EBBChar: device class registered correctly
    ");
     
       // Register the device driver
       ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
       if (IS_ERR(ebbcharDevice)){               // Clean up if there is an error
          class_destroy(ebbcharClass);           // Repeated code but the alternative is goto statements
          unregister_chrdev(majorNumber, DEVICE_NAME);
          printk(KERN_ALERT "Failed to create the device
    ");
          return PTR_ERR(ebbcharDevice);
       }
       printk(KERN_INFO "EBBChar: device class created correctly
    "); // Made it! device was initialized
    
       return 0;
    }
    
    static void hello_exit(void)
    {
        device_destroy(ebbcharClass, MKDEV(majorNumber, 0));     // remove the device
        class_unregister(ebbcharClass);                          // unregister the device class
        class_destroy(ebbcharClass);                             // remove the device class
        //释放设备号、注销设备
        unregister_chrdev(majorNumber,DEVICE_NAME);
        printk(KERN_EMERG "hello dev has been exit!
    "); //卸载驱动, 将major填入即可
    }

    (3)验证

    重新编译insmod后,会发现在/dev下自动的创建了hello设备节点

    其中在sys/class里有各种类的设备, 比如sys/class/ebb下就有hello设备

     附完整代码:

    //参考内核源码:gpio.c
    #include <linux/init.h>
    #include <linux/module.h>
    
    #include <linux/platform_device.h>
    #include <linux/miscdevice.h>
    #include <linux/fs.h>
    
    
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("pp");
    
    #define  DEVICE_NAME "hello"    ///< The device will appear at /dev/ebbchar using this value
    #define  CLASS_NAME  "ebb"        ///< The device class -- this is a character device driver
    
    /////////////////////////dev//////////////////////////////////////
    
    static int hello_open(struct inode *inode, struct file *file){
        printk(KERN_EMERG "hello open
    ");
        return 0;
    }
    
    static int hello_release(struct inode *inode, struct file *file){
        printk(KERN_EMERG "hello release
    ");
        return 0;
    }
    
    /*设备文件的读函数中,参数filp为文件结构体指针,buf为用户空间内存地址,count为要读取的字节数,ppos为写的位置相对于文件开头的偏移*/
    //copy_to_user 完成用户空间缓冲区到内核空间的复制
    static ssize_t hello_read(struct file *filp, char __user *buf, size_t len,loff_t *ppos)
    {
       printk("hello_read
    ");      
       return 0;
    }
    /*设备文件的写函数中,参数filp为文件结构体指针,buf为用户空间内存地址,count为要写入的字节数,ppos为写的位置相对于文件开头的偏移*/
    //copy_from_user 完成内核空间到用户空间缓冲区的复制
    static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
    {
       printk("hello_write
    ");      
       return 0;
    }
    //ioctl函数
    static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
        printk("cmd is %d, arg is %d
    ", cmd, arg);
        return 0;
    }
    
    
    static struct file_operations hello_fops = {
        .owner = THIS_MODULE,
        .open = hello_open,  
        .read = hello_read,   
        .write = hello_write,       
        .release = hello_release,
        .unlocked_ioctl = hello_ioctl,
    };
    
    
    
    
    
    
    
    static struct class*  ebbcharClass  = NULL; ///< The device-driver class struct pointer
    static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer
    static int    majorNumber;                  ///< Stores the device number -- determined automatically
    
    //register_chrdev作用:在VFS虚拟文件系统中找到字符设备,然后通过主设备号找到内核数组里对应位置,最后将设备名字和fops结构体填进去
    static int hello_init(void)
    {
        /*如果设置major为0,表示由内核动态分配主设备号,函数的返回值是主设备号*/
        majorNumber =register_chrdev (0, DEVICE_NAME, &hello_fops); 
    
       //创建类,它会在sys/class目录下创建CLASS_NAME这个类
       ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
       if (IS_ERR(ebbcharClass)){                // Check for error and clean up if there is
          unregister_chrdev(majorNumber, DEVICE_NAME);
          printk(KERN_ALERT "Failed to register device class
    ");
          return PTR_ERR(ebbcharClass);          // Correct way to return an error on a pointer
       }
       printk(KERN_INFO "EBBChar: device class registered correctly
    ");
     
       // Register the device driver
       ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
       if (IS_ERR(ebbcharDevice)){               // Clean up if there is an error
          class_destroy(ebbcharClass);           // Repeated code but the alternative is goto statements
          unregister_chrdev(majorNumber, DEVICE_NAME);
          printk(KERN_ALERT "Failed to create the device
    ");
          return PTR_ERR(ebbcharDevice);
       }
       printk(KERN_INFO "EBBChar: device class created correctly
    "); // Made it! device was initialized
    
       return 0;
    }
    
    static void hello_exit(void)
    {
        device_destroy(ebbcharClass, MKDEV(majorNumber, 0));     // remove the device
        class_unregister(ebbcharClass);                          // unregister the device class
        class_destroy(ebbcharClass);                             // remove the device class
        //释放设备号、注销设备
        unregister_chrdev(majorNumber,DEVICE_NAME);
        printk(KERN_EMERG "hello dev has been exit!
    "); //卸载驱动, 将major填入即可
    }
    
    
    module_init(hello_init);
    module_exit(hello_exit);

    应用程序测试代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ioctl.h>
    
    
    int main(int argc, char *argv[])
    {
        int fd;
        printf("enter driver test %s %s 
    ", argv[1], argv[2]);
        char *hello = "/dev/hello";
    
        if((fd = open(hello, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
        {
            printf("open %s failed
    ", hello);
        }
        else
        {
            printf("%s fd is %d 
    ", hello, fd);
            ioctl(fd, atoi(argv[1]), atoi(argv[2]));
        }
        close(fd);
        return 1;
    }

     

  • 相关阅读:
    .NET下的加密解密大全(1): 哈希加密
    orm fluentdata使用相关文章
    xml处理相关文章收藏
    Salty Fish(区间和)
    Fennec VS. Snuke
    Splitting Pile
    ST表(离线RMQ)
    Exponentiation(高精度大数)
    高斯消元(模板)
    Online Judge(字符串-格式)
  • 原文地址:https://www.cnblogs.com/shuqingstudy/p/9151618.html
Copyright © 2011-2022 走看看