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
|
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
#define DEV_NAME "sad_dev"
static int sad_open(struct inode *, struct file *);
static int sad_release(struct inode *, struct file *);
static ssize_t sad_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t sad_write(struct file *, const char __user *, size_t, loff_t *);
static dev_t dev;
static struct cdev *cdev;
static int device_open_count = 0;
struct sad_buf {
char *buf;
size_t size;
};
static struct file_operations sad_fops = {
.owner = THIS_MODULE,
.read = sad_read,
.write = sad_write,
.open = sad_open,
.release = sad_release
};
static ssize_t sad_read(struct file *flip, char __user *user_buf, size_t len, loff_t *offset) {
struct sad_buf *buf = flip->private_data;
size_t bytes_left, read_size, uncopied;
printk(KERN_INFO "Getting offset %llu\t buf size: %zu\n", *offset, buf->size);
if (*offset >= buf->size)
return 0;
bytes_left = buf->size - *offset;
read_size = min(bytes_left, len);
uncopied = copy_to_user(user_buf, buf->buf + *offset, read_size);
*offset += read_size - uncopied;
printk(KERN_INFO "off: %llu\t uncopied: %zu\t ret: %zu\n", *offset, uncopied, read_size - uncopied);
return read_size - uncopied;
}
static ssize_t sad_write(struct file *flip, const char __user *user_buf, size_t len, loff_t *offset) {
struct sad_buf *buf = flip->private_data;
size_t uncopied;
if (buf->buf)
kfree(buf->buf);
buf->buf = kmalloc(len, GFP_KERNEL);
if (!buf->buf)
return -ENOMEM;
buf->size = len;
*offset = 0;
uncopied = copy_from_user(buf->buf, user_buf, len);
printk(KERN_INFO "uncopied: %zu\t ret: %zu\n", uncopied, len - uncopied);
return len - uncopied;
}
static int sad_open(struct inode *inode, struct file *file) {
struct sad_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
file->private_data = buf;
device_open_count++;
try_module_get(THIS_MODULE);
return 0;
}
static int sad_release(struct inode *inode, struct file *file) {
struct sad_buf *buf = file->private_data;
if (buf->buf)
kfree(buf->buf);
kfree(buf);
device_open_count--;
module_put(THIS_MODULE);
return 0;
}
static int __init sad_init(void) {
int rc;
const unsigned int minor = 0;
const unsigned int count = 1;
rc = alloc_chrdev_region(&dev, minor, count, DEV_NAME);
if (rc < 0) {
printk(KERN_ALERT "Could not register device: %d\n", rc);
return rc;
} else {
printk(KERN_INFO "Allocated maj %d\n", MAJOR(dev));
}
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &sad_fops;
rc = cdev_add(cdev, dev, 1);
if (rc) {
printk(KERN_ALERT "Could not cdev_add: %d\n", rc);
return rc;
}
return 0;
}
static void __exit sad_exit(void) {
cdev_del(cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "okay, bye\n");
}
module_init(sad_init);
module_exit(sad_exit);
|