8c085f3d0341693826116d874c164969749d6eb7
[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 /* Message buffer we send to upstream */
16 static char hello_message[] = "hello, world\n";
17 static char goodbye_message[] = "goodbye, world\n";
18
19 struct message {
20         char *text;
21         size_t len;
22 };
23
24 static int helloc_open(struct inode *ino, struct file *filp)
25 {
26         struct message *msg;
27         printk(KERN_INFO "Opened file with minor %d\n", iminor(ino));
28         msg = kmalloc(sizeof(struct message), GFP_KERNEL);
29         if (!msg)
30                 return -ENOMEM;
31         if (iminor(ino) == 0) {
32                 msg->text = hello_message;
33                 msg->len = sizeof(hello_message);
34         } else {
35                 msg->text = goodbye_message;
36                 msg->len = sizeof(goodbye_message);
37         }
38         filp->private_data = msg;
39         return 0;
40 }
41
42 static int helloc_release(struct inode *ino, struct file *filp)
43 {
44         kfree(filp->private_data);
45         return 0;
46 }
47
48 /* our read function writes our message to the user buffer */
49 static ssize_t helloc_read(struct file *filp, char __user *buf, size_t len,
50                            loff_t *pos)
51 {
52         int r;
53         struct message *msg = filp->private_data;
54         /* do not read pass through the size of the message */
55         if (*pos >= msg->len)
56         /* return end of file */
57                 return 0;
58         /* if len is bigger than the rest of the message, clamp it */
59         if (len > msg->len - *pos)
60                 len = msg->len - *pos;
61         /* copy message to user space and return error if it fails */
62         r = copy_to_user(buf, msg->text + *pos, len);
63         if (r)
64                 return -EFAULT;
65         /* update the file position */
66         *pos += len;
67         return len;
68 }
69
70 /* we only implement read */
71 static struct file_operations helloc_fops = {
72         .owner = THIS_MODULE,
73         .open = helloc_open,
74         .release = helloc_release,
75         .read = helloc_read,
76 };
77
78 /* the device number and the char device struct */
79 static dev_t dev;
80 static struct cdev *cdev;
81
82 static int __init helloc_init(void)
83 {
84         int r;
85         /* allocate any major number with only one minor */
86         r = alloc_chrdev_region(&dev, 0, 2, "helloc");
87         if (r)
88                 goto out_region;
89         r = -ENOMEM;
90         /* print the major number allocated so we can create our device node */
91         printk(KERN_INFO "Allocated major number %d\n", MAJOR(dev));
92         /* allocate the character device struct */
93         cdev = cdev_alloc();
94         if (!cdev)
95                 goto out_alloc;
96         /* set the module owner and the file operations of our chardev */
97         cdev->owner = THIS_MODULE;
98         cdev->ops = &helloc_fops;
99         /* register the chardev to the system */
100         r = cdev_add(cdev, dev, 2);
101         if (r)
102                 goto out_add;
103         return 0;
104 out_add:
105         /* release memory allocated to the cdev device */
106         kfree(cdev);
107 out_alloc:
108         /* release the device number allocated */
109         unregister_chrdev_region(dev, 2);
110 out_region:
111         return r;
112 }
113
114 static void __exit helloc_exit(void)
115 {
116         /* remove the chardev from the system */
117         cdev_del(cdev);
118         /* release the device number allocated */
119         unregister_chrdev_region(dev, 2);
120 }
121
122 module_init(helloc_init);
123 module_exit(helloc_exit);