目录
编辑
一、register_chrdev
二、解决方法
2.1 alloc_chrdev_region函数:注册一系列字符设备编号
2.2 cdev_init函数:初始化cdev结构体
2.3 cdev_add函数:将字符设备添加到系统中
三、驱动程序
一、register_chrdev
major = register_chrdev(0, "100ask_hello", &hello_drv);
【IMX6ULL驱动开发学习】01.编写第一个hello驱动+自动创建设备节点(不涉及硬件操作)_阿龙还在写代码的博客-CSDN博客
在之前的hello驱动程序中,入口函数会用 register_chrdev来注册字符设备驱动程序,好处是方便快捷,缺点是霸占了主设备号下的所有此设备号。当我们手动创建一个设备节点(主设备号相同),因为有多个次设备号,所以用上面的设备节点也可以访问hello驱动程序。
Linux内核提供的主设备号是有限的,如果设备很多的情况下主设备号就可能不够用了,那怎么办呢?
解决办法:可以在注册驱动设备的时候,给设备分配好固定的次设备号。
二、解决方法
先定义两个静态全局变量
static struct cdev hello_cdev;
static dev_t dev;
2.1 alloc_chrdev_region函数:注册一系列字符设备编号
int ret;
// major = register_chrdev(0, "100ask_hello", &hello_drv);ret = alloc_chrdev_region(&dev, 0, 2, "hello"); // dev/hello c 245 0
if (ret < 0) {printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");return -EINVAL;
}
-
register_chrdev 该函数会把主设备号下所有次设备号都霸占了
-
dev为输出变量,这个结构体里会含有设备的主次设备号
-
0为次设备号,2为想获得几个次设备号,hello为名字
-
如果自己创建设备节点 mknod /dev/xyz c 245(具体数字看对应的主设备号) 1
-
因为有两个次设备号,所以用上面的设备节点也可以访问驱动程序
2.2 cdev_init函数:初始化cdev结构体
cdev_init(&hello_cdev, &hello_drv);
-
初始化hello_cdev,让hello_cdev与hello_drv结构体挂钩
2.3 cdev_add函数:将字符设备添加到系统中
ret = cdev_add(&hello_cdev, dev, 2);
if (ret)
{printk(KERN_ERR "cdev_add() failed for hello\n");return -EINVAL;
}
-
添加hello_cdev 2为此设备号个数
三、驱动程序
hello_drv.c
#include "asm-generic/errno-base.h"
#include "asm/cacheflush.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>static struct class *hello_class;
static struct cdev hello_cdev;
static dev_t dev;static unsigned char hello_buf[100];static int hello_open (struct inode *node, struct file *filp)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > 100 ? 100 : size;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);copy_to_user(buf, hello_buf, len);return len;
}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > 100 ? 100 : size;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);copy_from_user(hello_buf, buf, len);return len;
}static int hello_release (struct inode *node, struct file *filp)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 1. create file_operations */
static const struct file_operations hello_drv = {.owner = THIS_MODULE,.read = hello_read,.write = hello_write,.open = hello_open,.release = hello_release,
};/* 2. register_chrdev *//* 3. entry function */
static int hello_init(void)
{int ret;//申请主次设备号的空间 主次设备号放在dev结构体中//dev为输出变量,这个结构体里会含有设备的主次设备号//0为次设备号,2为想获得几个次设备号,hello为名字ret = alloc_chrdev_region(&dev, 0, 2, "hello");// dev/hello c 245 0if (ret < 0) {printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");return -EINVAL;}//初始化hello_cdev,让hello_cdev与hello_drv结构体挂钩cdev_init(&hello_cdev, &hello_drv);//添加hello_cdev 2为此设备号个数ret = cdev_add(&hello_cdev, dev, 2);if (ret){printk(KERN_ERR "cdev_add() failed for hello\n");return -EINVAL;}hello_class = class_create(THIS_MODULE, "hello_class");if (IS_ERR(hello_class)) {printk("failed to allocate class\n");return PTR_ERR(hello_class);}device_create(hello_class, NULL, dev, NULL, "hello"); /* /dev/hello */return 0;
}/* 4. exit function */
static void hello_exit(void)
{device_destroy(hello_class, dev);class_destroy(hello_class);//unregister_chrdev(major, "100ask_hello");cdev_del(&hello_cdev);unregister_chrdev_region(dev, 2);
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");