【设备节点创建】

本文章详细讲解如何在dev/下面创建设备节点,有通过mknod 静态创建方式和使用Linux为我们提供的接口动态创建两种方式。

1、静态创建设备文件节点

我们可以在启动脚本里面提前创建好一个设备节点。

/* 设备节点名称为create_node , 设备的主设备号为 1300, 从设备号为 0, 设备为字符设备 */
mknode  /dev/create_node  c  1300  0

然后我们编写设备的驱动文件。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h> 
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of_net.h>
#include <linux/netdevice.h>
#include <linux/uio_driver.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/kern_levels.h>
#include <linux/syscalls.h>
#include <linux/platform_device.h>

#define DEV_MAJOR  1300  
#define DEV_MINOR    0  
#define DEV_NAME  "char_dev_create"

static struct cdev *p_cdev = NULL;
static char buf[4028]      = {0};
static char *p_mem         = buf;

static int cdev_open(struct inode *p_node, struct file *p_file)  
{
    return simple_open(p_node, p_file);
}

static ssize_t cdev_read(struct file *p_file, char __user *p_buf, size_t size, loff_t * p_loff) 
{
    if(copy_to_user(p_buf, p_mem, size)) {  
         pr_err("copy_to_user error\n");
	 return 0;
    }
    return size;
}

static ssize_t cdev_write(struct file *p_file, const char __user *p_buf, size_t size, loff_t *p_loff) 
{
    if(copy_from_user(p_mem, p_buf, size)) {
        pr_err("copy_from_user error\n");
	return 0;
    }
    return size; 
}

static const struct file_operations cdev_fops = {
    .owner   = THIS_MODULE,
    .open    = cdev_open,   
    .read    = cdev_read, 
    .write   = cdev_write,  
    .release = cdev_release, 
};

static int __init char_device_init(void)
{
    int ret;
    dev_t devno;

    devno = MKDEV(DEV_MAJOR, DEV_MINOR);
    ret = register_chrdev_region(devno, 1, DEV_NAME );
    if (ret < 0) {
        pr_err("register_chrdev_region failed!\n");
         return ret;
    }
    
    p_cdev  = cdev_alloc();
    cdev_init(p_cdev , &cdev_fops);
    p_cdev->owner = THIS_MODULE;
    ret = cdev_add(p_cdev, devno, 1);  /* 向系统添加cdev结构信息 */
    if (ret < 0) {
        pr_err("cdev_add failed!\n");
        return ret;
    }
    return ret;
}
static void __exit char_device_exit(void)
{
    dev_t devno = MKDEV(DEV_MAJOR, DEV_MINOR);
    cdev_del(p_cdev);
    kfree(p_cdev);
    unregister_chrdev_region(devno, 1);
    
    return;
}

module_init(char_device_init);
module_exit(char_device_exit);
MODULE_LICENSE("GPL v2");

结果:
ls /dev/char_dev //table补全
char_dev_create

2、动态创建设备文件节点

动态创建设备文件就不必在启动脚本里面写mknode /dev/create_node c 1300 0这一句代码了。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/sys/dev/char目录下创建相应的设备节点(显示形式是主设备号:从设备号)。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sys/class下寻找对应的类从而创建设备节点。

 #include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h> 
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of_net.h>
#include <linux/netdevice.h>
#include <linux/uio_driver.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/kern_levels.h>
#include <linux/syscalls.h>
#include <linux/platform_device.h>

#define DEV_NAME  "char_dev_create"
#define SYSCALL_VL(vl_syscall)\
({    \
    long err = 0;   \
    mm_segment_t old_fs;    \
    \
    old_fs = get_fs();  \
    set_fs(KERNEL_DS);  \
    err = vl_syscall; \
    set_fs(old_fs); \
    err;\
})

static struct device *p_dev    = NULL;
static struct class *p_class   = NULL;
static struct cdev *p_cdev     = NULL;
static char buf[4028]          = {0};
static char *p_mem             = buf;

static int cdev_open(struct inode *p_node, struct file *p_file)  
{
    return simple_open(p_node, p_file);
}

static ssize_t cdev_read(struct file *p_file, char __user *p_buf, size_t size, loff_t * p_loff) 
{
    if(copy_to_user(p_buf, p_mem, size)) {  
         pr_err("copy_to_user error\n");
	 return 0;
    }
    return size;
}

static ssize_t cdev_write(struct file *p_file, const char __user *p_buf, size_t size, loff_t *p_loff) 
{
    if(copy_from_user(p_mem, p_buf, size)) {
        pr_err("copy_from_user error\n");
	return 0;
    }
    return size; 
}

static const struct file_operations cdev_fops = {
    .owner   = THIS_MODULE,
    .open    = cdev_open,   
    .read    = cdev_read, 
    .write   = cdev_write,  
    .release = cdev_release, 
};



static int __init char_device_init(void)
{
    int ret, major, dev_minor = 0;
    dev_t devno;
    
    ret = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME);
    if (ret < 0) {
        pr_err("alloc_chrdev_region failed\n");
	    return -1;
    }
    
    major = MAJOR(devno);
    p_cdev = cdev_alloc();
    if (p_cdev == NULL) {
        pr_err("p_cdev alloc failed!\n");
	    return -1;
    }
    cdev_init(p_cdev, &cdev_fops);
    p_cdev->owner = THIS_MODULE;

    err = cdev_add(p_cdev, devno, 1);
    if (err < 0) {
        pr_err("cdev_add failed\n");
	    return -1;
    }

    p_class = class_create(THIS_MODULE, "cdev_class");
    if (p_class == NULL) {
        pr_err("class_create failed!\n");
	    return -1;
    }//在sys/class下面创建cdev_class文件夹
    
    p_dev = device_create(p_class, NULL, MKDEV(major, dev_minor), NULL, DEV_NAME);
    if (p_dev == NULL) {
        pr_err("device_create failed!\n");
	    return -1;
    }//在/sys/dev/char创建char_dev_create设备节点
    
    SYSCALL_VL ( sys_mknod ( "/dev/char_dev_create", S_IFCHR | S_IRUSR | S_IWUSR, new_encode_dev(MKDEV (major, dev_minor))));//在/dev下面创建char_dev_create文件节点,该节点设备是major+dev_minor的设备节点,  与/sys/dev/char/major:dev_minor等价

    return 0;
}

static void __exit char_device_exit(void)
{
    dev_t devno = MKDEV(DEV_MAJOR, DEV_MINOR);
    
    device_destroy(p_class ,devno);  
    class_destroy(p_class ); 
    cdev_del(p_cdev);
    kfree(p_cdev);
    unregister_chrdev_region(devno, 1);
    
    return;
}

module_init(char_device_init);
module_exit(char_device_exit);
MODULE_LICENSE("GPL v2");

结果:
ls /dev/char_dev //table补全
char_dev_create

也可以指定设备的主设备号和从设备号:
rv = register_chrdev(MAJOR_NUM, DRV_NAME, &fops);
class = class_create(THIS_MODULE, DRV_NAME);
dev_devt = MKDEV(MAJOR_NUM, 0);
device_create(class, NULL, dev_devt, “%s”, DRV_NAME);

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值