美文网首页
力卉编程 | 简单驱动自动创建设备节点

力卉编程 | 简单驱动自动创建设备节点

作者: 力卉编程 | 来源:发表于2020-04-04 15:30 被阅读0次

    原理: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
    
    static struct class*  ebbcharClass  = NULL;
    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\n");
          return PTR_ERR(ebbcharClass);          // Correct way to return an error on a pointer
       }
       printk(KERN_INFO "EBBChar: device class registered correctly\n");
     
       // 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\n");
          return PTR_ERR(ebbcharDevice);
       }
       printk(KERN_INFO "EBBChar: device class created correctly\n"); // 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!\n"); //卸载驱动, 将major填入即可
    }
    
    重新编译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\n");
        return 0;
    }
    
    static int hello_release(struct inode *inode, struct file *file){
        printk(KERN_EMERG "hello release\n");
        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\n");      
       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\n");      
       return 0;
    }
    //ioctl函数
    static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
        printk("cmd is %d, arg is %d\n", 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\n");
          return PTR_ERR(ebbcharClass);          // Correct way to return an error on a pointer
       }
       printk(KERN_INFO "EBBChar: device class registered correctly\n");
     
       // 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\n");
          return PTR_ERR(ebbcharDevice);
       }
       printk(KERN_INFO "EBBChar: device class created correctly\n"); // 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!\n"); //卸载驱动, 将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 \r\n", argv[1], argv[2]);
        char *hello = "/dev/hello";
    
        if((fd = open(hello, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
        {
            printf("open %s failed\n", hello);
        }
        else
        {
            printf("%s fd is %d \r\n", hello, fd);
            ioctl(fd, atoi(argv[1]), atoi(argv[2]));
        }
        close(fd);
        return 1;
    }
    

    整理 | 力卉编程

    相关文章

      网友评论

          本文标题:力卉编程 | 简单驱动自动创建设备节点

          本文链接:https://www.haomeiwen.com/subject/smjjphtx.html