驱动学习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;
}

 

原文地址:https://www.cnblogs.com/shuqingstudy/p/9151618.html