前言
这个 主要是 最开始的时候了解驱动的时候, 看到的一系列的 case, 这里 来大致剖析一下 相关的道理
这些模块 是怎么和内核交互的, 内核的这些业务是怎么实现的
这里主要是一个模块来注册了一个字符设备
然后这个字符设备 可读可写, 基于的是分配的一段空间
测试用例
测试模块如下, 模块主要是来自于 某git仓库, 这里未记录信息, 感谢原作者
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/errno.h>
MODULE_LICENSE("Dual BSD/GPL");
static char data[] = "0123456789\r\n";
/* The lock for device data. */
static rwlock_t lock;
static int example_open(struct inode *inode, struct file *filp) {
printk(KERN_DEBUG "EXAMPLE: open\n");
/* Initial lock. */
rwlock_init(&lock);
/* Map the data location to the file data pointer. */
filp->private_data = data;
return 0;
}
static int example_close(struct inode *inode, struct file *filp) {
printk(KERN_DEBUG "EXAMPLE: close\n");
/* Release the mapping of file data address. */
if(filp->private_data) {
filp->private_data = NULL;
}
return 0;
}
static ssize_t example_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) {
size_t count;
uint8_t byte;
uint8_t *data_p;
printk(KERN_DEBUG "EXAMPLE: read (size=%zu)\n", size);
data_p = filp->private_data;
/* Get the lock for reading. */
read_lock(&lock);
/* Read from the device data to user space. */
for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {
byte = data_p[*f_pos];
if(copy_to_user(buf + count, &byte, 1) != 0) {
break;
}
printk(KERN_DEBUG "EXAMPLE: read (buf[%zu]=%02x)\n", count, (unsigned)byte);
}
/* Release the lock for reading. */
read_unlock(&lock);
return count;
}
static ssize_t example_write(struct file *filp, const char __user *buf, size_t size, loff_t *f_pos) {
size_t count;
ssize_t ret;
uint8_t byte;
uint8_t *data_p;
printk(KERN_DEBUG "EXAMPLE: write (size=%zu)\n", size);
data_p = filp->private_data;
/* Get the lock for writing. */
write_lock(&lock);
/* Write from user space to the device. */
for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {
if(copy_from_user(&byte, buf + count, 1) != 0) {
break;
}
data_p[*f_pos] = byte;
printk(KERN_DEBUG "EXAMPLE: write (buf[%zu]=%02x)\n", count, (unsigned)byte);
}
/* Release the lock for writing. */
write_unlock(&lock);
if((count == 0) && ((*f_pos) >= strlen(data))) {
ret = -ENOBUFS;
}
else {
ret = count;
}
return ret;
}
static struct file_operations example_fops = {
.open = example_open,
.release = example_close,
.read = example_read,
.write = example_write,
};
#define EXAMPLE_NAME "example"
static unsigned int example_major;
static unsigned int example_devs = 2;
static struct cdev example_cdev;
static struct class *example_sys_class = NULL;
static int example_init(void) {
dev_t dev;
int alloc_ret, cdev_err;
printk(KERN_DEBUG "EXAMPLE: init\n");
/* Allocate a character device. */
alloc_ret = alloc_chrdev_region(&dev, 0, example_devs, EXAMPLE_NAME);
if(alloc_ret) {
printk(KERN_DEBUG "EXAMPLE: Failed to allocate a character device\n");
return -1;
}
/* Initial the character device ddriver. */
example_major = MAJOR(dev);
cdev_init(&example_cdev, &example_fops);
example_cdev.owner = THIS_MODULE;
/* Add the character device driver into system. */
dev = MKDEV(example_major, 0);
cdev_err = cdev_add(&example_cdev, dev, example_devs);
if(cdev_err) {
printk(KERN_DEBUG "EXAMPLE: Failed to register a character device\n");
/* Release the allocated character device. */
if(alloc_ret == 0) {
unregister_chrdev_region(dev, example_devs);
}
return -1;
}
printk(KERN_DEBUG "EXAMPLE: %s driver(major %d) installed.\n", EXAMPLE_NAME, example_major);
/* Create device class. */
example_sys_class = class_create(THIS_MODULE, EXAMPLE_NAME);
if(IS_ERR(example_sys_class)) {
printk(KERN_DEBUG "EXAMPLE: Failed to create a class of device.\n");
/* Release the added character device. */
if(cdev_err == 0)
cdev_del(&example_cdev);
/* Release the allocated character device. */
if(alloc_ret == 0)
unregister_chrdev_region(dev, example_devs);
return -1;
}
printk(KERN_DEBUG "EXAMPLE: %s class created.\n", EXAMPLE_NAME);
/* Create device node. */
device_create(example_sys_class, NULL, dev, NULL, EXAMPLE_NAME);
printk(KERN_DEBUG "EXAMPLE: %s device node created.\n", EXAMPLE_NAME);
return 0;
}
static void example_exit(void) {
dev_t dev = MKDEV(example_major, 0);
printk(KERN_DEBUG "EXAMPLE: exit\n");
/* Destory device nodes. */
device_destroy(example_sys_class, dev);
/* Delete device class. */
class_destroy(example_sys_class);
/* Delete the character device driver from system. */
cdev_del(&example_cdev);
/* Unregister the allocated character device. */
unregister_chrdev_region(dev, example_devs);
printk(KERN_DEBUG "EXAMPLE: %s driver removed.\n", EXAMPLE_NAME);
}
module_init(example_init);
module_exit(example_exit);
创建 dev, cdev
分为创建 dev, 初始化 cdev, 关联 dev, cdev
创建设备节点
创建 char_device_struct, 将 dev 和 char_device_sturct 关联起来, 通过 major, baseminor 关联
这里是初始化 cdev, 以及 cdev->ops 为传入的 example_fops
将 dev 关联到 cdev 上面
创建 probe, 关联 dev, cdev, module 并且添加到 cdev_map 中
创建当前 module 下面的 example 的 class
创建 device 以及设备文件
创建 device 相关
创建 kobject, 注册到 sysfs, 创建设备文件 等等
创建 device 对象并初始化 dev_initialize 中主要是初始化各类链表
device_add 中处理的相关核心业务
根据 dev.kobj 在 sysfs 中注册 sysfs 文件
dev, subsystem, group, power 相关
创建 /dev 中设备文件的地方, 这里放入 任务队列
真实异步创建 /dev 中设备文件的地方
创建的设备文件对应的 inode 的 f_ops 初始化如下
open 函数为 chrdev_open
device 设备文件的使用
然后 open 的时候, 会获取到 dev 对应的 cdev, 进而获取到 cdev->ops[驱动中注册的 example_fops]
然后 替换掉 file 中的 f_ops, 作为 真实业务读写的 f_ops
这里可以对比一下 上面 cdev_init 初始化的时候, 传入的 example_fops 的地址信息, 发现 差不多是能够对上的, 地址有一些差异 主要是 两次截图的调试不是在同一个 insmod 但是实际上这里获取到的 f_ops 就是 cdev_init 的时候传入的 example_fops
代理了一层之后, 使用代理的 f_ops->open 再处理了一次
其后, read, write, ioctl 就是基于代理的 f_ops 来进行处理了
这里以 read 为例来进行调试
完