summaryrefslogtreecommitdiffstats
path: root/main.c
blob: d791d62a6fa58d96baa6e1f258da6bcb316980b2 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h> /* used for kmalloc */
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /* copy_*_user */

#include <main.h>
#include <debug.h>

/* major and minor numbers for character device under /dev/ */
int hello_major = HELLO_MAJOR;
int hello_minor = 0;

char *magicstr = "Tesla coil is a hi freq transformer";
char *hellomsg = "photon is the energy carrier particle for EM waves within light spectrum";
struct dentry *debugfs_dir = NULL;
struct dentry *debugfs_file = NULL;

struct hello_dev *hello_device;

static int hello_create_debugfs(void)
{
    int ret = 0;

    /* modifies passed in ptrs */
    ret = hello_debugfs_init(&debugfs_dir, &debugfs_file);
    if (ret)
        printk(KERN_WARNING "[hello] debugfs might not have been mounted\n");

    return ret;
}

static ssize_t hello_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
{
    struct hello_dev *dev = filp->private_data;

    if (*f_pos + count > HELLO_KERNEL_BUFF_LEN)
        count = HELLO_KERNEL_BUFF_LEN - *f_pos;

    if (count > 0)
    {
        copy_to_user(buff, dev->hello_buffer + *f_pos, count);
        *f_pos += count; /* yes we have to keep track */
        return count;
    }

    return 0; /* end of file, also in case of out of bound */
}

static ssize_t hello_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
    struct hello_dev *dev = filp->private_data;

#if 0
    /* we will truncate hello_buffer */
    copy_from_user(dev->hello_buffer + *f_pos, buff, count);
    return count;
#endif

    return 0;
}

/* there's a count in filp that gets incremented every time our char device gets opened, but note that
 * this function does not get called multiple times, instead copies are made, think fork(), or just simple ptr?
 */
static int hello_open(struct inode *inode, struct file *filp)
{
    struct hello_dev *dev;
    dev = container_of(inode->i_cdev, struct hello_dev, cdev);
    filp->private_data = dev; /* our only chance to do that is here in open(), place to save our dev struct */

    return 0;
}

static int hello_release(struct inode *inode, struct file *filp)
{
    return 0;
}

struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .read = hello_read,
    .write = hello_write,
    .open = hello_open,
    .release = hello_release
};

/* __init is a hint that the func is only used at initialization time, then module
 * loader drops the function after module is loaded making its memory available for other uses
 */
static int __init hello_init(void)
{
    int ret = 0;
    dev_t devnum = 0;
    char *hellomsg_copy = NULL;

    printk("[hello] module inserted\n"); /* little hello msg below */
    hellomsg_copy = (char *) kmalloc_array(128, sizeof(char), GFP_KERNEL /* kernel ram */);
    strcpy(hellomsg_copy, hellomsg);
    printk(KERN_INFO "%s\n", hellomsg_copy);
    kfree(hellomsg_copy);

    /* device numbers, major will be picked for us */
    if (hello_major) {
        devnum = MKDEV(hello_major, hello_minor);
        ret = register_chrdev_region(devnum, 1, "helloc"); /* last param name will be reflected in /proc/devices */
    } else {
        ret = alloc_chrdev_region(&devnum, 0 /* first minor */, 1, "helloc");
        hello_major = MAJOR(devnum);
        hello_minor = MINOR(devnum);
        printk(KERN_INFO "[hello] major: %d, minor: %d\n", hello_major, hello_minor);
    }
    if (ret < 0) {
        printk(KERN_WARNING "[hello] can't get the major %d number\n", hello_major);
        return ret;
    }

    /* allocate hello device and set it up */
    hello_device = kmalloc(sizeof(struct hello_dev), GFP_KERNEL);
    if (!hello_device) {
        ret = -ENOMEM;
        goto fail;
    }
    memset(hello_device, 0, sizeof(struct hello_dev));
    hello_device->devnum = devnum;
    strcpy(hello_device->hello_buffer, "write to me\n");

    /* setup char_dev structure */
    cdev_init(&hello_device->cdev, &hello_fops);
    hello_device->cdev.owner = THIS_MODULE;
    if ((ret = cdev_add(&hello_device->cdev, devnum, 1)) < 0)
    {
        printk(KERN_WARNING "[hello] failed to add char device\n");
        goto fail;
    }

    hello_create_debugfs();

    return 0;

  fail:
    unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);
    return ret;
}
 
static void __exit hello_exit(void)
{
    unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);
    hello_debugfs_destroy(debugfs_dir, debugfs_file);
    printk(KERN_INFO "[hello] module removed\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");