本文章详细讲解如何在dev/下面创建设备节点,有通过mknod 静态创建方式和使用Linux为我们提供的接口动态创建两种方式。
我们可以在启动脚本里面提前创建好一个设备节点。
/* 设备节点名称为create_node , 设备的主设备号为 1300, 从设备号为 0, 设备为字符设备 */
mknode /dev/create_node c 1300 0
然后我们编写设备的驱动文件。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
动态创建设备文件就不必在启动脚本里面写mknode /dev/create_node c 1300 0这一句代码了。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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/class/cdev_class创建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/class/cdev_class/char_dev_create等价
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);