字符驱动介绍

观看视频讲解

  • Linux 驱动大致可以分为三种:字符设备,块设备,网络设备. 字符设备由devfs 发展而来,当前由sysfs + mdev 取代。 mdev 是应用层进程,自动扫描/sys/class 下内容生成/dev/ 节点.

  • 字符设备特点
    应用程序按字节读写数据,它通常不支持随机存取数据,大多不使用缓存,直接从设备读取/写入每一个字节。

  • linux内核使用cdev结构体来描述字符设备,dev_t来定义设备号(可以理解为整数),并且是唯一的,其中12位是主设备号,20位是次设备号。

  • file_operations定义字符设备驱动提供给VFS的接口函数,应用层通过系统函数访问,如open()、read()、write()等

  • 主要涉及接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    register_chrdev_region();   //静态申请
    alloc_chrdev_region(); //动态申请
    class_create(); //生成/sys/class/ 目录
    device_create(); //生成/sys/class/ 文件
    class_destroy();
    unregister_chrdev_region();
    copy_to_user(); // 将数据传输到应用层
    copy_from_user(); // 将应用层数据拷到内核
    static const struct file_operations _fops = {
    .owner = THIS_MODULE,
    .llseek = no_llseek,
    .read = _read,
    .write = _write,
    .open = _open,
    .release = _release,
    };

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>

//copy from/to user
#include <linux/uaccess.h>

static struct class *dummy_class;
static struct cdev dummy_cdev;
static dev_t dummy_devt;
static char dummy_buf[100] = {0};
static ssize_t dummy_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
size_t num = strlen(dummy_buf);
if(num == 0) return 0;

if(count > sizeof(dummy_buf)){
num = sizeof(dummy_buf);
}

if(copy_to_user(buf,dummy_buf,num)){
return -EFAULT;
}
memset(dummy_buf,0,sizeof(dummy_buf));
return num;
}
static ssize_t dummy_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
if(count > sizeof(dummy_buf)){
count = sizeof(dummy_buf);
}
if(copy_from_user(dummy_buf,buf,count)){
return -EFAULT;
}

return count;
}

static int dummy_open(struct inode *inode, struct file *file)
{
return 0;
}
static int dummy_release(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations dummy_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = dummy_read,
.write = dummy_write,
.open = dummy_open,
.release = dummy_release,
};
static int __init dummy_init(void)
{
int res;
dev_t devno;
struct device *dev = NULL;

dummy_class = class_create(THIS_MODULE, "dummy");
if (IS_ERR(dummy_class)) {
res = PTR_ERR(dummy_class);
return res;
}

// 动态申请设备号
res = alloc_chrdev_region(&dummy_devt, 0,1, "dummy");
if(res) {
pr_err("alloc chrdev failed res=%d\r\n",res);
return res;
}

devno = MKDEV(MAJOR(dummy_devt), 0);
dev = device_create(dummy_class,NULL,devno,NULL,"dummy-%d",0);

cdev_init(&dummy_cdev, &dummy_fops);
res = cdev_add(&dummy_cdev, dummy_devt, 2);
if (res) {
return res;
}
return 0;
}

static void __exit dummy_exit(void)
{
class_destroy(dummy_class);
unregister_chrdev_region(MKDEV(MAJOR(dummy_devt), 0), 1);
}
module_init(dummy_init);
module_exit(dummy_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("trainne");