新字符设备驱动原理
分配和释放设备号
int major; // 主设备号
int minor; // 次设备号
dev_t devid; // 设备号
if(major) // 定义主设备号
{
devid = MKDEV(major, 0); // 构建次设备号
// 起始设备号:devid
// 申请数量:1
// 设备名:text
register_chrdev_region(devid, 1, "test"); // 注册设备号
}
else // 未定义主设备号
{
alloc_chrdev_region(&devid, 0, 1, "text"); // 申请设备号
major = MAJOR(devid); // 获取主设备号
minor = MINOR(devid); // 获取次设备号
}
unregister_chrdev_region(devid, 1); // 注销设备号
新的字符设备注册方法
字符设备结构
// linux-5.5.4\linux-5.5.4\include\linux\cdev.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H
//...
struct cdev // 字符设备结构体
{
struct kobject kobj;
struct module *owner;
const struct file_operations *ops; // 字符设备文件操作函数集合
struct list_head list;
dev_t dev; // 设备号
unsigned int count;
}__randomize_layout;
//...
#endif
cdev_init 函数
struct cdev testcdev;
static struct file_operations test_fogs = // 设备操作函数
{
.owner = THIS_MODULE;
//...
};
testcdev.owner = THIS_MODULE;
// 初始化cdev变量:testcdev
// 字符设备文件操作函数集合:test_fops
cdev_init(&testcdev,&test_fops); //初始化cdev结构体变量
自动创建设备节点
mdev 机制
创建和删除类
// linux-5.5.7/include/linux/device.h
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
// linux-5.5.7/drivers/base/class.c
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
* @name: pointer to a string for the name of this class.
* @key: the lock_class_key for this class; used by mutex lock debugging
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
*
* Returns &struct class pointer on success, or ERR_PTR() on error.
*
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
struct class *__class_create(struct module *owner, const char *name,
struct lock_class_key *key)
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(__class_create);
创建设备
// linux-5.5.7/drivers/base/core.c
/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
* @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
*
* This function can be used by char device classes. A struct device
* will be created in sysfs, registered to the specified class.
*
* A "dev" file will be created, showing the dev_t for the device, if
* the dev_t is not 0,0.
* If a pointer to a parent struct device is passed in, the newly created
* struct device will be a child of that device in sysfs.
* The pointer to the struct device will be returned from the call.
* Any further sysfs files that might be required can be created using this
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
*
* Note: the struct class passed to this function must have previously
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
EXPORT_SYMBOL_GPL(device_create);
设置文件私有数据
// 设备结构体
struct test_dev
{
dev_t devid; //设备号
struct cdev cdev; //cdev
struct class *class; //类
struct device *device; // 设备
int major; //主设备号
int minor; //次设备号
};
struct test_dev testdev;
// open函数
static int test_open(struct inode *inode, struct file *filp)
{
// 设置私有数据
filp -> private_data = &testdev;
return 0;
}