summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsyn <isaqtm@gmail.com>2020-01-30 20:31:23 +0300
committersyn <isaqtm@gmail.com>2020-01-30 20:31:23 +0300
commit6662cea6b2572cd70a60aadfb28312e2bbef6ecc (patch)
tree1a4a527af10c11ba47224efd14cf866158ca774e
downloadsad-6662cea6b2572cd70a60aadfb28312e2bbef6ecc.tar.gz
Faulty first implementation of echo module
-rw-r--r--.gitignore10
-rw-r--r--Makefile18
-rw-r--r--assoc_buf.h72
-rw-r--r--mod_add.c162
4 files changed, 262 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f0e9079
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.o
+*.ko
+*.mod
+*.mod.c
+*.ko.cmd
+*.mod.cmd
+*o.cmd
+*.symvers
+modules.order
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8fd4a5d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+obj-m += mod_add.o
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+ins:
+ sudo insmod mod_add.ko
+
+rm:
+ sudo rmmod mod_add
+ sudo rm -f dev
+
+nod: all ins
+ sudo mknod dev c 238 0
+ sudo chmod 666 dev
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+
diff --git a/assoc_buf.h b/assoc_buf.h
new file mode 100644
index 0000000..e9e7fbc
--- /dev/null
+++ b/assoc_buf.h
@@ -0,0 +1,72 @@
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+struct asbuf {
+ unsigned long ino;
+ char *buf;
+ size_t alloc;
+};
+
+
+ssize_t asbuf_free_alloc(struct asbuf *buf, size_t size) {
+ char *mem;
+
+ mem = kmalloc(size, GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ if (buf->alloc)
+ kfree(buf->buf);
+
+ buf->buf = mem;
+ buf->alloc = size;
+ return 0;
+}
+
+void asbuf_free(struct asbuf *buf) {
+ kfree(buf->buf);
+}
+
+ssize_t asbuf_to_user(struct asbuf *buf,
+ char __user *user_buf,
+ size_t len,
+ loff_t *offset) {
+
+ size_t chars_left, request;
+ ssize_t uncopied, copied;
+
+ if (!buf->alloc)
+ return -EINVAL;
+
+ if (*offset > buf->alloc)
+ return -EINVAL;
+
+ chars_left = buf->alloc - *offset;
+ request = min(len, chars_left);
+
+ printk(KERN_INFO "buf: %p\t alloc: %zu\t request: %zu\n", buf, buf->alloc, request);
+
+ uncopied = copy_to_user(user_buf, buf->buf + *offset, request);
+ copied = request - uncopied;
+
+ *offset += copied;
+ return copied;
+}
+
+ssize_t asbuf_from_user(struct asbuf *buf,
+ const char __user *user_buf,
+ size_t len,
+ loff_t *offset) {
+ ssize_t rc = 0;
+ size_t uncopied, copied;
+ rc = asbuf_free_alloc(buf, len);
+ if (rc != 0)
+ return rc;
+
+ uncopied = copy_from_user(buf->buf, user_buf, len);
+ copied = len - uncopied;
+
+ *offset = 0;
+
+ return copied;
+}
diff --git a/mod_add.c b/mod_add.c
new file mode 100644
index 0000000..cfabc50
--- /dev/null
+++ b/mod_add.c
@@ -0,0 +1,162 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_LICENSE("GPL");
+
+#include "assoc_buf.h"
+
+#define DEV_NAME "mod_add"
+#define OP_INVALID "Operation invalid\n"
+
+static int device_open(struct inode *, struct file *);
+static int device_release(struct inode *, struct file *);
+static ssize_t device_read(struct file *, char *, size_t, loff_t *);
+static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
+
+static int major_num;
+static int device_open_count = 0;
+
+static struct buf_list {
+ struct asbuf *buf;
+ struct buf_list *next;
+} *buffers;
+
+struct asbuf *find_by_inode(unsigned long ino) {
+ struct buf_list *iter = buffers->next;
+ while (iter) {
+ if (iter->buf->ino == ino)
+ return iter->buf;
+ iter = iter->next;
+ }
+ return NULL;
+}
+
+ssize_t add_buf(struct asbuf *buf) {
+ struct buf_list *iter;
+
+ if (!buffers) {
+ buffers = kmalloc(sizeof(struct buf_list), GFP_KERNEL);
+ if (!buffers)
+ return -ENOMEM;
+ buffers->buf = buf;
+ buffers->next = NULL;
+ return 0;
+ }
+
+ iter = buffers;
+ while (iter->next)
+ iter = iter->next;
+
+ iter->next = kmalloc(sizeof(struct buf_list), GFP_KERNEL);
+ if (!iter->next)
+ return -ENOMEM;
+ iter->buf = buf;
+ iter->next = NULL;
+ return 0;
+}
+
+void free_buf_list(void) {
+ struct buf_list *iter = buffers, *next;
+
+ while (iter) {
+ next = iter->next;
+ asbuf_free(iter->buf);
+ kfree(iter->buf);
+ kfree(iter);
+ iter = next;
+ }
+}
+
+ssize_t rm_by_inode(unsigned long ino) {
+ struct buf_list *iter = buffers;
+ struct buf_list *save_prev;
+
+ if (!buffers)
+ return 0;
+
+ while (iter->next) {
+ save_prev = iter;
+ iter = iter->next;
+ if (iter->buf->ino == ino) {
+ save_prev->next = iter->next;
+ asbuf_free(iter->buf);
+ kfree(iter->buf);
+ kfree(iter);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations file_ops = {
+ .read = device_read,
+ .write = device_write,
+ .open = device_open,
+ .release = device_release
+};
+
+
+static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
+ struct asbuf *buf = find_by_inode(flip->f_inode->i_ino);
+ printk(KERN_INFO "req inode %lu\n", flip->f_inode->i_ino);
+ if (!buf)
+ return -EINVAL;
+ return asbuf_to_user(buf, buffer, len, offset);
+}
+
+static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
+ if (len == 0)
+ return 0;
+
+ return asbuf_from_user(find_by_inode(flip->f_inode->i_ino), buffer, len, offset);
+}
+
+static int device_open(struct inode *inode, struct file *file) {
+ struct asbuf *newbuf;
+
+ if (device_open_count)
+ return -EBUSY;
+
+ newbuf = kzalloc(sizeof(*newbuf), GFP_KERNEL);
+ if (!newbuf)
+ return -ENOMEM;
+
+ printk(KERN_INFO "opened for inode %lu", inode->i_ino);
+ newbuf->ino = inode->i_ino;
+ add_buf (newbuf);
+ device_open_count++;
+ try_module_get(THIS_MODULE);
+ return 0;
+}
+
+static int device_release(struct inode *inode, struct file *file) {
+ rm_by_inode(inode->i_ino);
+ device_open_count--;
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static int __init mod_add_init(void) {
+ buffers = kzalloc(sizeof(*buffers), GFP_KERNEL);
+
+ major_num = register_chrdev(0, DEV_NAME, &file_ops);
+ if (major_num < 0) {
+ printk(KERN_ALERT "Could not register device: %d\n", major_num);
+ return major_num;
+ } else {
+ printk(KERN_INFO "mod_add loaded. dev maj = %d\n", major_num);
+ return 0;
+ }
+}
+static void __exit mod_add_exit(void) {
+ unregister_chrdev(major_num, DEV_NAME);
+ free_buf_list();
+ printk(KERN_INFO "okay, bye\n");
+}
+
+module_init(mod_add_init);
+module_exit(mod_add_exit);