文章目录
- 前言
- 1、怎么自动创建一个设备节点?
- 2、什么是mdev
- 3、什么是udev?
- 4、怎么自动创建设备节点?
- 5、创建和删除类函数--自动生成类
-
- 6、创建设备函数--自动生成节点
-
前言
在上一节中,使用insmod加载模块后,还需要通过mknod命令来手动创建设备节点,这样太麻烦了。需要加入自动创建设备节点的功能。
1、怎么自动创建一个设备节点?
在嵌入式Linux中使用mdev来实现设备节点文件的自动创建和删除。
2、什么是mdev
mdev是udev的简化版本,是busybox中所带的程序,最适合在嵌入式系统。
3、什么是udev?
udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中的真正存在的设备了。udev一般用在PC上的linux中,相对mdev来说要复杂一些。
4、怎么自动创建设备节点?
自动创建设备节点分为两个步骤:
步骤一:使用class_create函数来创建一个class的类。
步骤二:使用device_create函数在我们创建的类下面创建一个设备。
5、创建和删除类函数–自动生成类
在Linux驱动程序中一般通过两个函数来完成设备节点的创建和删除。首先要创建一个class类结构体。
class类结构体定义在includ/linux/device.h里面。class_create是类创建函数,class_create是个宏,内容:
#define class_create(owner,name)\
({\
static struct lock_class_key __key;\
__class_create(owner,name,&__key);\
})
struct class* __class_create(struct module *owner,const char* name,struct lock_class_key *key);
class_create一共有两个参数,参数owner一般为THIS_MODULE,参数name是类的名字。返回值是个指向结构体class的指针,也就是创建的类。
卸载驱动程序的时候需要删除掉类,类删除函数为class_destroy,函数原型为:
void class_destroy(struct class *cls);
代码
下面的代码模块只是在/sys/class/
目录下生成一个chrdev_class
的类文件。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEVICE_NUMBER 1
#define DEVICE_SNAME "schrdev"
#define DEVICE_ANAME "achrdev"
#define DEVICE_MINOR_NUMBER 0
#define DEVICE_CLASS_NAME "chrdev_class"
static int major_num, minor_num;
struct cdev cdev;
struct class *class;
int chrdev_open(struct inode *inode, struct file *file)
{
printk("chrdev_open\n");
return 0;
}
struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open
};
module_param(major_num, int, S_IRUSR);
module_param(minor_num, int, S_IRUSR);
static int hello_init(void)
{
dev_t dev_num;
int ret;
if(major_num)
{
printk("major_num: %d\n", major_num);
printk("minor_num: %d\n", minor_num);
dev_num = MKDEV(major_num, minor_num);
ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME);
if(ret < 0)
{
printk("register_chrdev_region error\n");
}
else
printk("register_chrdev_region ok\n");
}
else
{
ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, DEVICE_NUMBER, DEVICE_ANAME);
if(ret <0)
{
printk("alloc_chrdev_region error\n");
}
else
printk("alloc_chrdev_region ok\n");
major_num = MAJOR(dev_num);
minor_num = MINOR(dev_num);
printk("major_num: %d\n", major_num);
printk("minor_num: %d\n", minor_num);
}
printk("major_num = %d, minor_num = %d\n",major_num, minor_num);
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &chrdev_ops);
cdev_add(&cdev, dev_num, DEVICE_NUMBER);
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
return 0;
}
static void hello_exit(void)
{
unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
cdev_del(&cdev);
class_destroy(class);
printk("Bye Bye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
上面代码模块就可以在/sys/class/
目录下生成一个chrdev_class
的类文件了。
6、创建设备函数–自动生成节点
当使用上的函数创建完成一个类后,使用device_create函数在这个类下创建一个设备。
device_create的函数原型如下:
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt,...)
device_create是个可变参数函数
- 参数class 就是设备要创建的那个类;
- 参数parent 是父设备,一般为NULL,也就是没有父设备;
- 参数devt 是设备号;
- 参数drvdata 是设备可能会使用的一些数据,一般为NULL;
- 参数fmt 是设备名字,如果设置
fmt=xxx
的话,就是生成/dev/xxx
这个设备文件了。 - 返回值就是创建好的设备。
同样的,在卸载驱动的时候需要删除掉创建的设备,设备删除函数为:device_destroy
函数原型为:
void device_destroy(struct class* class, dev_t devt);
参数class是要删除的设备所处的类,参数devt是要删除的设备号。
代码
下面的代码就可以自动在/sys/class
下面生成一个chrdev_class类了,并且还自动生成了设备节点/dev/chrdev_test
。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEVICE_NUMBER 1
#define DEVICE_SNAME "schrdev"
#define DEVICE_ANAME "achrdev"
#define DEVICE_MINOR_NUMBER 0
#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME "chrdev_test"
static int major_num, minor_num;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
int chrdev_open(struct inode *inode, struct file *file)
{
printk("chrdev_open\n");
return 0;
}
struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open
};
module_param(major_num, int, S_IRUSR);
module_param(minor_num, int, S_IRUSR);
static int hello_init(void)
{
int ret;
if(major_num)
{
printk("major_num: %d\n", major_num);
printk("minor_num: %d\n", minor_num);
dev_num = MKDEV(major_num, minor_num);
ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME);
if(ret < 0)
{
printk("register_chrdev_region error\n");
}
else
printk("register_chrdev_region ok\n");
}
else
{
ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, DEVICE_NUMBER, DEVICE_ANAME);
if(ret <0)
{
printk("alloc_chrdev_region error\n");
}
else
printk("alloc_chrdev_region ok\n");
major_num = MAJOR(dev_num);
minor_num = MINOR(dev_num);
printk("major_num: %d\n", major_num);
printk("minor_num: %d\n", minor_num);
}
printk("major_num = %d, minor_num = %d\n",major_num, minor_num);
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &chrdev_ops);
cdev_add(&cdev, dev_num, DEVICE_NUMBER);
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
return 0;
}
static void hello_exit(void)
{
unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
cdev_del(&cdev);
device_destroy(class, dev_num);
class_destroy(class);
printk("Bye Bye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR ?= /home/liefyuan/Linux/rk356x_linux/kernel
PWD := $(shell pwd)
obj-m += chrdev.o
all:
make -C $(KERN_DIR) M=$(PWD) modules
clean:
rm -rf *.order *o *.symvers *.mod.c *.mod *.ko
编译:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make
测试验证:
[root@RK356X:/opt]
[34481.427842] alloc_chrdev_region ok
[34481.427948] major_num: 236
[34481.427956] mino[root@RK356X:/optr]
[34481.427964] major_num = 236, minor_num = 0
[root@RK356X:/opt]
/dev/chrdev_test
[root@RK356X:/opt]
android_usb drm misc regulator tpm
ata_device extcon mmc_host rfkill tpmrm
ata_link gpio mpp_class rkwifi tty
ata_port graphics mtd rtc ubi
backlight hidraw net scsi_device udc
bdi hwmon nvme scsi_disk usbmon
block i2c-adapter nvme-subsystem scsi_host vc
bluetooth i2c-dev pci_bus sound video4linux
bsg ieee80211 phy spi_host vtconsole
chrdev_class input power_supply spi_master wakeup
devcoredump iommu ppp spi_transport watchdog
devfreq leds pps spidev zram-control
devfreq-event mdio_bus ptp tee
dma mem pwm thermal
[root@RK356X:/opt]
[34512.952629] Bye Bye
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)