Allow writing to a buffer for our devices.
[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
17 /* Message buffer we send to upstream */
18 static char *hello_message;
19 static char *goodbye_message;
20
21 struct message {
22         char *text;
23         size_t len;
24 };
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         msg = kmalloc(sizeof(struct message), GFP_KERNEL);
31         if (!msg)
32                 return -ENOMEM;
33         if (iminor(ino) == 0) {
34                 msg->text = hello_message;
35                 msg->len = BUFFER_SIZE;
36         } else {
37                 msg->text = goodbye_message;
38                 msg->len = BUFFER_SIZE;
39         }
40         filp->private_data = msg;
41         return 0;
42 }
43
44 static int helloc_release(struct inode *ino, struct file *filp)
45 {
46         kfree(filp->private_data);
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 >= msg->len)
79         /* return end of file */
80                 return 0;
81         /* if len is bigger than the rest of the message, clamp it */
82         if (len > msg->len - *pos)
83                 len = msg->len - *pos;
84         /* copy message to user space and return error if it fails */
85         r = copy_from_user(msg->text + *pos, buf, len);
86         if (r)
87                 return -EFAULT;
88         /* update the file position */
89         *pos += len;
90         return len;
91 }
92
93 /* we only implement read */
94 static struct file_operations helloc_fops = {
95         .owner = THIS_MODULE,
96         .open = helloc_open,
97         .release = helloc_release,
98         .read = helloc_read,
99         .write = helloc_write,
100 };
101
102 /* the device number and the char device struct */
103 static dev_t dev;
104 static struct cdev *cdev;
105
106 static int __init helloc_init(void)
107 {
108         int r = -ENOMEM;
109         /* allocate any major number with only one minor */
110         hello_message = kzalloc(BUFFER_SIZE, GFP_KERNEL);
111         if (!hello_message)
112                 goto out_hello;
113         goodbye_message = kzalloc(BUFFER_SIZE, GFP_KERNEL);
114         if (!goodbye_message)
115                 goto out_goodbye;
116         r = alloc_chrdev_region(&dev, 0, 2, "helloc");
117         if (r)
118                 goto out_region;
119         r = -ENOMEM;
120         /* print the major number allocated so we can create our device node */
121         printk(KERN_INFO "Allocated major number %d\n", MAJOR(dev));
122         /* allocate the character device struct */
123         cdev = cdev_alloc();
124         if (!cdev)
125                 goto out_alloc;
126         /* set the module owner and the file operations of our chardev */
127         cdev->owner = THIS_MODULE;
128         cdev->ops = &helloc_fops;
129         /* register the chardev to the system */
130         r = cdev_add(cdev, dev, 2);
131         if (r)
132                 goto out_add;
133         return 0;
134 out_add:
135         /* release memory allocated to the cdev device */
136         kfree(cdev);
137 out_alloc:
138         /* release the device number allocated */
139         unregister_chrdev_region(dev, 2);
140 out_region:
141         kfree(goodbye_message);
142 out_goodbye:
143         kfree(hello_message);
144 out_hello:
145         return r;
146 }
147
148 static void __exit helloc_exit(void)
149 {
150         /* remove the chardev from the system */
151         cdev_del(cdev);
152         /* release the device number allocated */
153         unregister_chrdev_region(dev, 2);
154         kfree(goodbye_message);
155         kfree(hello_message);
156 }
157
158 module_init(helloc_init);
159 module_exit(helloc_exit);