一、register_chrdev
在之前的hello驱动中,注册驱动设备的方式如下
/*初始化设备方法1:自动分配设备号,占用所有次设备号*/
major = register_chrdev(0,"hello_drv",&hello_fops);
使用 register_chrdev 分配设备号的方式比较简单直接,但是会导致设备占用所有的次设备号
举个例子:
比如我的hello驱动主设备号是240,次设备号是0,
如果我使用 mknod 创建一个 /dev/abc 设备,主设备号也是240,次设备号 设置为1,
当我操作 /dev/abc 设备时,同样会调用我的hello驱动程序,
因为 register_chrdev 函数使得分配的主设备号下的所有次设备号都对应到hello驱动上了
效果如下图所示
linux内核提供的主设备号是有限的,如果设备很多的情况下主设备号就可能不够用了
那怎么办呢?
解决办法:可以在注册驱动设备的时候,给设备分配好固定的次设备号
二、alloc_chrdev_region、cdev_init、cdev_add
1. alloc_chrdev_region:注册一系列字符设备编号
/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers. The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev. Returns zero or a negative error code.*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
参数 | 含义 |
---|---|
dev | 分配的设备号(高12位是主设备号) |
baseminor | 请求的起始次设备号 |
count | 请求的次设备号的数量 |
name | 自定义驱动程序名称 |
2. cdev_init:初始化cdev结构体
/*** cdev_init() - initialize a cdev structure* @cdev: the structure to initialize* @fops: the file_operations for this device** Initializes @cdev, remembering @fops, making it ready to add to the* system with cdev_add().*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数 | 含义 |
---|---|
cdev | cdev结构体 |
file_operations | 关联应用程序和驱动程序的结构体(包含驱动读写函数指针等) |
cdev结构体(file_operations 成员、dev_t成员等)
struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;
};
3. cdev_add:将字符设备添加到系统中
/*** cdev_add() - add a char device to the system* @p: the cdev structure for the device* @dev: the first device number for which this device is responsible* @count: the number of consecutive minor numbers corresponding to this* device** cdev_add() adds the device represented by @p to the system, making it* live immediately. A negative error code is returned on failure.*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数 | 含义 |
---|---|
p | cdev结构体 |
dev | 分配的设备号(高12位是主设备号) |
count | 请求的次设备号数量 |
三、注册驱动设备,并分配驱动设备次设备号
/*初始化设备方法2:自动分配设备号,设置次设备号占用区域*/
//1.自动分配设备号(起始次设备号0,次设备数量 2,设备名称“hello_drv”)
alloc_chrdev_region(&hello_dev, 0, 2,"hello_drv");
//2.initialize a cdev structure
cdev_init(&hello_cdev, &hello_fops);
//3.add a char device to the system
cdev_add(&hello_cdev, hello_dev, 2);
//4.register a range of device numbers
register_chrdev_region(hello_dev, 2, "hello_drv");
四、hello驱动程序
在博客【IMX6ULL驱动开发学习】06.APP与驱动程序传输数据_自动创建设备节点(hello驱动) 的基础上修改,
注册驱动设备时,采用cdev函数注册,分配两个次设备号0、1
hello驱动程序代码:
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/therm.h>
#include <linux/string.h>static unsigned char buff[100];
static struct class *hello_class;//static int major; //主设备号
dev_t hello_dev; //保存分配的设备号
unsigned baseminor; //分配设备号的起始次设备号
static struct cdev hello_cdev; //定义struct cdev 类型全局变量
unsigned char minor_count = 2; //指定次设备号个数static int hello_open (struct inode *node, struct file *filp)
{printk("hello_open\n");printk("%s %s %d\n",__FILE__, __FUNCTION__, __LINE__);return 0;
}static ssize_t hello_read (struct file *filp, char *buf, size_t size, loff_t *offset)
{size_t len = size > 100 ? 100 : size;printk("hello_read\n");copy_to_user(buf, buff, len);return len;
}static ssize_t hello_write (struct file *filp, const char *buf, size_t size, loff_t *offset)
{size_t len = size > 100 ? 100 : size;memset(buff, 0 ,sizeof(buff));printk("hello_write\n");copy_from_user(buff, buf, len);return len;
}static int hello_release (struct inode *node, struct file *filp)
{printk("hello_release\n");return 0;
}/*1.定义 file_operations 结构体*/
static const struct file_operations hello_fops = {.owner = THIS_MODULE,.read = hello_read,.write = hello_write,.open = hello_open,.release = hello_release,
};/*2.register_chrdev*//*3.入口函数*/
static int hello_init(void)
{struct device *dev;/*初始化设备方法1:自动分配设备号,占用所有次设备号*///设备号
// major = register_chrdev(0,"hello_drv",&hello_fops);/*初始化设备方法2:自动分配设备号,设置次设备号占用区域*///1.自动分配设备号(起始次设备号baseminor,次设备数量 2)if(alloc_chrdev_region(&hello_dev, baseminor, minor_count,"hello_drv") < 0){printk(KERN_ERR"Unable to alloc_chrdev_region.\n");return -EINVAL;}//2.initialize a cdev structurecdev_init(&hello_cdev, &hello_fops);//3.add a char device to the systemif(cdev_add(&hello_cdev, hello_dev, minor_count) < 0){printk(KERN_ERR "Unable to cdev_add.\n");return -EINVAL;}//4.register a range of device numbersregister_chrdev_region(hello_dev, minor_count, "hello_drv");/*自动创建设备节点*//*在内核中创建设备*/hello_class = class_create(THIS_MODULE, "hello_class");if (IS_ERR(hello_class)) {printk("hello class create failed!\n");}/*在/dev下面创建设备节点*/device_create(hello_class, NULL, hello_dev, NULL, "hello");if (IS_ERR(dev)) {printk("hello device_create failed!\n");}return 0;
}/*4.退出函数*/
static int hello_exit(void)
{//销毁设备device_destroy(hello_class, hello_dev);//删除设备类class_destroy(hello_class);/*对应初始化设备方法1:自动分配设备号,占用所有次设备号*/
// unregister_chrdev(major,"hello_fops");/*对应初始化设备方法2:自动分配设备号,设置次设备号占用区域*/unregister_chrdev_region(hello_dev, minor_count);cdev_del(&hello_cdev);return 0;
} module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
hello应用程序代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char *argv[])
{int len;char read_buf[10];if(argc < 2){printf("please input at least 2 args\n");printf("%s <dev> [string]\n", argv[0]);return -1;}/*open*/int fd;fd = open(argv[1], O_RDWR);if(fd < 0){printf("open failed\n");return -2;}/*read*/if(argc == 2){ read(fd, read_buf, 10); //调用read函数,只为了触发系统调用hello驱动的read函数printf("read operation : %s\n", read_buf);}/*write*/if(argc == 3){len = write(fd, argv[2], strlen(argv[2])); //调用write函数,只为了触发系统调用hello驱动的write函数printf("write length = %d \n", len);}close(fd);return 0;
}