f43a767b9c477b7ba5b5e2bc21101a9264f56632
[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         return 0;
35 }
36
37 static int helloc_release(struct inode *ino, struct file *filp)
38 {
39         return 0;
40 }
41
42 /* our read function writes our message to the user buffer */
43 static ssize_t helloc_read(struct file *filp, char __user *buf, size_t len,
44                            loff_t *pos)
45 {
46         int r;
47         struct message *msg = filp->private_data;
48         /* do not read pass through the size of the message */
49         if (*pos >= msg->len)
50         /* return end of file */
51                 return 0;
52         /* if len is bigger than the rest of the message, clamp it */
53         if (len > msg->len - *pos)
54                 len = msg->len - *pos;
55         /* copy message to user space and return error if it fails */
56         r = copy_to_user(buf, msg->text + *pos, len);
57         if (r)
58                 return -EFAULT;
59         /* update the file position */
60         *pos += len;
61         return len;
62 }
63
64 static ssize_t helloc_write(struct file *filp, const char __user *buf,
65                             size_t len, loff_t *pos)
66 {
67         int r;
68         struct message *msg = filp->private_data;
69         /* do not read pass through the size of the message */
70         if (*pos >= msg->len)
71         /* return end of file */
72                 return 0;
73         /* if len is bigger than the rest of the message, clamp it */
74         if (len > msg->len - *pos)
75                 len = msg->len - *pos;
76         /* copy message to user space and return error if it fails */
77         r = copy_from_user(msg->text + *pos, buf, len);
78         if (r)
79                 return -EFAULT;
80         /* update the file position */
81         *pos += len;
82         return len;
83 }
84
85 /* we only implement read */
86 static struct file_operations helloc_fops = {
87         .owner = THIS_MODULE,
88         .open = helloc_open,
89         .release = helloc_release,
90         .read = helloc_read,
91         .write = helloc_write,
92 };
93
94 /* the device number and the char device struct */
95 static dev_t dev;
96 static struct cdev *cdev;
97
98 static void helloc_free(void)
99 {
100         int i;
101         for (i = 0; i < DEVICE_NUMBER; i++) {
102                 if (hello_message[i])
103                         kfree(hello_message[i]->text);
104                 kfree(hello_message[i]);
105         }
106 }
107
108 static int __init helloc_alloc(void)
109 {
110         int i;
111         for (i = 0; i < DEVICE_NUMBER; i++) {
112                 hello_message[i] = kzalloc(sizeof(struct message), GFP_KERNEL);
113                 if (!hello_message[i])
114                         goto out;
115                 hello_message[i]->text = kzalloc(BUFFER_SIZE, GFP_KERNEL);
116                 if (!hello_message[i]->text)
117                         goto out;
118         }
119         return 0;
120 out:
121         for (; i >= 0; i--) {
122                 if (hello_message[i])
123                         kfree(hello_message[i]->text);
124                 kfree(hello_message[i]);
125         }
126         return -ENOMEM;
127 }
128
129 static int __init helloc_init(void)
130 {
131         int r;
132         r = helloc_alloc();
133         if (r)
134                 goto out_alloc2;
135         /* allocate any major number with only one minor */
136         r = alloc_chrdev_region(&dev, 0, DEVICE_NUMBER, "helloc");
137         if (r)
138                 goto out_region;
139         r = -ENOMEM;
140         /* print the major number allocated so we can create our device node */
141         printk(KERN_INFO "Allocated major number %d\n", MAJOR(dev));
142         /* allocate the character device struct */
143         cdev = cdev_alloc();
144         if (!cdev)
145                 goto out_alloc;
146         /* set the module owner and the file operations of our chardev */
147         cdev->owner = THIS_MODULE;
148         cdev->ops = &helloc_fops;
149         /* register the chardev to the system */
150         r = cdev_add(cdev, dev, DEVICE_NUMBER);
151         if (r)
152                 goto out_add;
153         return 0;
154 out_add:
155         /* release memory allocated to the cdev device */
156         kfree(cdev);
157 out_alloc:
158         /* release the device number allocated */
159         unregister_chrdev_region(dev, DEVICE_NUMBER);
160 out_region:
161 out_alloc2:
162         return r;
163 }
164
165 static void __exit helloc_exit(void)
166 {
167         /* remove the chardev from the system */
168         cdev_del(cdev);
169         /* release the device number allocated */
170         unregister_chrdev_region(dev, DEVICE_NUMBER);
171         helloc_free();
172 }
173
174 module_init(helloc_init);
175 module_exit(helloc_exit);