[FEATURE] New driver implement
authorAlexander Aksenov <a.aksenov@samsung.com>
Fri, 28 Jun 2013 12:07:46 +0000 (16:07 +0400)
committerAlexander Aksenov <a.aksenov@samsung.com>
Fri, 28 Jun 2013 12:07:46 +0000 (16:07 +0400)
12 files changed:
driver_new/Kbuild [new file with mode: 0644]
driver_new/device_driver.c [new file with mode: 0644]
driver_new/device_driver.h [new file with mode: 0644]
driver_new/device_driver_to_driver_to_buffer.h [new file with mode: 0644]
driver_new/driver_defs.h [new file with mode: 0644]
driver_new/driver_to_buffer.c [new file with mode: 0644]
driver_new/driver_to_buffer.h [new file with mode: 0644]
driver_new/ioctl_commands.h [new file with mode: 0644]
driver_new/kernel_operations.h [new file with mode: 0644]
driver_new/swap_driver_errors.h [new file with mode: 0644]
driver_new/swap_driver_module.c [new file with mode: 0644]
driver_new/swap_driver_module.h [new file with mode: 0644]

diff --git a/driver_new/Kbuild b/driver_new/Kbuild
new file mode 100644 (file)
index 0000000..26fa88c
--- /dev/null
@@ -0,0 +1,5 @@
+EXTRA_CFLAGS :=
+
+obj-m := swap_driver.o
+
+swap_driver-y := swap_driver_module.o device_driver.o driver_to_buffer.o
diff --git a/driver_new/device_driver.c b/driver_new/device_driver.c
new file mode 100644 (file)
index 0000000..9f87d8c
--- /dev/null
@@ -0,0 +1,456 @@
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/splice.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#include "device_driver.h"
+#include "swap_driver_errors.h"
+#include "driver_to_buffer.h"
+#include "ioctl_commands.h"
+#include "driver_defs.h"
+#include "device_driver_to_driver_to_buffer.h"
+
+#include "../ksyms/ksyms.h"
+
+#define SWAP_DEVICE_NAME "swap_device"
+
+/* swap_device driver routines */
+static int swap_device_open(struct inode *inode, struct file *filp);
+static int swap_device_release(struct inode *inode, struct file *file);
+static ssize_t swap_device_read(struct file *filp, char __user *buf,
+                                size_t count, loff_t *f_pos);
+static ssize_t swap_device_write(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *f_pos);
+static long swap_device_ioctl(struct file *filp, unsigned int cmd,
+                             unsigned long arg);
+static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
+                                       struct pipe_inode_info *pipe, size_t len,
+                                       unsigned int flags);
+
+/* File operations structure */
+const struct file_operations swap_device_fops = {
+    .read = swap_device_read,
+    .write = swap_device_write,
+    .open = swap_device_open,
+    .release = swap_device_release,
+    .unlocked_ioctl = swap_device_ioctl,
+    .splice_read = swap_device_splice_read,
+};
+
+/* Typedefs for splice_* funcs. Prototypes are for linux-3.8.6 */
+typedef ssize_t(*splice_to_pipe_p_t)(struct pipe_inode_info *pipe,
+                                     struct splice_pipe_desc *spd);
+typedef void(*splice_shrink_spd_p_t)(struct splice_pipe_desc *spd);
+typedef int(*splice_grow_spd_p_t)(const struct pipe_inode_info *pipe,
+                                  struct splice_pipe_desc *spd);
+
+static splice_to_pipe_p_t splice_to_pipe_p = NULL;
+static splice_shrink_spd_p_t splice_shrink_spd_p = NULL;
+static splice_grow_spd_p_t splice_grow_spd_p = NULL;
+
+/* Typedef for swap_message_parser handler func */
+typedef int(*swap_message_parser_handler_t)(unsigned int cmd, void __user * arg);
+
+static swap_message_parser_handler_t swap_message_parser_handler = NULL;
+
+/* Device numbers */
+static dev_t swap_device_no = 0;
+
+/* Device cdev struct */
+static struct cdev *swap_device_cdev = NULL;
+
+/* Device class struct */
+static struct class *swap_device_class = NULL;
+
+/* Device device struct */
+static struct device *swap_device_device = NULL;
+
+/* Reading tasks queue */
+static DECLARE_WAIT_QUEUE_HEAD(swap_device_wait);
+
+/* Register device TODO Think of permanent major */
+int swap_device_init(void)
+{
+    int result;
+
+    /* Allocating device major and minor nums for swap_device */
+    result = alloc_chrdev_region(&swap_device_no, 0, 1, SWAP_DEVICE_NAME);
+    if (result < 0) {
+        print_crit("Major number allocation has failed\n");
+        result = -E_SD_ALLOC_CHRDEV_FAIL;
+        goto init_fail;
+    }
+
+    print_debug("Device with major num %d allocated\n", MAJOR(swap_device_no));
+
+    /* Creating device class. Using IS_ERR, because class_create returns ERR_PTR
+     * on error. */
+    swap_device_class = class_create(THIS_MODULE, SWAP_DEVICE_NAME);
+    if (IS_ERR(swap_device_class)) {
+        print_crit("Class creation has failed\n");
+        result = -E_SD_CLASS_CREATE_FAIL;
+        goto init_fail;
+    }
+
+    /* Cdev allocation */
+    swap_device_cdev = cdev_alloc();
+    if (!swap_device_cdev) {
+        print_crit("Cdev structure allocation has failed\n");
+        result = -E_SD_CDEV_ALLOC_FAIL;
+        goto init_fail;
+    }
+
+    /* Cdev intialization and setting file operations */
+    cdev_init(swap_device_cdev, &swap_device_fops);
+
+    /* Adding cdev to system */
+    result = cdev_add(swap_device_cdev, swap_device_no, 1);
+    if (result < 0) {
+        print_crit("Device adding has failed\n");
+        result = -E_SD_CDEV_ADD_FAIL;
+        goto init_fail;
+    }
+
+    /* Create device struct */
+    swap_device_device = device_create(swap_device_class, NULL, swap_device_no,
+                                       "%s", SWAP_DEVICE_NAME);
+    if (IS_ERR(swap_device_device)) {
+        print_crit("Device struct creating has failed\n");
+        result = -E_SD_DEVICE_CREATE_FAIL;
+        goto init_fail;
+    }
+
+    /* Find splice_* funcs addresses */
+    splice_to_pipe_p = (splice_to_pipe_p_t)swap_ksyms("splice_to_pipe");
+    if (!splice_to_pipe_p) {
+        print_err("splice_to_pipe() not found!\n");
+        result = -E_SD_NO_SPLICE_FUNCS;
+        goto init_fail;
+    }
+
+    splice_shrink_spd_p = (splice_shrink_spd_p_t)swap_ksyms("splice_shrink_spd");
+    if (!splice_shrink_spd_p) {
+        print_err("splice_shrink_spd() not found!\n");
+        result = -E_SD_NO_SPLICE_FUNCS;
+        goto init_fail;
+    }
+
+    splice_grow_spd_p = (splice_grow_spd_p_t)swap_ksyms("splice_grow_spd");
+    if (!splice_grow_spd_p) {
+        print_err("splice_grow_spd() not found!\n");
+        result = -E_SD_NO_SPLICE_FUNCS;
+        goto init_fail;
+    }
+
+    return 0;
+
+init_fail:
+    if (swap_device_cdev) {
+        cdev_del(swap_device_cdev);
+    }
+    if (swap_device_class) {
+        class_destroy(swap_device_class);
+    }
+    if (swap_device_no) {
+        unregister_chrdev_region(swap_device_no, 1);
+    }
+    return result;
+}
+
+/* Unregister device TODO Check wether driver is registered */
+void swap_device_exit(void)
+{
+    splice_to_pipe_p = NULL;
+    splice_shrink_spd_p = NULL;
+    splice_grow_spd_p = NULL;
+
+    device_destroy(swap_device_class, swap_device_no);
+    cdev_del(swap_device_cdev);
+    class_destroy(swap_device_class);
+    unregister_chrdev_region(swap_device_no, 1);
+    print_debug("Device unregistered\n");
+}
+
+static int swap_device_open(struct inode *inode, struct file *filp)
+{
+    // TODO MOD_INC_USE_COUNT
+    return 0;
+}
+
+static int swap_device_release(struct inode *inode, struct file *filp)
+{
+    // TODO MOD_DEC_USE_COUNT
+    return 0;
+}
+
+static ssize_t swap_device_read(struct file *filp, char __user *buf,
+                                size_t count, loff_t *f_pos)
+{
+    /* Wait queue item that consists current task. It is used to be added in
+     * swap_device_wait queue if there is no data to be read. */
+    DECLARE_WAITQUEUE(wait, current);
+    int result;
+
+    /* Add process to the swap_device_wait queue and set the current task state
+     * TASK_INTERRUPTIBLE. If there is any data to be read, then the current 
+     * task is removed from the swap_device_wait queue and its state is changed
+     * to this. */
+    add_wait_queue(&swap_device_wait, &wait);
+    __set_current_state(TASK_INTERRUPTIBLE);
+
+    //TODO : Think about spin_locks to prevent reading race condition.
+    do {
+        result = driver_to_buffer_next_buffer_to_read();
+        if (result < 0) {
+            result = 0;
+            goto swap_device_read_out;
+        } else if (result == E_SD_SUCCESS) {
+            break;
+        } else if (result == E_SD_NO_DATA_TO_READ) {
+            /* Yes, E_SD_NO_DATA_TO_READ should be positive, cause it's not
+             * really an error */
+            if (filp->f_flags & O_NONBLOCK) {
+                result = -EAGAIN;
+                goto swap_device_read_out;
+            }
+            if (signal_pending(current)) {
+                result = -ERESTARTSYS;
+                goto swap_device_read_out;
+            }
+            // TODO Check for sleep conditions
+            schedule();
+        }
+    } while (1);
+
+    result = driver_to_buffer_read(buf, count);
+    /* If there is an error - return 0 */
+    if (result < 0)
+        result = 0;
+
+swap_device_read_out:
+    __set_current_state(TASK_RUNNING);
+    remove_wait_queue(&swap_device_wait, &wait);
+
+    return result;
+
+}
+
+static ssize_t swap_device_write(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *f_pos)
+{
+    char *kern_buffer = NULL;
+    ssize_t result = 0;
+
+    kern_buffer = kmalloc(count, GFP_KERNEL);
+    if (!kern_buffer) {
+        print_err("Error allocating memory for buffer\n");
+        goto swap_device_write_out;
+    }
+
+    result = copy_from_user(kern_buffer, buf, count);
+
+    result = count - result;
+
+    /* Return 0 if there was an error while writing */
+    result = driver_to_buffer_write(result, kern_buffer);
+    if (result < 0)
+        result = 0;
+
+    kfree(kern_buffer);
+
+swap_device_write_out:
+    return result;
+}
+
+static long swap_device_ioctl(struct file *filp, unsigned int cmd,
+                             unsigned long arg)
+{
+    int result;
+
+    switch(cmd) {
+        case SWAP_DRIVER_BUFFER_INITIALIZE:
+        {
+            struct buffer_initialize initialize_struct;
+
+            result = copy_from_user(&initialize_struct, (void*)arg,
+                                    sizeof(struct buffer_initialize));
+            if (result) {
+                break;
+            }
+
+            result = driver_to_buffer_initialize(initialize_struct.size,
+                                                 initialize_struct.count);
+            if (result < 0) {
+                print_err("Buffer initialization failed %d\n", result);
+                break;
+            }
+            result = E_SD_SUCCESS;
+
+            break;
+        }
+        case SWAP_DRIVER_BUFFER_UNINITIALIZE:
+        {
+            result = driver_to_buffer_uninitialize();
+            if (result < 0)
+                print_err("Buffer uninitialization failed %d\n"< result);
+
+            break;
+        }
+        case SWAP_DRIVER_NEXT_BUFFER_TO_READ:
+        {
+            /* Use this carefully */
+            result = driver_to_buffer_next_buffer_to_read();
+            if (result == E_SD_NO_DATA_TO_READ) {
+                /* TODO Do what we usually do when there are no subbuffers to
+                 * read (make daemon sleep ?) */
+            }
+            break;
+        }
+        case SWAP_DRIVER_FLUSH_BUFFER:
+        {
+            result = driver_to_buffer_flush();
+            break;
+        }
+        default:
+        {
+            if (swap_message_parser_handler) {
+                result = swap_message_parser_handler(cmd, (void __user *) arg);
+            } else {
+//                print_warn("Unknown command %d\n", cmd);
+                result = -EINVAL;
+            }
+            break;
+        }
+    }
+    return result;
+}
+
+static void swap_device_pipe_buf_release(struct pipe_inode_info *inode,
+                                         struct pipe_buffer *pipe)
+{
+    /* TODO
+     * In generic one it leads to __page_cache_release. Look at it and
+     * understand. */
+}
+
+static void swap_device_page_release(struct splice_pipe_desc *spd,
+                                     unsigned int i)
+{
+}
+
+static const struct pipe_buf_operations swap_device_pipe_buf_ops = {
+    .can_merge = 0,
+    .map = generic_pipe_buf_map,
+    .unmap = generic_pipe_buf_unmap,
+    .confirm = generic_pipe_buf_confirm,
+    .release = swap_device_pipe_buf_release,
+    .steal = generic_pipe_buf_steal,
+    .get = generic_pipe_buf_get
+};
+
+static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
+                                       struct pipe_inode_info *pipe,
+                                       size_t len, unsigned int flags)
+{
+    /* Wait queue item that consists current task. It is used to be added in
+     * swap_device_wait queue if there is no data to be read. */
+    DECLARE_WAITQUEUE(wait, current);
+    int result;
+    struct page **pages;
+    struct partial_page *partial;
+    int subbuffers_count;
+    struct splice_pipe_desc spd;
+
+    /* Add process to the swap_device_wait queue and set the current task state
+     * TASK_INTERRUPTIBLE. If there is any data to be read, then the current 
+     * task is removed from the swap_device_wait queue and its state is changed
+     * to this. */
+    add_wait_queue(&swap_device_wait, &wait);
+    __set_current_state(TASK_INTERRUPTIBLE);
+
+    /* Get next buffer to read */
+    //TODO : Think about spin_locks to prevent reading race condition.
+    do {
+        result = driver_to_buffer_next_buffer_to_read();
+        if (result < 0) {
+            print_err("driver_to_buffer_next_buffer_to_read error %d\n", result);
+            result = 0;
+            goto swap_device_splice_read_out;
+        } else if (result == E_SD_SUCCESS) {
+            break;
+        } else if (result == E_SD_NO_DATA_TO_READ) {
+            if (filp->f_flags & O_NONBLOCK) {
+                result = -EAGAIN;
+                goto swap_device_splice_read_out;
+            }
+            if (signal_pending(current)) {
+                result = -ERESTARTSYS;
+                goto swap_device_splice_read_out;
+            }
+            // TODO Check for sleep conditions
+            schedule();
+        }
+    } while (1);
+
+//    /* Check whether there is buffer to read */
+//    if (!driver_to_buffer_buffer_to_read()){
+//        return 0;
+//    }
+
+    /* Fill pages and partial fields */
+    subbuffers_count = driver_to_buffer_fill_pages_arrays(&pages, &partial);
+    if (subbuffers_count < 0) {
+        print_err("Error splicing subbuffer! Err code: %d\n", subbuffers_count);
+        result = 0;
+        goto swap_device_splice_read_out;
+    }
+
+    /* Structure for splice_to_pipe() */
+    spd.pages = pages;
+    spd.partial = partial;
+    spd.nr_pages = subbuffers_count;
+    spd.nr_pages_max = subbuffers_count;
+    spd.flags = flags;
+    spd.ops = &swap_device_pipe_buf_ops;
+    spd.spd_release = swap_device_page_release;
+
+    /* Check if pages array has enough size */
+    if (splice_grow_spd_p(pipe, &spd)) {
+        result = -ENOMEM;
+        goto swap_device_splice_read_out;
+    }
+
+    result = splice_to_pipe_p(pipe, &spd);
+    // TODO Read len adjustment ??
+
+    splice_shrink_spd_p(&spd);
+
+/*    kfree(pages);
+    kfree(partial);*/
+swap_device_splice_read_out:
+    __set_current_state(TASK_RUNNING);
+    remove_wait_queue(&swap_device_wait, &wait);
+
+    return result;
+}
+
+void swap_device_wake_up_process(void)
+{
+    wake_up_interruptible(&swap_device_wait);
+}
+
+int register_message_handler(void *s_m_p_h)
+{
+    print_debug("Register message handler\n");
+
+    swap_message_parser_handler = s_m_p_h;
+    return E_SD_SUCCESS;
+}
diff --git a/driver_new/device_driver.h b/driver_new/device_driver.h
new file mode 100644 (file)
index 0000000..b9aa9c8
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __SWAP_DRIVER_DEVICE_DRIVER__
+#define __SWAP_DRIVER_DEVICE_DRIVER__
+
+/* Create and register device */
+int swap_device_init(void);
+
+/* Delete device */
+void swap_device_exit(void);
+
+/* Register swap_message_parser handler */
+int register_message_handler(void *s_m_p_h);
+
+#endif /* __SWAP_DRIVER_DEVICE_DRIVER__ */
diff --git a/driver_new/device_driver_to_driver_to_buffer.h b/driver_new/device_driver_to_driver_to_buffer.h
new file mode 100644 (file)
index 0000000..f6252d6
--- /dev/null
@@ -0,0 +1,3 @@
+/* SWAP device interface for driver_to_buffer */
+
+void swap_device_wake_up_process(void);
diff --git a/driver_new/driver_defs.h b/driver_new/driver_defs.h
new file mode 100644 (file)
index 0000000..7c0b4c5
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __SWAP_DRIVER_DEVICE_DEFS_H__
+#define __SWAP_DRIVER_DEVICE_DEFS_H__
+
+#include <linux/kernel.h>
+
+#define print_debug(msg, args...) \
+    printk(KERN_DEBUG "SWAP_DRIVER DEBUG : " msg, ##args)
+#define print_msg(msg, args...)   \
+    printk(KERN_INFO "SWAP_DRIVER : " msg, ##args)
+#define print_warn(msg, args...)  \
+    printk(KERN_WARNING "SWAP_DRIVER WARNING : " msg, ##args)
+#define print_err(msg, args...)   \
+    printk(KERN_ERR "SWAP_DRIVER ERROR : " msg, ##args)
+#define print_crit(msg, args...)  \
+    printk(KERN_CRIT "SWAP_DRIVER CRITICAL : " msg, ##args)
+
+#endif /* __SWAP_DRIVER_DEVICE_DEFS_H__ */
diff --git a/driver_new/driver_to_buffer.c b/driver_new/driver_to_buffer.c
new file mode 100644 (file)
index 0000000..b6889e5
--- /dev/null
@@ -0,0 +1,284 @@
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/splice.h>
+#include <asm/uaccess.h>
+
+#include "driver_defs.h"
+#include "swap_driver_errors.h"
+#include "../buffer/swap_buffer_module.h" //TODO
+#include "../buffer/swap_buffer_errors.h" //TODO
+#include "device_driver_to_driver_to_buffer.h"
+
+/* Current busy buffer */
+static struct swap_subbuffer *busy_buffer = NULL;
+
+/* Buffers count ready to be read */
+static int buffers_to_read = 0;
+
+/* Pages count in one subbuffer */
+static int pages_per_buffer = 0;
+
+
+/* TODO Get subbuffer for reading */
+static size_t driver_to_buffer_get(void)
+{
+    int result;
+
+    /* If there is no readable buffers, return error */
+    result = swap_buffer_get(&busy_buffer);
+    if (result == -E_SB_NO_READABLE_BUFFERS) {
+        busy_buffer = NULL;
+        return -E_SD_NO_DATA_TO_READ;
+    } else if (result < 0) {
+        print_err("swap_buffer_get unhandle error %d\n", result);
+        return -E_SD_BUFFER_ERROR;
+    }
+
+    return busy_buffer->full_buffer_part;
+}
+
+/* TODO Release subbuffer */
+static int driver_to_buffer_release(void)
+{
+    int result;
+
+    if (!busy_buffer)
+        return -E_SD_NO_BUSY_SUBBUFFER;
+
+    result = swap_buffer_release(&busy_buffer);
+    if (result == -E_SB_NO_SUBBUFFER_IN_BUSY) {
+        return -E_SD_WRONG_SUBBUFFER_PTR;
+    } else if (result < 0) {
+        print_err("swap_buffer_release unhandle error %d\n", result);
+        return -E_SD_BUFFER_ERROR;
+    }
+
+    busy_buffer = NULL;
+
+    return E_SD_SUCCESS;
+}
+
+/* Buffers callback function */
+int driver_to_buffer_callback(void)
+{
+//XXX: Think of sync with get next
+    int result;
+
+    /* Increment buffers_to_read counter */
+    buffers_to_read++;
+    swap_device_wake_up_process();
+
+    return E_SD_SUCCESS;
+}
+
+/* Write to buffers */
+ssize_t driver_to_buffer_write(size_t size, void* data)
+{
+    ssize_t result;
+
+    result = swap_buffer_write(size, data);
+    if (result == -E_SB_IS_STOPPED) {
+        print_err("Buffer is not run! Initialize it before writing\n");
+        return -E_SD_WRITE_ERROR;
+    } else if (result < 0) {
+        print_err("swap_buffer_write error %d\n", result);
+        return -E_SD_WRITE_ERROR;
+    }
+
+    return result;
+}
+
+/* Read buffers */
+ssize_t driver_to_buffer_read(char __user *buf, size_t count)
+{
+    size_t bytes_to_copy;
+    size_t bytes_to_read = 0;
+    int page_counter = 0;
+
+    /* Reading from swap_device means reading only current busy_buffer. So, if
+     * there is no busy_buffer, we don't get next to read, we just read nothing.
+     * In this case, or if there is nothing to read from busy_buffer - return
+     * -E_SD_NO_DATA_TO_READ. It should be correctly handled in device_driver */
+    if (!busy_buffer || !busy_buffer->full_buffer_part)
+        return -E_SD_NO_DATA_TO_READ;
+
+    /* Bytes count that we're going to copy to user buffer is equal to user
+     * buffer size or to subbuffer readable size whichever is less */
+    bytes_to_copy = (count > busy_buffer->full_buffer_part) ?
+                    busy_buffer->full_buffer_part : count;
+
+    /* Copy data from each page to buffer */
+    while(bytes_to_copy > 0) {
+        /* Get size that should be copied from current page */
+        size_t read_from_this_page = (bytes_to_copy > PAGE_SIZE) ? PAGE_SIZE
+                                                                 : bytes_to_copy;
+
+        /* Copy and add size to copied bytes count */
+
+        // TODO Check with more than one page
+        bytes_to_read += read_from_this_page -
+                         copy_to_user(buf, page_address(busy_buffer->data_buffer) +
+                                                        (sizeof(struct page*) *
+                                                         page_counter),
+                                                        read_from_this_page);
+        bytes_to_copy -= read_from_this_page;
+        page_counter++;
+    }
+
+    return bytes_to_read;
+}
+
+/* Flush swap_buffer */
+int driver_to_buffer_flush(void)
+{
+    int result;
+
+    result = swap_buffer_flush();
+
+    if (result >= 0)
+        buffers_to_read = result;
+    else if (result < 0)
+        return -E_SD_BUFFER_ERROR;
+
+    swap_device_wake_up_process();
+
+    return E_SD_SUCCESS;
+}
+
+/* Fills page and partial arrays in splice_pipe_desc struct for splice_read */
+int driver_to_buffer_fill_pages_arrays(struct page ***pages,
+                                       struct partial_page **partial)
+{
+    int page_counter = 0;
+    size_t data_to_splice;
+
+    /* Sanitization */
+    if (!busy_buffer || !busy_buffer->full_buffer_part)
+        return -E_SD_NO_BUSY_SUBBUFFER;
+
+    data_to_splice = busy_buffer->full_buffer_part;
+
+    /* Allocate memory for arrays */
+    *pages = kmalloc(sizeof(struct page*) * pages_per_buffer, GFP_KERNEL);
+    *partial = kmalloc(sizeof(struct partial_page) * pages_per_buffer, GFP_KERNEL);
+
+    while (data_to_splice) {
+        /* Get bytes count that are should be read from current page */
+        size_t read_from_current_page = (data_to_splice > PAGE_SIZE) ? PAGE_SIZE
+                                        : data_to_splice;
+
+        /* Fill pages array */
+        (*pages)[page_counter] = (void *)((unsigned long)busy_buffer->data_buffer +
+                                          (sizeof(struct page *) * page_counter));
+
+        /* Offset is always 0, cause we write to buffers from the very beginning
+         * of the first page */
+        (*partial)[page_counter].offset = 0;
+        (*partial)[page_counter].len = read_from_current_page;
+
+        /* TODO Private not used */
+        (*partial)[page_counter].private = 0;
+
+        page_counter++;
+        data_to_splice -= read_from_current_page;
+    }
+    return page_counter;
+}
+
+/* Check for subbuffers ready to be read */
+int driver_to_buffer_buffer_to_read(void)
+{
+    return busy_buffer ? 1 : 0;
+}
+
+/* Set buffers size and count */
+int driver_to_buffer_initialize(size_t size, unsigned int count)
+{
+    int result;
+
+    if (size == 0 && count == 0) {
+        return -E_SD_WRONG_ARGS;
+    }
+
+    result = swap_buffer_init(size, count, (void*)&driver_to_buffer_callback);
+    if (result == -E_SB_NO_MEM_QUEUE_BUSY
+        || result == -E_SB_NO_MEM_BUFFER_STRUCT) {
+        return -E_SD_NO_MEMORY;
+    }
+
+    // TODO Race condition: buffer can be used in other thread till we're in
+    // this func
+    /* Initialize driver_to_buffer variables */
+    pages_per_buffer = result;
+    busy_buffer = NULL;
+    buffers_to_read = 0;
+
+    return E_SD_SUCCESS;
+}
+
+/* Uninitialize buffer */
+int driver_to_buffer_uninitialize(void)
+{
+    int result;
+
+    /* Release occupied buffer */
+    if (busy_buffer) {
+        result = driver_to_buffer_release();
+        // TODO Maybe release anyway
+        if (result < 0) {
+            return result;
+        }
+        busy_buffer = NULL;
+    }
+
+    result = swap_buffer_uninit();
+    if (result == -E_SB_UNRELEASED_BUFFERS) {
+        print_err("Can't uninit buffer! There are busy subbuffers!\n");
+        result = -E_SD_BUFFER_ERROR;
+    } else if (result < 0) {
+        print_err("swap_buffer_uninit error %d\n", result);
+        result = -E_SD_BUFFER_ERROR;
+    } else {
+        result = E_SD_SUCCESS;
+    }
+
+    /* Reinit driver_to_buffer vars */
+    buffers_to_read = 0;
+    pages_per_buffer = 0;
+
+    return result;
+}
+
+/* Get next buffer to read */
+int driver_to_buffer_next_buffer_to_read(void)
+{
+//XXX: Think of sync with callback
+    int result;
+
+    /* If there is busy_buffer first release it */
+    if (busy_buffer) {
+        print_debug(" There are busy subbuffer!\n");
+        result = driver_to_buffer_release();
+        if (result)
+            return result;
+    }
+
+    /* If there is no buffers to read, return E_SD_NO_DATA_TO_READ.
+     * SHOULD BE POSITIVE, cause there is no real error. */
+    if (!buffers_to_read) {
+        return E_SD_NO_DATA_TO_READ;
+    }
+
+    /* Get next buffer to read */
+    result = driver_to_buffer_get();
+    if (result < 0) {
+        print_err("buffer_to_reads > 0, but there are no buffers to read\n");
+        return result;
+    }
+
+    /* Decrement buffers_to_read counter */
+    buffers_to_read--;
+
+    return E_SD_SUCCESS;
+}
+
diff --git a/driver_new/driver_to_buffer.h b/driver_new/driver_to_buffer.h
new file mode 100644 (file)
index 0000000..70decd7
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __SWAP_DRIVER_DRIVER_TO_BUFFER__
+#define __SWAP_DRIVER_DRIVER_TO_BUFFER__
+
+int driver_to_buffer_initialize(size_t size, unsigned int count);
+int driver_to_buffer_uninitialize(void);
+ssize_t driver_to_buffer_write(size_t size, void* data);
+ssize_t driver_to_buffer_read(char __user *buf, size_t count);
+void driver_to_buffer_callback(void);
+int driver_to_buffer_fill_pages_arrays(struct page ***pages,
+                                       struct partial_page **partial);
+int driver_to_buffer_buffer_to_read(void);
+int driver_to_buffer_next_buffer_to_read(void);
+int driver_to_buffer_flush(void);
+
+
+#endif /* __SWAP_DRIVER_DRIVER_TO_BUFFER__ */
diff --git a/driver_new/ioctl_commands.h b/driver_new/ioctl_commands.h
new file mode 100644 (file)
index 0000000..f367734
--- /dev/null
@@ -0,0 +1,32 @@
+/* SWAP Device ioctl commands */
+
+#include <linux/ioctl.h>
+
+#ifndef __SWAP_DRIVER_IOCTL_COMMANDS__
+#define __SWAP_DRIVER_IOCTL_COMMANDS__
+
+#define SWAP_DRIVER_IOC_MAGIC 0xAF
+
+// TODO Think about magic num
+
+struct buffer_initialize {
+    size_t size;
+    unsigned int count;
+};
+
+#define SWAP_DRIVER_BUFFER_INITIALIZE       _IOW(SWAP_DRIVER_IOC_MAGIC, 1, \
+                                                 struct buffer_initialize *)
+#define SWAP_DRIVER_BUFFER_UNINITIALIZE     _IO(SWAP_DRIVER_IOC_MAGIC, 2)
+#define SWAP_DRIVER_NEXT_BUFFER_TO_READ     _IO(SWAP_DRIVER_IOC_MAGIC, 3)
+#define SWAP_DRIVER_FLUSH_BUFFER            _IO(SWAP_DRIVER_IOC_MAGIC, 4)
+#define SWAP_DRIVER_MSG_START               _IOW(SWAP_DRIVER_IOC_MAGIC, 5, \
+                                                 void *)
+#define SWAP_DRIVER_MSG_STOP                _IO(SWAP_DRIVER_IOC_MAGIC, 6)
+#define SWAP_DRIVER_MSG_CONFIG              _IOW(SWAP_DRIVER_IOC_MAGIC, 7,\
+                                                 void *)
+#define SWAP_DRIVER_MSG_SWAP_INST_ADD       _IOW(SWAP_DRIVER_IOC_MAGIC, 8,\
+                                                 void *)
+#define SWAP_DRIVER_MSG_SWAP_INST_REMOVE    _IOW(SWAP_DRIVER_IOC_MAGIC, 9,\
+                                                 void *)
+
+#endif /* __SWAP_DRIVER_IOCTL_COMMANDS__ */
diff --git a/driver_new/kernel_operations.h b/driver_new/kernel_operations.h
new file mode 100644 (file)
index 0000000..df9d343
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *  SWAP Buffer Module
+ *  modules/buffer/kernel_operations.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013         Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement
+ *
+ */
+
+/* Kernel functions wrap */
+
+#ifndef __KERNEL_OPERATIONS_H__
+#define __KERNEL_OPERATIONS_H__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+
+
+/* LOCKS */
+
+/* Using spinlocks as sync primitives */
+typedef spinlock_t sync_t;
+
+/* Spinlock flags */
+static unsigned long flags;
+
+/* Spinlocks initialization */
+static inline void sync_init(sync_t *buffer_sync)
+{
+       spin_lock_init(buffer_sync);
+}
+
+/* Lock spinlock */
+static inline void sync_lock(sync_t *buffer_sync)
+{
+       spin_lock_irqsave(buffer_sync, flags);
+}
+
+/* Unlock spinlock */
+static inline void sync_unlock(sync_t *buffer_sync)
+{
+       spin_unlock_irqrestore(buffer_sync, flags);
+}
+
+
+/* SWAP SUBBUFER */
+
+/* swap_subbuffer_ptr points to the first memory page of the subbuffer */
+typedef struct page *swap_subbuffer_ptr;
+
+/* We alloc memory for swap_subbuffer structures with common kmalloc */
+#define memory_allocation(memory_size)  kmalloc(memory_size, GFP_KERNEL)
+#define memory_free(ptr)                kfree(ptr)
+
+/* For subbuffers themselves, we allocate memory with alloc_pages, so, we have
+ * to evaluate required pages order */
+#define buffer_allocation(memory_size)                        \
+       alloc_pages(GFP_KERNEL, (pages_order_in_subbuffer >= 0) ? \
+               pages_order_in_subbuffer :                            \
+               get_order_for_alloc_pages(memory_size))
+
+#define buffer_free(ptr, subbuf_size)                         \
+       __free_pages(ptr, (pages_order_in_subbuffer >= 0) ?       \
+                pages_order_in_subbuffer :                           \
+                get_order_for_alloc_pages(subbuf_size))
+
+#define buffer_address(buffer_ptr)  page_address(buffer_ptr)
+#define set_pages_order_in_subbuffer(memory_size) \
+       pages_order_in_subbuffer = get_order_for_alloc_pages(memory_size)
+
+/* Functions for pages allocation */
+static inline unsigned int nearest_power_of_two(unsigned int number)
+{
+       unsigned int result = 0;
+       unsigned int two_to_the_power = 1;
+
+       /* If aligned_size == PAGE_SIZE we need only one page, so return 0 */
+       if (number == 1)
+               return result;
+
+       while (two_to_the_power < number) {
+               two_to_the_power <<= 1;
+               result++;
+       }
+
+       return result;
+}
+
+static inline unsigned int get_order_for_alloc_pages(size_t memory_size)
+{
+       /* First evaluate remainder of the division memory_size by PAGE_SIZE.
+        * If memory_size is divisible by PAGE_SIZE, then remainder equals 0. */
+       size_t remainder = (memory_size % PAGE_SIZE) ?
+                      (memory_size % PAGE_SIZE) : PAGE_SIZE;
+
+       /* Align memory_size to the PAGE_SIZE. aligned_size >= memory_size */
+       size_t aligned_size = memory_size + (PAGE_SIZE - remainder);
+
+       return nearest_power_of_two(aligned_size / PAGE_SIZE);
+}
+
+
+/* MESSAGES */
+#define print_debug(msg, args...) \
+       printk(KERN_DEBUG "SWAP_BUFFER DEBUG : " msg, ##args)
+#define print_msg(msg, args...)   \
+       printk(KERN_INFO "SWAP_BUFFER : " msg, ##args)
+#define print_warn(msg, args...)  \
+       printk(KERN_WARNING "SWAP_BUFFER WARNING : " msg, ##args)
+#define print_err(msg, args...)   \
+       printk(KERN_ERR "SWAP_BUFFER ERROR : " msg, ##args)
+#define print_crit(msg, args...)  \
+       printk(KERN_CRIT "SWAP_BUFFER CRITICAL : " msg, ##args)
+
+#endif /* __KERNEL_OPERATIONS_H__ */
diff --git a/driver_new/swap_driver_errors.h b/driver_new/swap_driver_errors.h
new file mode 100644 (file)
index 0000000..69c6935
--- /dev/null
@@ -0,0 +1,22 @@
+/* SWAP Driver error codes enumeration */
+
+enum _swap_driver_errors {
+    E_SD_SUCCESS = 0,               /* Success */
+    E_SD_ALLOC_CHRDEV_FAIL = 1,     /* alloc_chrdev_region failed */
+    E_SD_CDEV_ALLOC_FAIL = 2,       /* cdev_alloc failed */
+    E_SD_CDEV_ADD_FAIL = 3,         /* cdev_add failed */
+    E_SD_CLASS_CREATE_FAIL = 4,     /* class_create failed */
+    E_SD_DEVICE_CREATE_FAIL = 5,    /* device_create failed */
+    E_SD_NO_SPLICE_FUNCS = 6,       /* splice_* funcs not found */
+    E_SD_NO_DATA_TO_READ = 7,       /* swap_buffer_get tells us that there is no
+                                       readable subbuffers */
+    E_SD_NO_BUSY_SUBBUFFER = 8,     /* No busy subbuffer */
+    E_SD_WRONG_SUBBUFFER_PTR = 9,    /* Wrong subbuffer pointer passed to
+                                       swap_buffer module */
+    E_SD_BUFFER_ERROR = 10,         /* Unhandled swap_buffer error */
+    E_SD_WRITE_ERROR = 11,          /* Write to subbuffer error */
+    E_SD_WRONG_ARGS = 12,           /* Arguments, passed to the func, doesn't 
+                                       pass sanity check */
+    E_SD_NO_MEMORY = 13,            /* No memory to allocate */
+    E_SD_UNINIT_ERROR = 14          /* swap_buffer uninitialization error */
+};
diff --git a/driver_new/swap_driver_module.c b/driver_new/swap_driver_module.c
new file mode 100644 (file)
index 0000000..84c79c7
--- /dev/null
@@ -0,0 +1,35 @@
+#include <linux/module.h>
+
+#include "driver_defs.h"
+#include "device_driver.h"
+
+int register_swap_message_parser_handler(void *s_m_p_h)
+{
+    return register_message_handler(s_m_p_h);
+}
+EXPORT_SYMBOL_GPL(register_swap_message_parser_handler);
+
+static int __init swap_driver_init(void)
+{
+    print_msg("Module init\n");
+
+#ifdef TEST_MODE
+    print_msg("Test mode on\n");
+#endif
+
+    swap_device_init();
+
+    return 0;
+}
+
+static void __exit swap_driver_exit(void)
+{
+    //TODO Kill userspace daemon process
+    swap_device_exit();
+    print_msg("Module exit\n");
+}
+
+module_init(swap_driver_init);
+module_exit(swap_driver_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/driver_new/swap_driver_module.h b/driver_new/swap_driver_module.h
new file mode 100644 (file)
index 0000000..a464b10
--- /dev/null
@@ -0,0 +1,9 @@
+/* SWAP Device Driver interface module */
+
+#ifndef __SWAP_DRIVER_MODULE_H__
+#define __SWAP_DRIVER_MODULE_H__
+
+/* Register swap_message_parser messages handler */
+int register_swap_message_parser_handler(void *s_m_p_h);
+
+#endif /* __SWAP_DRIVER_MODULE_H__ */