创建chardev
前言
之前一直没搞明白struct device 和 struct cdev 的关系,下面我们来问问chatgpt
让chatgpt帮我们写个例子
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
#define DEVICE_NAME "my_device"
struct my_device_data {
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
char buffer[256];
};
struct my_device_data my_data;
static int my_open(struct inode *inode, struct file *file)
{
struct my_device_data *data = container_of(inode->i_cdev, struct my_device_data, cdev);
file->private_data = data;
printk(KERN_INFO "my_device opened\n");
return 0;
}
static int my_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "my_device closed\n");
return 0;
}
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct my_device_data *data = file->private_data;
ssize_t ret = simple_read_from_buffer(buf, count, offset, data->buffer, strlen(data->buffer));
printk(KERN_INFO "my_device read: %s\n", data->buffer);
return ret;
}
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
struct my_device_data *data = file->private_data;
ssize_t ret = simple_write_to_buffer(data->buffer, sizeof(data->buffer), offset, buf, count);
printk(KERN_INFO "my_device write: %s\n", data->buffer);
return ret;
}
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
static int __init my_init(void)
{
int ret;
printk(KERN_INFO "my_device driver loaded\n");
/* Get a range of minor numbers (starting with 0) to work with */
ret = alloc_chrdev_region(&my_data.dev_num, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ALERT "Failed to allocate character device region\n");
return ret;
}
/* Create a class for the devices to belong to */
my_data.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(my_data.class)) {
unregister_chrdev_region(my_data.dev_num, 1);
return PTR_ERR(my_data.class);
}
/* Initialize the character device and add it to the kernel */
cdev_init(&my_data.cdev, &my_fops);
my_data.cdev.owner = THIS_MODULE;
ret = cdev_add(&my_data.cdev, my_data.dev_num, 1);
if (ret < 0) {
device_destroy(my_data.class, my_data.dev_num);
class_destroy(my_data.class);
unregister_chrdev_region(my_data.dev_num, 1);
return ret;
}
/* Create the device and register it with sysfs */
my_data.device = device_create(my_data.class, NULL, my_data.dev_num, NULL, DEVICE_NAME);
if (IS_ERR(my_data.device)) {
cdev_del(&my_data.cdev);
class_destroy(my_data.class);
unregister_chrdev_region(my_data.dev_num, 1);
printk(KERN_ERR "Failed to create device node\n");
return PTR_ERR(my_data.device);
}
printk(KERN_INFO "my_device initialized\n");
return 0;
}
static void my_exit(void)
{
device_destroy(my_data.class, my_data.dev_num);
cdev_del(&my_data.cdev);
unregister_chrdev_region(my_data.dev_num, 1);
class_destroy(my_data.class);
printk(KERN_INFO "my_device unloaded\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
这个例子最初明显漏了几行,我问他是否每次打印的字符串数量受限,他也不理我。后来我让它开开给我函数,这才补齐。
补上Makefile
obj-m += my_module.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
问答
我问它:
上面的例子中,如果我不调用cdev_add,会影响内核态驱动的功能吗?
它回答说:
是的,如果你不调用 cdev_add() 函数将字符设备对象添加到内核中,那么该设备就无法正常工作。当应用程序尝试打开这个设备文件时,内核会无法找到对应的设备对象,从而无法分配资源或处理数据传输请求。因此,一旦你创建了一个字符设备对象,就必须调用 cdev_add() 函数将其添加到内核中,以使其能够正常工作。
总结
简单来说,应该是alloc_chrdev_region申请设备号,cdev_init cdev_add 创建cdev,device_create 创建device。