#include "head.h"

unsigned int driver_poll_callback(struct file *filep,
                                  struct poll_table_struct *wait) {
    unsigned int mask = 0;
    g_is_r3_ready = false;
    poll_wait(filep, &g_r3_wait_queue, wait);
    g_is_r3_ready = true;
    if (get_msg_list_length() != 0) {
        mask |= POLLIN | POLLRDNORM;
    }

    return mask;
}
int safe_duck_release(struct inode *inode, struct file *file) { return 0; }
ssize_t safe_duck_read(struct file *file, char __user *buf, size_t count,
                       loff_t *pos) {
    if (count < sizeof(struct kernel_msg_t)) {
        return 0;
    }
    struct kernel_msg_t *msg = get_msg();
    if (msg == NULL) {
        return 0;
    }
    int ret = copy_to_user(buf, msg, sizeof(struct kernel_msg_t));
    kfree(msg);
    if (ret != 0) {
        printk(KERN_ERR "Failed to copy msg to user space\n");
    }
    return ret;
}

ssize_t safe_duck_write(struct file *filp, const char __user *buf, size_t count,
                        loff_t *f_pos) {
    if (count > sizeof(struct client_msg_t)) {
        return -EINVAL;
    }
    struct client_msg_t *message =
        kmalloc(sizeof(struct client_msg_t), GFP_KERNEL);
    do {
        if (message == NULL) {
            printk(KERN_ERR "Failed to allocate memory for new msg\n");
            break;
        }
        if (copy_from_user(message, buf, sizeof(struct client_msg_t))) {
            return -EFAULT;
        }
        if (message->check_sum != MSG_CHECK_SUM) {
            printk(KERN_ERR "Invalid checksum\n");
            break;
        }
        dispath_client_msg(message);
    } while (false);
    if (message != NULL) {
        kfree(message);
    }
    return count;
}
int safe_duck_open(struct inode *inode, struct file *filep) { return 0; }
bool build_dev(void) {
    int rc = alloc_chrdev_region(&g_driver_dev_build.devid, 0, DEVICE_CNT,
                                 DEVICE_NAME);
    g_driver_dev_build.major = MAJOR(g_driver_dev_build.devid);
    g_driver_dev_build.minor = MINOR(g_driver_dev_build.devid);
    if (rc < 0) {
        printk("newchrled chr_dev region err\n");
        return false;
    }
    g_driver_dev_build.init_chrdev_region = true;

    printk(KERN_WARNING "major:%d, minor:%d\n", g_driver_dev_build.major,
           g_driver_dev_build.minor);
    cdev_init(&g_driver_dev_build.cdev, &g_fops);
    rc = cdev_add(&g_driver_dev_build.cdev, g_driver_dev_build.devid,
                  DEVICE_CNT);
    g_driver_dev_build.class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(g_driver_dev_build.class)) {
        return false;
    }
    g_driver_dev_build.init_cdev_add = true;
    g_driver_dev_build.device =
        device_create(g_driver_dev_build.class, NULL, g_driver_dev_build.devid,
                      NULL, DEVICE_NAME);
    const bool build_dev_success = IS_ERR(g_driver_dev_build.device) == false;
    if (build_dev_success) {
        g_driver_dev_build.init_device_create = true;
    }
    return build_dev_success;
}
void destory_dev(void) {
    if (g_driver_dev_build.init_cdev_add) {
        cdev_del(&g_driver_dev_build.cdev);
    }
    if (g_driver_dev_build.init_chrdev_region) {
        unregister_chrdev_region(g_driver_dev_build.devid, DEVICE_NAME);
    }
    if (g_driver_dev_build.init_device_create) {
        device_destroy(g_driver_dev_build.class, g_driver_dev_build.devid);
    }
    if (g_driver_dev_build.init_cdev_add) {
        class_destroy(g_driver_dev_build.class);
    }
}
int cleanup(void) {
    if (g_driver_dev_build.init_netfilter) {
        nf_unregister_net_hook(&init_net, &g_network_hook_ops);
    }
    if (g_driver_dev_build.init_hashmap) {
        cleanup_iphashmap();
    }
    destory_dev();
    cleanup_msg();
    // uninstall_hooks();
    return -1;
}

static int __init driver_entry(void) {
    printk(KERN_WARNING "[DebugMessage] safe duck init\n");
    /*
   if (init_kallsyms_lookup_name() == false) {
       printk(KERN_ERR "Failed to init kallsyms_lookup_name\n");
       return -1;
   }

   if (init_hooks() == false) {
       printk(KERN_ERR "Failed to init kallsyms_lookup_name\n");
       return -1;
   }
   */
    // Initialize list of addresses
    if (build_dev() == false) {
        printk(KERN_ERR "Failed to build device\n");
        return -1;
    }
    if (init_ip_hashmap() == false) {
        printk(KERN_ERR "Failed to init ip hashmap\n");
        return cleanup();
    }
    g_driver_dev_build.init_hashmap = true;
    init_msg();
    int rc = nf_register_net_hook(&init_net, &g_network_hook_ops);
    if (rc < 0) {
        printk(KERN_ERR "Failed to register network hook: %d\n", rc);
        return cleanup();
    }
    g_driver_dev_build.init_netfilter = true;
    printk(KERN_WARNING "[DebugMessage] safe duck init success \n");
    return 0;
}

static void __exit driver_exit(void) {
    printk(KERN_INFO "[DebugMessage] safe duck exit\n");
    cleanup();
}

module_init(driver_entry);
module_exit(driver_exit);
