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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
// mychardev.c - 字符设备驱动完整示例
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define DEVICE_NAME "mychardev"
#define CLASS_NAME "myclass"
#define BUF_SIZE 1024
// 设备私有数据
struct mydev_data {
char *buffer; // 数据缓冲区
size_t size; // 数据大小
struct mutex lock; // 互斥锁
struct cdev cdev; // 字符设备结构
};
static dev_t dev_num; // 设备号
static struct class *dev_class; // 设备类
static struct device *dev_device; // 设备
static struct mydev_data *dev_data; // 设备数据
// 打开设备
static int mydev_open(struct inode *inode, struct file *filp)
{
struct mydev_data *data;
// 从inode获取设备数据
data = container_of(inode->i_cdev, struct mydev_data, cdev);
filp->private_data = data;
pr_info("Device opened\n");
return 0;
}
// 关闭设备
static int mydev_release(struct inode *inode, struct file *filp)
{
pr_info("Device closed\n");
return 0;
}
// 读设备
static ssize_t mydev_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct mydev_data *data = filp->private_data;
ssize_t ret = 0;
if (mutex_lock_interruptible(&data->lock))
return -ERESTARTSYS;
if (*f_pos >= data->size)
goto out;
if (*f_pos + count > data->size)
count = data->size - *f_pos;
// 复制数据到用户空间
if (copy_to_user(buf, data->buffer + *f_pos, count)) {
ret = -EFAULT;
goto out;
}
*f_pos += count;
ret = count;
out:
mutex_unlock(&data->lock);
return ret;
}
// 写设备
static ssize_t mydev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct mydev_data *data = filp->private_data;
ssize_t ret = 0;
if (mutex_lock_interruptible(&data->lock))
return -ERESTARTSYS;
if (*f_pos >= BUF_SIZE) {
ret = -ENOSPC;
goto out;
}
if (*f_pos + count > BUF_SIZE)
count = BUF_SIZE - *f_pos;
// 从用户空间复制数据
if (copy_from_user(data->buffer + *f_pos, buf, count)) {
ret = -EFAULT;
goto out;
}
*f_pos += count;
if (data->size < *f_pos)
data->size = *f_pos;
ret = count;
out:
mutex_unlock(&data->lock);
return ret;
}
// IOCTL操作
#define MYDEV_IOCTL_CLEAR _IO('M', 1)
#define MYDEV_IOCTL_GET_SIZE _IOR('M', 2, int)
static long mydev_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct mydev_data *data = filp->private_data;
int ret = 0;
switch (cmd) {
case MYDEV_IOCTL_CLEAR:
if (mutex_lock_interruptible(&data->lock))
return -ERESTARTSYS;
memset(data->buffer, 0, BUF_SIZE);
data->size = 0;
mutex_unlock(&data->lock);
pr_info("Buffer cleared\n");
break;
case MYDEV_IOCTL_GET_SIZE:
if (copy_to_user((int __user *)arg, &data->size, sizeof(int)))
ret = -EFAULT;
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
// 文件操作结构
static const struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
.read = mydev_read,
.write = mydev_write,
.unlocked_ioctl = mydev_ioctl,
};
// 模块初始化
static int __init mydev_init(void)
{
int ret;
// 1. 分配设备数据
dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
if (!dev_data)
return -ENOMEM;
dev_data->buffer = kzalloc(BUF_SIZE, GFP_KERNEL);
if (!dev_data->buffer) {
ret = -ENOMEM;
goto fail_buffer;
}
mutex_init(&dev_data->lock);
// 2. 动态分配设备号
ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
if (ret < 0)
goto fail_region;
pr_info("Major: %d, Minor: %d\n", MAJOR(dev_num), MINOR(dev_num));
// 3. 初始化字符设备
cdev_init(&dev_data->cdev, &mydev_fops);
dev_data->cdev.owner = THIS_MODULE;
ret = cdev_add(&dev_data->cdev, dev_num, 1);
if (ret < 0)
goto fail_cdev;
// 4. 创建设备类和设备节点
dev_class = class_create(CLASS_NAME);
if (IS_ERR(dev_class)) {
ret = PTR_ERR(dev_class);
goto fail_class;
}
dev_device = device_create(dev_class, NULL, dev_num, NULL, DEVICE_NAME);
if (IS_ERR(dev_device)) {
ret = PTR_ERR(dev_device);
goto fail_device;
}
pr_info("Device initialized successfully\n");
return 0;
fail_device:
class_destroy(dev_class);
fail_class:
cdev_del(&dev_data->cdev);
fail_cdev:
unregister_chrdev_region(dev_num, 1);
fail_region:
kfree(dev_data->buffer);
fail_buffer:
kfree(dev_data);
return ret;
}
// 模块卸载
static void __exit mydev_exit(void)
{
device_destroy(dev_class, dev_num);
class_destroy(dev_class);
cdev_del(&dev_data->cdev);
unregister_chrdev_region(dev_num, 1);
kfree(dev_data->buffer);
kfree(dev_data);
pr_info("Device removed\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rovina");
MODULE_DESCRIPTION("Character device driver example");
|