Control size of buffer.
[cascardo/kernel/samples/02.char/.git] / helloc.c
1 #include <linux/module.h>
2 /* Needed for struct file_operations and others */
3 #include <linux/fs.h>
4 /* Needed for struct cdev */
5 #include <linux/cdev.h>
6 /* Needed for copying to/from user space */
7 #include <asm/uaccess.h>
8 #include <linux/slab.h>
9
10 MODULE_LICENSE("GPL");
11 MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>");
12 MODULE_DESCRIPTION("A hello world char device");
13 MODULE_VERSION("1.0.0");
14
15 #define BUFFER_SIZE 4096
16 #define DEVICE_NUMBER 2
17
18 struct message {
19         char *text;
20         size_t len;
21 };
22
23 /* Message buffer we send to upstream */
24 static struct message *hello_message[DEVICE_NUMBER];
25
26 static int helloc_open(struct inode *ino, struct file *filp)
27 {
28         struct message *msg;
29         printk(KERN_INFO "Opened file with minor %d\n", iminor(ino));
30         if (iminor(ino) >= DEVICE_NUMBER)
31                 return -ENODEV;
32         msg = hello_message[iminor(ino)];
33         filp->private_data = msg;
34         if (filp->f_flags & O_TRUNC) {
35                 memset(msg->text, 0, msg->len);
36                 msg->len = 0;
37                 printk(KERN_INFO "File opened with truncate flags\n");
38         }
39         if (filp->f_flags & O_APPEND) {
40                 filp->f_pos = msg->len;
41         }
42         return 0;
43 }
44
45 static int helloc_release(struct inode *ino, struct file *filp)
46 {
47         return 0;
48 }
49
50 /* our read function writes our message to the user buffer */
51 static ssize_t helloc_read(struct file *filp, char __user *buf, size_t len,
52                            loff_t *pos)
53 {
54         int r;
55         struct message *msg = filp->private_data;
56         /* do not read pass through the size of the message */
57         if (*pos >= msg->len)
58         /* return end of file */
59                 return 0;
60         /* if len is bigger than the rest of the message, clamp it */
61         if (len > msg->len - *pos)
62                 len = msg->len - *pos;
63         /* copy message to user space and return error if it fails */
64         r = copy_to_user(buf, msg->text + *pos, len);
65         if (r)
66                 return -EFAULT;
67         /* update the file position */
68         *pos += len;
69         return len;
70 }
71
72 static ssize_t helloc_write(struct file *filp, const char __user *buf,
73                             size_t len, loff_t *pos)
74 {
75         int r;
76         struct message *msg = filp->private_data;
77         /* do not read pass through the size of the message */
78         if (*pos >= BUFFER_SIZE)
79         /* return end of file */
80                 return 0;
81         /* if len is bigger than the rest of the message, clamp it */
82         if (len > BUFFER_SIZE - *pos)
83                 len = BUFFER_SIZE - *pos;
84
85         /* copy message to user space and return error if it fails */
86         r = copy_from_user(msg->text + *pos, buf, len);
87         if (r) {
88                 if (*pos + len > msg->len)
89                         memset(msg->text + msg->len, 0, *pos + len - msg->len);
90                 return -EFAULT;
91         }
92         /* update the file position */
93         *pos += len;
94         if (msg->len < *pos) {
95                 printk(KERN_DEBUG "Updating size from %d to %lld\n", msg->len, *pos);
96                 msg->len = *pos;
97         }
98
99         return len;
100 }
101
102 /* we only implement read */
103 static struct file_operations helloc_fops = {
104         .owner = THIS_MODULE,
105         .open = helloc_open,
106         .release = helloc_release,
107         .read = helloc_read,
108         .write = helloc_write,
109 };
110
111 /* the device number and the char device struct */
112 static dev_t dev;
113 static struct cdev *cdev;
114
115 static void helloc_free(void)
116 {
117         int i;
118         for (i = 0; i < DEVICE_NUMBER; i++) {
119                 if (hello_message[i])
120                         kfree(hello_message[i]->text);
121                 kfree(hello_message[i]);
122         }
123 }
124
125 static int __init helloc_alloc(void)
126 {
127         int i;
128         for (i = 0; i < DEVICE_NUMBER; i++) {
129                 hello_message[i] = kzalloc(sizeof(struct message), GFP_KERNEL);
130                 if (!hello_message[i])
131                         goto out;
132                 hello_message[i]->text = kzalloc(BUFFER_SIZE, GFP_KERNEL);
133                 if (!hello_message[i]->text)
134                         goto out;
135         }
136         return 0;
137 out:
138         for (; i >= 0; i--) {
139                 if (hello_message[i])
140                         kfree(hello_message[i]->text);
141                 kfree(hello_message[i]);
142         }
143         return -ENOMEM;
144 }
145
146 static int __init helloc_init(void)
147 {
148         int r;
149         r = helloc_alloc();
150         if (r)
151                 goto out_alloc2;
152         /* allocate any major number with only one minor */
153         r = alloc_chrdev_region(&dev, 0, DEVICE_NUMBER, "helloc");
154         if (r)
155                 goto out_region;
156         r = -ENOMEM;
157         /* print the major number allocated so we can create our device node */
158         printk(KERN_INFO "Allocated major number %d\n", MAJOR(dev));
159         /* allocate the character device struct */
160         cdev = cdev_alloc();
161         if (!cdev)
162                 goto out_alloc;
163         /* set the module owner and the file operations of our chardev */
164         cdev->owner = THIS_MODULE;
165         cdev->ops = &helloc_fops;
166         /* register the chardev to the system */
167         r = cdev_add(cdev, dev, DEVICE_NUMBER);
168         if (r)
169                 goto out_add;
170         return 0;
171 out_add:
172         /* release memory allocated to the cdev device */
173         kfree(cdev);
174 out_alloc:
175         /* release the device number allocated */
176         unregister_chrdev_region(dev, DEVICE_NUMBER);
177 out_region:
178 out_alloc2:
179         return r;
180 }
181
182 static void __exit helloc_exit(void)
183 {
184         /* remove the chardev from the system */
185         cdev_del(cdev);
186         /* release the device number allocated */
187         unregister_chrdev_region(dev, DEVICE_NUMBER);
188         helloc_free();
189 }
190
191 module_init(helloc_init);
192 module_exit(helloc_exit);