目录
编辑
一、register_chrdev
二、解决方法
2.1 alloc_chrdev_region函数:注册一系列字符设备编号
2.2 cdev_init函数:初始化cdev结构体
2.3 cdev_add函数:将字符设备添加到系统中
三、驱动程序
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;
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
因为有两个次设备号,所以用上面的设备节点也可以访问驱动程序
cdev_init(&hello_cdev, &hello_drv);
初始化hello_cdev,让hello_cdev与hello_drv结构体挂钩
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 0
if (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");