--- /dev/null
+EXTRA_CFLAGS :=
+
+obj-m := swap_driver.o
+
+swap_driver-y := swap_driver_module.o device_driver.o driver_to_buffer.o
--- /dev/null
+#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;
+}
--- /dev/null
+#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__ */
--- /dev/null
+/* SWAP device interface for driver_to_buffer */
+
+void swap_device_wake_up_process(void);
--- /dev/null
+#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__ */
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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 */
+};
--- /dev/null
+#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");
--- /dev/null
+/* 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__ */